| Age | Commit message (Collapse) | Author |
|
A first-run dialog for an existing wallet.dat with no backend choice yet:
"Convert to SQLite" exports the wallet and records the sqlite marker so
the next start opens it; "Keep Berkeley DB" records that choice so the
offer does not return; "Decide later" leaves no marker and asks again.
wallet.dat is never touched, and the change takes effect on restart --
matching the restart-to-apply model.
The conversion itself is DoConvertWalletToSQLite(), a headless-testable
function the button calls: it reuses the export path (extracted as
ExportActiveWalletToSQLite, shared with -walletsqliteexport, which keeps
its own specific messages), refuses if already on SQLite or if a
wallet.sqlite exists, and writes the marker only after a verified export.
New wallet-convert self-test proves the action end to end: build a
Berkeley DB wallet, convert, and confirm wallet.sqlite + the marker are
written, wallet.dat is left untouched, a second convert refuses, and a
flagless restart opens the converted SQLite wallet with its phrase intact.
All wallet suites pass. The modal itself is thin UI over this tested
action.
|
|
A fresh install with no wallet.dat now starts on the SQLite backend and
creates wallet.sqlite, instead of a Berkeley DB wallet.dat. The choice is
recorded in a plain-text <datadir>/wallet-backend marker so it survives a
restart without a command-line flag.
Backend resolution when no -walletbackend is given: an existing
wallet.sqlite (or a "sqlite" marker) stays on SQLite; an existing
wallet.dat stays on Berkeley DB -- nothing is converted behind the user;
a fresh datadir starts on SQLite. An explicit -walletbackend=bdb or a
"bdb" marker forces Berkeley DB and overrides the default.
Adds CreateNewSQLiteWallet() (WalletSQLiteRuntimeOpen gains fMustExist so
the create path can make a new file) which mints the first default key
straight into the SQLite store via the same FinishLoadedWallet() call the
Berkeley DB create path uses, then cleans up on failure. Marker read/write
helpers are plain text, hand-editable.
New wallet-backend-default self-test proves the resolution in child
processes (fresh->SQLite+marker, existing wallet.dat->BDB, "bdb"
marker->BDB, explicit flag->wallet.dat). The three wallet.dat-specific
suites now ask for -walletbackend=bdb explicitly, since a flagless fresh
datadir is SQLite now. All wallet suites pass.
|
|
-walletstorageaudit, -walletstoragecheck and -backupwallet all read
wallet.dat regardless of the selected backend. Under -walletbackend=sqlite
that is wrong, and for backup it is dangerous: it silently backs up the
stale plaintext wallet.dat instead of the live wallet.sqlite.
- ScanWalletRecords() routes to the active SQLite store instead of a
Berkeley DB cursor (it returned an error under the runtime before). This
is the single choke point audit and check scan through.
- WalletSQLiteRuntimeOpenReadOnly() opens wallet.sqlite for scanning
without loading or validating records, so the pre-load diagnostics can
inspect a wallet the loader would refuse. main_gui opens it for
audit/check when the SQLite backend is selected (and rejects an unknown
backend value there too).
- BackupWallet() copies wallet.sqlite (checkpoint, then the temp/old/rename
dance; a checkpointed SQLite file is self-contained, no lsn_reset).
wallet-sqlite-encrypt self-test proves routing on a datadir where the
SQLite wallet is encrypted and wallet.dat beside it is still plaintext:
audit with the flag reports the encrypted seed, without it reports the
plaintext one; the SQLite backup carries no plaintext key and loadchecks
as a standalone wallet.
|
|
The SQLite runtime wrote each record as its own autocommit, so a group
that must land together -- hdmaster and hdchaincode, the two halves of an
HD seed -- could be left half-written by a failure or crash between them,
the incomplete-seed state -walletstoragecheck exists to catch. Berkeley DB
was no worse (it is not transactional here either) but no better.
Add WalletSQLiteRuntimeBeginTxn/CommitTxn/RollbackTxn -- no-ops that
succeed when the SQLite backend is not active, so Berkeley DB keeps its
existing per-write behaviour -- and wrap WriteHDMaster in them so the seed
lands all-or-nothing under SQLite. The one BDB-atomic wallet rewrite, the
encrypt path, is already matched by its own transaction in the SQLite
encrypt branch; every other wallet multi-write is ordering-based on both
backends by design.
wallet-sqlite self-test now proves the bracket: a rolled-back runtime
write leaves no record, a committed one persists, and WriteHDMaster lands
both halves of the seed.
|
|
EncryptWallet() had only a Berkeley DB path: it rebuilt a wallet.dat temp
file and swapped it in. Under -walletbackend=sqlite that was wrong -- it
would leave the live wallet.sqlite unencrypted and write a divergent
wallet.dat instead. Add a SQLite persistence branch that rebuilds a fresh
wallet.sqlite in one transaction (public records copied from the live
store, encrypted key material serialized byte-for-byte the way the BDB
path writes it), checkpoints it, and swaps it in with a .before-encrypt
backup, mirroring the BDB rename dance. A fresh file rather than an
in-place rewrite keeps the change atomic and leaves no plaintext key in a
freed page.
CmdEncryptWallet skips PurgeDbEnvironmentLogs under SQLite (no Berkeley DB
environment behind the wallet) and reports the backend it used.
New wallet-sqlite-encrypt self-test drives the real command path in child
processes: export a wallet to SQLite, encrypt it through
-walletbackend=sqlite, and prove it comes out encrypted, unlockable with
the right passphrase, refusing the wrong one, with its recovery phrase
intact and the original wallet.dat left untouched.
|
|
Adds -walletbackend=sqlite: an opt-in flag that runs the node on
<datadir>/wallet.sqlite instead of wallet.dat. It requires an export to
exist first (no silent migration) and prints a step-by-step guide when
one does not; wallet.dat is never opened or touched, so a plain restart
goes back to Berkeley DB.
- LoadWalletFromSQLiteRuntime opens the export read-write, keeps the
handle open as the active backend, loads records into memory through
the same visitor/reconcile/finish path as the read-only staging load,
and never fabricates a default key.
- Shutdown checkpoints and closes the SQLite backend; a kill before that
loses nothing because WAL + synchronous=FULL already made every
committed write durable.
- Self-test proves runtime writes persist across a close and reopen.
BDB stays the default: nothing activates the SQLite runtime unless the
flag is given. One-shot audit/recovery commands still use Berkeley DB.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The legacy parser only accepted two decimal places (cents) and rejected
0.5, leaving six of the eight coin decimals unreachable by -sendto and the
send window. Parse up to 8 fractional digits Bitcoin-style, reject >8 to
avoid silent truncation. Adds the parse-money selftest (12 cases).
|
|
The coin type 4346950 (0x425446 = BTF) was merged into the SLIP-0044
registry on 2026-08-07. Rename HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL to
HD_BIP44_COIN_TYPE_BITFLASH and update comments, logs and UI strings.
Value is unchanged, so derivation paths and addresses are identical.
|
|
|
|
(#134)
* wallet: refuse future encrypted wallet formats
* wallet: add encrypted key unlock core
* wallet: add encryptwallet rewrite command
* wallet: read passphrases from stdin or files
* wallet: keep the derivation across encryption, and take the logs with it
Two things stopped the encryption stack from being safe to merge. Both
were found against a wallet holding real coin, not in the self-tests.
1. Encrypting turned a BIP44 wallet into a legacy one.
LoadWallet() ends by resetting the schema and the derivation counters
when no seed is in memory. That was written when "no seed after loading"
could only mean the wallet had none. An encrypted wallet has a seed --
it just has not been decrypted yet -- so the reset threw away metadata
that had been read correctly a moment earlier. Unlocking later restores
the seed and nothing restores the rest.
Measured on the wallet from the #124 proof: after encrypting, index 1
moved off BAtgSgra2X8Dqo5PJzPCLJhPwrrC2thYVJ, the address actually
holding 1.00 BTF, and the audit went from "1.00 covered by the phrase"
to "0.00 covered, 1.00 wallet.dat-only". Encrypt your wallet and the
program told you your recovery phrase no longer covered your money.
Now the condition also accepts a wallet whose seed is present but
encrypted. Same wallet, before and after: schema bip44, same four
derived addresses, 1.00 BTF still reported as covered.
2. The plaintext keys stayed behind in the Berkeley DB logs.
wallet.dat comes out clean -- verified by scanning the raw file for the
118 bytes of a known private key. database/log.0000000002 did not: it
held the whole key, and still did after a full node restart, because
every record this wallet ever wrote passed through the log.
That defeats the feature for the case it exists for, since our own
backup advice is to copy the whole directory rather than wallet.dat
alone. log_archive() cannot fix it: it only drops logs Berkeley DB
considers obsolete and never the one it is writing to, and with one 10
MB log per directory that is precisely where the keys are. They also did
not get there at encryption time -- they accumulated over the wallet's
whole life -- so nothing done during the rewrite can archive them out.
So the encrypt command now closes the environment and deletes the logs
on its way out, after the flush that makes every .dat self-contained.
This is not secure erasure; the blocks are unlinked, not overwritten.
It closes the case of someone reading a copied data directory.
Getting there uncovered a third bug, older than both.
DBFlush(true) is not safe to call twice. The first call closes the
environment, and txn_checkpoint() on a closed environment segfaults
instead of returning an error, so the try/catch around it never sees
anything. EncryptWallet() already flushes before renaming the wallet,
and the command flushes again on the way out -- that pair crashed every
single time. Guarded now.
The same function has been calling log_archive() with the address of an
uninitialised pointer since 0.1.0, on every clean shutdown, in a path
whose catch(...) cannot catch a structured fault on Windows.
DB_ARCH_REMOVE requires that argument to be NULL. Fixed while here,
because the encryption cleanup depends on that call not being a coin
flip.
---------
Co-authored-by: Gilcimar Duarte <ti@gpm.infra>
|
|
* Add BIP32 path derivation foundation
* Add HD wallet schema metadata
* Add provisional BIP44 coin type metadata
* Activate BIP44 wallet derivation
* wallet: scan BIP44 restore depth per branch
* wallet: widen default BIP44 restore lookahead
---------
Co-authored-by: Gilcimar Duarte <ti@gpm.infra>
|
|
Co-authored-by: Gilcimar Duarte <ti@gpm.infra>
|
|
CheckBlock() validated block size against MAX_SIZE, the serializer's generic 32 MB guard rather than a block rule; MAX_BLOCK_SIZE did not exist in this tree at all, because Bitcoin's 1 MB cap arrived in 0.3.x, after the 0.1.0 this fork came from. A miner could produce a 32 MB block and every node would download, verify and store it.
Original work by mtg0001 in #110, rebased over #109 with the self-test conflict resolved, plus a comment explaining why the inherited count-against-bytes test stays.
Measured before choosing the number: 5709 blocks read from blk0001.dat give a largest block of 34,290 bytes and a median of 216, so 1 MB leaves 29x of headroom and rejects nothing that exists. A node built from this branch then synced genesis to 5706 on the live network with zero size-limit rejections and zero CheckBlock errors -- the same test that caught BIP30 rejecting a real block at height 859.
|
|
Change from an HD wallet now goes to a newly derived phrase-backed key instead of reusing a spent input's key. Reusing an input key silently moved value back to a key the phrase cannot reproduce; if derivation fails the transaction is refused rather than creating unrecoverable change. Legacy wallets with no seed keep the 0.1.0 behaviour. Restore also scans past the pre-generated key-pool window before treating an empty batch as the end.
The message parser no longer consumes a header it cannot complete: it reads from a copy, so the O(n) reinsert and the 100 ms sleep inside the shared message thread are gone, and a peer that sits on an incomplete payload is disconnected after two minutes. The wire cap drops from 256 MB to MAX_SIZE. New net-message self-test covers complete, incomplete, stale and oversized headers.
Verified here: clean rebuild, all self-tests pass, lock order checked (no path takes cs_keyPool before cs_mapWallet), and a node built from this branch synced from genesis to the chain tip on the live network with zero rejections.
|
|
does not cover (#104)
Follow-up to #102. Reserves derivation index 0 before deriving it, so a failure between installing the seed and writing the default key cannot leave the pool free to hand that index out as an ordinary key. Renames the address that stops being the default to 'Your Address (created before the recovery phrase)', which was otherwise left under the same name as the new one. Also documents why the key-pool clear precedes the seed write, correcting a review comment of mine that asked for the reverse.
|
|
-newphrase installed a seed but left the wallet's visible address as the pre-seed random key and the old random pool intact, so the next address handed out -- a mining payout included -- could be one the twelve words cannot reproduce, while the window said a recovery phrase existed. Installing a phrase now clears the stale pool records (private keys stay in wallet.dat), reserves a derived default receiving key, tolerates already-stored derived keys so repeated installs do not stall the pool, and -restorephrase reports what it recovered rather than the wallet's total transaction count. Self-test covers installing a phrase over a pre-existing random pool.
|
|
Adds -newphrase, -restorephrase, -restoredepth, -newaddress and -showderived, plus the window buttons that reach the same code. Restore derives forward in batches of 100 and rescans after each, stopping when a whole batch turns up nothing; the phrase is shown on the terminal only, never in debug.log. Proved end to end on a live wallet: paid a derived address, deleted wallet.dat, restored from the twelve words and recovered the coin. Coins on the pre-seed default address do not come back, and the command says so.
|
|
Adds BIP32 derivation behind the wallet key pool, with no user-facing way to create a seed yet. wallet.dat gains hdmaster/hdchaincode/hdnext; the mnemonic is never stored. TopUpKeyPool derives m/n' when a seed exists and falls back to a random key, with a log line, if derivation fails. New -selftest=wallet-hd, wired into make tests on both platforms.
|
|
On Windows the self-test from #91 printed nothing. printf in this tree is
OutputDebugStringF, which writes to the data directory debug.log, and the
Windows binary is linked -mwindows with no console. A failure was still caught,
since a non-zero return stops make, but it arrived with no way to tell which
check failed.
And because the first line printed before strSetDataDir pointed at the temporary
directory, every line was appended to the real debug.log -- the file users are
asked to paste into issues.
The file now gets the real printf, the data directory is set before anything
prints, and on Windows a console is borrowed only when stdout has nowhere to go.
That last condition matters: the first version reopened stdout unconditionally
and took the output away from `make tests > log`, which is the case that counts.
|
|
Co-authored-by: Gilcimar Duarte <ti@gpm.infra>
|