upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
AgeCommit message (Collapse)Author
2026-08-19gui: add a "Restart Now" button to the conversion wizardbitflash
After converting to SQLite the wizard said "restart to apply" but offered no way to do it, so a user could close the dialog and keep mining on the old Berkeley DB wallet without realizing the switch had not taken effect -- and coins mined in the meantime land only in wallet.dat, not the SQLite wallet they think they are now on. The post-conversion screen now offers "Restart Now", which relaunches Bitflash with the same command line and datadir (a short delay lets this instance release the network port and block-index files first) and asks this one to exit, so mining resumes on the SQLite wallet. "Later" keeps the old behaviour. A dimmed line warns that mining before the restart still credits the old wallet. wallet.dat is still never touched. The conversion action itself is unchanged (wallet-convert self-test still green); this is GUI wiring only.
2026-08-18gui: offer to convert a Berkeley DB wallet to SQLite on first runbitflash
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.
2026-08-18wallet: make SQLite the default backend for new walletsbitflash
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.
2026-08-18wallet: one-shot storage commands honour -walletbackend=sqlitebitflash
-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.
2026-08-18wallet: bracket WriteHDMaster in a SQLite runtime transactionbitflash
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.
2026-08-18wallet: encrypt atomically under the SQLite backendbitflash
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.
2026-08-18Merge pull request 'net: cap inbound connections per address' (#60) from ↵Bitflash
net-per-ip-connlimit into main
2026-08-18net: exempt loopback from the per-IP connection capbitflash
Review follow-up. A managed-Tor node receives every inbound onion peer over its local hidden-service listener, so they all accept as 127.0.0.1. Capping loopback would cap onion peers as one group and refuse the 33rd legitimate onion peer -- the wrong limit for a network moving toward onion-only. Exempt loopback (127.0.0.0/8): the global MAX_CONNECTIONS still bounds the total, and the direct-IP flood this defends against never arrives over loopback. Bench: a 40-connection flood from 127.0.0.1 is now held in full (no per-IP refusal), while a flood from the machine's LAN address still caps at exactly 32.
2026-08-18wallet: clearer refusal for an empty or misnamed SQLite backendbitflash
Review follow-up on the SQLite activation. Two guards, both fail-closed; neither is a correctness fix -- FinishLoadedWallet already refuses a missing default key -- but they make the refusal say what happened: - an empty or non-wallet wallet.sqlite is refused with "no default key record; it may be empty or not a wallet export", instead of the generic "no usable default public key" that reads as corruption; - a misspelled -walletbackend value (e.g. sqlit) is refused instead of silently falling through to Berkeley DB; -walletbackend=bdb is accepted as the explicit default.
2026-08-18net: cap inbound connections per addressbitflash
A single IP could hold every inbound slot up to MAX_CONNECTIONS -- which is what a connection-exhaustion flood did to the rendezvous relays, one address parking hundreds of sockets and starving real peers. Add MAX_CONNECTIONS_PER_IP (32): the accept path counts existing connections from the same address and refuses a new one past the cap. The relays' firewall connlimit does the same a layer lower; this makes every build carry the defense, including future poll()-based relays with no FD ceiling.
2026-08-17wallet: run the node on a SQLite backend (opt-in)bitflash
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.
2026-08-17wallet: route CWalletDB through SQLite runtimeNsable-sh
2026-08-17tests: prove typed SQLite wallets loadNsable-sh
2026-08-17wallet: add typed SQLite wallet recordsNsable-sh
2026-08-14wallet: stage SQLite runtime loadingNsable-sh
2026-08-14wallet: check SQLite exports against loader typesNsable-sh
2026-08-14wallet: clean up failed SQLite migrationsNsable-sh
2026-08-14wallet: restore BDB wallets from SQLite exportsNsable-sh
2026-08-14wallet: verify SQLite wallet exportsNsable-sh
2026-08-14wallet: scan SQLite wallet recordsNsable-sh
2026-08-14wallet: export BDB records to SQLiteNsable-sh
2026-08-14wallet: add experimental SQLite record storeNsable-sh
2026-08-14db: recreate Berkeley DB environment after shutdownNsable-sh
2026-08-14tests: guard destructive wallet mutation helperNsable-sh
2026-08-14tests: prove wallet storage sanity failuresNsable-sh
2026-08-14wallet: install backups via temporary fileNsable-sh
2026-08-14tests: prove encrypted rewrite keeps public wallet recordsNsable-sh
2026-08-14wallet: route rewrite copies through record scanNsable-sh
2026-08-14wallet: add read-only record scan boundaryNsable-sh
2026-08-14wallet: drop bdb log check from storage sanityNsable-sh
2026-08-14wallet: initialize storage audit counts explicitlyNsable-sh
2026-08-13wallet: add storage sanity checkNsable-sh
2026-08-13wallet: export storage audit jsonNsable-sh
2026-08-13wallet: add storage audit commandNsable-sh
2026-08-13tests: assert encryptwallet purges BDB logsNsable-sh
2026-08-13tests: cover portable wallet copiesNsable-sh
2026-08-13wallet: report locked encrypted recovery seedNsable-sh
2026-08-13Merge pull request 'net: auto-start bundled managed Tor' (#37) from ↵Bitflash
auto-managed-tor into main
2026-08-13net: auto-start bundled managed TorNsable-sh
2026-08-13net: count direct onion peers by endpointNsable-sh
2026-08-13net: show Tor transport in diagnosticsNsable-sh
2026-08-13net: add onion-only btf dial modeNsable-sh
2026-08-13net: stop managed Tor through control portNsable-sh
2026-08-13net: fix managed Tor Windows startupNsable-sh
2026-08-13net: harden managed Tor lifecycleNsable-sh
2026-08-13net: add managed Tor hidden service bootstrapNsable-sh
2026-08-13net: add signed direct onion peer endpointsNsable-sh
2026-08-12wallet: collapse restored legacy hdnextNsable-sh
2026-08-12release: v1.2.18 (Tor/SOCKS5 outbound privacy, poll() socket handler, new ↵bitflash
app icon)
2026-08-12build: fix leftover merge markers in makefile.mingw (drop FD_SETSIZE, keep ↵bitflash
BITFLASH_NODE_BUILD)
2026-08-12test mergebitflash
# Conflicts: # src/makefile.mingw
2026-08-11net: replace peer select loop with pollNsable-sh
2026-08-11net: warn when Tor mode overrides SOCKSNsable-sh
2026-08-11net: add Tor mode for outbound discoveryNsable-sh
2026-08-11net: harden SOCKS5 proxy parsing and testsNsable-sh
2026-08-11net: add optional SOCKS5 outbound proxyNsable-sh
2026-08-11money: ParseMoney accepts full 8 decimals (0.5, 0.00000001)Bitflash
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).
2026-08-11wallet: SLIP-0044 4346950 is official, drop 'provisional' namingBitflash
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.
2026-08-07shutdown: flush wallet.dat on Windows headless stopBitflash
The headless loop installed SIGINT/SIGTERM handlers only under #ifndef _WIN32, so a headless node on Windows never reached the clean StopNode()+DBFlush() path: Ctrl-C, closing the console window, a service stop and OS shutdown all killed it mid-write, and a Berkeley DB wallet.dat left unflushed will not reopen. Install a SetConsoleCtrlHandler that mirrors the POSIX path: set fShutdown and, because the OS terminates us shortly after the handler returns on CLOSE/LOGOFF/SHUTDOWN, block until the main loop signals the flush is done. A 'taskkill /F' is still an uncatchable hard kill by design. Closes #16.
2026-08-07pool: publish round proof ledgerNsable-sh
2026-08-07bignum: shift setint64 in unsigned space to avoid signed overflow UBBitflash
setint64() extracted big-endian bytes with a signed left shift (n <<= 8). When the top byte is set that overflows int64 -- undefined behaviour, which the script fuzzer flagged under UBSan (bignum.h:158). The result was correct on two's-complement hardware, so this is a latent UB inherited from the original 0.1 code, not a behaviour change. Do the shift on a uint64.
2026-08-07pool: add local Stratum bridgeNsable-sh
2026-08-07pool: write dashboard status JSONNsable-sh
2026-08-07test: cover pool Stratum share accountingNsable-sh
2026-08-07pool: make payout ledger crash-safeNsable-sh
2026-08-07randomx: serialize cache initialization (#4)Nsable-sh
2026-08-07fuzz: add script parser harness (#5)Nsable-sh
2026-08-07net: disable legacy order messages (#3)Nsable-sh
2026-08-07wallet: cleanse unlocked secrets before clearing them (#2)Nsable-sh
2026-08-07nostr: verify relay TLS and lock peer counts (#1)Nsable-sh
2026-08-04fuzz: add P2P message parser harness (#142)mtg0001
* fuzz: add P2P message parser harness * fuzz: avoid duplicate legacy header includes * fuzz: seed the corpus with our network magic, not Bitcoin's The two seed inputs began f5 be b6 d9, which is Bitcoin's message start. Ours is bf 20 5c fd. CMessageHeader::IsValid rejects on that field first, so both seeds died at the first check and the smoke run exercised the reject path and nothing else. libFuzzer would have to mutate its way onto four exact bytes before reaching any parser, which does not happen in 128 runs. The job would have gone green while covering none of what it was added to cover. --------- Co-authored-by: Gilcimar Duarte <ti@gpm.infra> Co-authored-by: Bitflash-sh <btf01@proton.me>
2026-08-04rpc: build the pool block template the way the solo miner does (#141)Bitflash
Two bugs in RebuildJob(), the operator's Stratum template builder. It is a separate path from BitcoinMiner() and had drifted from it. A single pass over mapTransactions is not enough. That map is keyed by hash and carries no dependency order, so a transaction spending another still-unconfirmed transaction's output -- change from a recent send, usually -- fails ConnectInputs whenever its parent sits later in map order. One pass drops it, and map order does not change between rebuilds, so every later rebuild drops it at the same point. That is deterministic exclusion rather than bad luck: where pool operators mine most blocks, a transaction of that shape can stay unconfirmed for as long as its parent is. Now it retries until a pass adds nothing, which is what the solo builder has always done. And the fees reached nobody. nFees was declared inside the scan loop, so each transaction reset it and the running total was discarded, while the coinbase line passed a hardcoded 0. An operator collected fees into a block and paid none of them out. The block stayed valid, since paying less than allowed always is, so nothing ever complained. Both the diagnosis and the fix are Vic-Nas's, from his fork of this code. He deleted the fork, so this brings the work back rather than letting it go with it. His description of the failure was precise enough that finding the same two defects here took one read of our RebuildJob.
2026-08-04gui: say why the wallet would not open, where the user can see it (#136)Bitflash
Every fatal startup path wrote to stderr and to printf, and on Windows neither reaches anybody. printf is remapped to OutputDebugStringF, so it lands in debug.log inside the data directory. A -mwindows binary started by double-clicking has no stderr at all, and AttachTerminal() cannot help: it attaches to a parent console, which a double-clicked exe does not have. So Bitflash exited silently. Someone whose wallet.dat needs a newer build -- exactly what the wallet format guard was added to tell them -- double-clicked and watched nothing happen. The guard was correct and mute, which is the failure it was written to prevent. Fatal startup errors now go through one reporter: debug.log as before, stderr as before, and a message box when there is a person in front of the screen. Applied to the three that mean "your data will not open": the block index failing to load, the block index throwing, and wallet.dat failing or throwing. The box is deliberately not shown for a headless node. Those run under scheduled tasks and services, where a modal dialog waits forever for a click nobody is there to make -- a hung process instead of an exit code. Headless keeps stderr and the log. LoadWallet() now also reports why it refused, in a sentence, instead of only logging it. The unsupported-format case is the one that has to be understood by whoever reads it, and "LoadWallet failed" is not that. Verified by breaking a throwaway wallet on purpose and starting both ways. Headless: exit code 1, no dialog, no process left behind. Windowed with no console: a box naming the file, the Berkeley DB error, the usual cause, and where to look for more.
2026-08-04wallet encryption: the stack from #130–#133, with the two blockers fixed ↵Bitflash
(#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>
2026-08-03gui: name the icon resource GLFW_ICON so the window actually wears it (#126)Bitflash
The right icon has been in the exe the whole time. GLFW never found it. GLFW registers its own window class and fills hIcon with LoadImageW(module, L"GLFW_ICON", ...), falling back to IDI_APPLICATION when that exact resource name is missing. Ours was named "bitflash", so the lookup failed and every window this program has opened wore the generic Windows icon -- in the title bar and in the taskbar -- while the correct artwork sat unused a few kilobytes away. One line, verified by looking at it: rebuilt, ran the window, and zoomed in on both places the user pointed at. The title bar and the taskbar button now show the green BF coin. Checked the shell path too, since it resolves icons differently: ExtractAssociatedIcon on the new exe still returns the coin, so renaming the resource did not cost the file icon in Explorer.
2026-08-03Activate BIP44 wallet derivation (#124)mtg0001
* 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>
2026-08-03wallet: wire -sendto into main(), where it never was (#119)Bitflash
CmdSendTo() has been in walletcmd.cpp since #115 and nothing declared it or called it. The flag was dead: `bitflash -sendto=ADDRESS,AMOUNT` started a node and said nothing, because an unrecognised argument is not an error here. The v1.2.14 notes announce it under "A headless node can finally spend", and the headless Linux binary is exactly the one that does not have it -- `-help` there lists /newaddress and stops. The published Windows binary does have it, because it was built from a working tree that carried these two files uncommitted. So the artifacts disagree with each other and neither matches the tag. This adds only what was missing: the declaration in walletcmd.h, the dispatch in main(), and the -help line. No behaviour change to CmdSendTo itself. Checked on a build of this branch, against a throwaway data directory: -help lists /sendto=ADDRESS,AMOUNT -sendto=NOTANADDRESS,1 "Not a valid address", exit 1 -sendto=<valid>,-5 "Not a valid amount", exit 1 -sendto=<valid>,999 refused on an empty wallet, exit 1 All five self-tests still pass.
2026-08-03Add wallet recovery balance audit (#117)mtg0001
Co-authored-by: Gilcimar Duarte <ti@gpm.infra>
2026-08-03gui: read the Finish condition once, before the button that clears it (#118)Bitflash
Creating a recovery phrase from the window aborted with an ImGui assertion on the success path: Expression: (g.DisabledStackSize > 0) && "Calling EndDisabled() too many times!" File: imgui/imgui.cpp Line: 8435 The Finish button is wrapped in BeginDisabled/EndDisabled guarded by !g_phraseWrittenDown, and its own click handler sets g_phraseWrittenDown to false. So the pair was opened while the checkbox was ticked and closed after the handler had unticked it: BeginDisabled was never called, EndDisabled was, and ImGui asserted on the unmatched close. It fires on the only path that installs a seed -- tick the box, press Finish -- so every phrase created from the window has hit it since the feature shipped in 1.2.12. Three releases carried it because nothing tests the window. Reproduced on the published 1.2.14 binary and confirmed fixed on this build, driving the real window: Wallet Safety -> Create recovery phrase -> tick -> Finish. The old build raises the assertion; the fixed build closes the dialog, reports "This wallet has a recovery phrase", and the address changes to a derived one. The seed itself was already written before the assertion, so the wallet was never left half-installed -- and ImGui's IM_ASSERT_USER_ERROR_RET returns before touching its state, so answering Ignore was safe. Abort was not: it kills the process with no DBFlush. The restore dialog next door already does this correctly, copying the condition into a local first. This makes the create dialog match.
2026-08-02wallet: one quiet batch is not far enough to stop a restore (#115)Bitflash
Restoring from a phrase gave up after the first batch of a hundred addresses that turned up nothing, and the wallet digs a wider gap than that by itself: a restore leaves nHDNext at the depth it scanned, the key pool derives KEYPOOL_SIZE more, and change takes the index after those. Measured on a real wallet with real coin -- restore, spend once, and the outputs land at indices 201 and 302; restoring again found only the first transaction and reported the spend as gone. With -restoredepth=500 the same wallet found both, which is what said this was reach and not loss. Three quiet batches instead of one: 2 transaction(s) across 601 derived addresses on the same wallet, chain and phrase. Worse after #109, not before it: change used to return to an input's key, a low index the scan had already passed. Making change recoverable moved it out to where the scan stopped looking. Does not fix the gap growing with every restore -- pulling nHDNext back to the last used index is the structural answer and needs its own testing.
2026-08-02net: back to one accept thread until every relay takes more than one (#114)Bitflash
#113 added three accept threads and called the extras harmless against an un-upgraded relay. They are not: an old relay closes the previous registration whenever the same node registers again, so the threads evict each other in a loop. Measured on the live network -- the node whose registrations landed on the un-upgraded New Jersey relay made 189 registrations in fifteen minutes with zero pairings, unreachable through it and hammering it, while the node on the upgraded relay made 9 with 7 pairings. One relay of three is upgraded, so this would hit two thirds of every node's registrations. The relay half of #113 stays; it is compatible in both directions and is the prerequisite for raising this again, which the comment now states.
2026-08-02rendezvous: let a node park more than one registration (#113)Bitflash
A registration was consumed by the first caller and registering again replaced the previous one, so a node was reachable to one peer at a time and unreachable for the gap between being paired and registering again. Measured on a live node: descriptors resolved 53/54, our own registrations paired 11/12, nothing paired and went silent -- and half of every outbound dial failed, 15 of 30 in eleven minutes. The relay keeps a queue per pubkey capped at four, pairs with the oldest still-alive registration and discards the dead; the node runs three accept threads, each holding its own registration. The wire protocol does not change, so either side can be deployed first. Bench with both relays built from source and waves of concurrent dialers: old+1 65%, new+1 58%, old+3 68%, new+3 98%. Each half alone lands within noise of doing nothing.
2026-08-02Add consensus block size limit (#111, from #110)Bitflash
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.
2026-08-02Keep HD change recoverable and bound message stalls (#109)mtg0001
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.
2026-08-01docs: bring the README up to the wallet that exists, and make -help print (#106)Bitflash
The README still said deterministic restore was an open issue, told people the mining mode was not remembered between restarts, and wrote every flag in the slash form that MSYS2 turns into a path before the node sees it. Rewritten around the phrase and the file as two backups covering different keys, the -restorephrase note for anyone who made a phrase on 1.2.12, -rescan, the diagnostics report, and the two upgrade reasons measured this week. Also: -help printed nothing at all. This tree remaps printf to OutputDebugStringF, so the usage page went into debug.log, and a -mwindows binary has no console to write to either way -- the one command whose job is to tell you something returned 0 and said nothing. It now takes the real printf and attaches the terminal. The help text was missing -restoredepth, -showderived and the wallet-hd self-test.
2026-08-01wallet: reserve index 0 before deriving it, and name the address the phrase ↵Bitflash
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.
2026-08-01Keep new phrase addresses recoverable (#102)mtg0001
-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.
2026-08-01net: stop closing a peer's socket twice (#103)Bitflash
CNode::Disconnect() closed the socket and left the handle number in the object; ~CNode() then closed it again with a raw closesocket() the accounting never saw. There is no assignment of INVALID_SOCKET to CNode::hSocket anywhere in this tree, so the destructor's guard never fired. Between the two closes the OS can reissue that number, and the second close then takes down somebody else's connection. The same omission left a closed handle in the select() set, which fails every iteration and drives the recovery path that prints 'dropping node: its socket is no longer valid'. Measured A/B on the real network at matched socket volume: unpatched 552 sockets, 4 closes failing with 10038, 741 invalid-socket drops (all one peer), 11 peers held; patched 547 sockets, 0, 0, and 21 peers held. No protocol change.
2026-08-01diag: catch a handle handed out while a site still claims it (#101)Bitflash
A live node reported one close failing with 10038 (WSAENOTSOCK) at the tunnel's pump end. It leaks nothing; what it says is that a site's bookkeeping and its sockets disagree about ownership -- and Windows reissues a handle number only after its last close, so a site whose bookkeeping outlives its socket will eventually close a number that belongs to a live connection. BtfSocketTag now counts a handle arriving while another site still claims it, which names the site to go and read.
2026-08-01diag: count the socket leak the way that is true, and say what it found (#100)Bitflash
Windows lists a wildcard-bound socket twice in MSFT_NetTCPConnection, so counting Bound rows makes the number climb whenever the node talks to more peers -- 35 of a node's 38 Bound rows shared a local port with one of its own established connections. Counting orphans instead: a 1.2.10 node at 26h held 1262 and was still taking on 30-90 an hour; nodes on 1.2.11 and 1.2.12 held 3, transient. The leak was real and 1.2.11 ended it. Adds a live-unconn column (getpeername on every tagged socket, reports 1: the listener) and rewrites the notes that said otherwise.
2026-08-01build: list every header, so editing one rebuilds what includes it (#99)Bitflash
sockcount.h was in neither makefile's HEADERS, so editing it rebuilt only the objects whose .cpp had also changed and the rest kept the old struct layout -- one object indexing a shared function-local static by the old layout and another by the new one, in the same binary. compat.h and font_roboto.h were missing from one list each. Verified with a full clean rebuild: no duplicate-section warning.
2026-08-01diag: say why a close failed, and total what the program thinks it holds (#98)Bitflash
The 1.2.11 accounting answered 'which site' and stopped. On a node with 3h51m of uptime it reported 58 live sockets while Windows attributed 112 to the process, 47 of them bound and never connected, against 55 closes that had returned an error -- with nothing recording what those errors were. Close failures are now counted per code per site, and the table totals, so the live figure can be compared directly against what the OS attributes to the process. Instrumentation only.
2026-08-01wallet: create and restore a recovery phrase (#96)Bitflash
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.
2026-08-01wallet: derive the key pool from a seed, so a phrase could bring it back (#93)Bitflash
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.
2026-08-01Avoid MSYS2 self-test argument conversion (#94)mtg0001
Co-authored-by: Gilcimar Duarte <ti@gpm.infra>
2026-08-01diag: make the self-test report where the person running it can see (#92)Bitflash
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.
2026-08-01Add wallet keypool self-test (#91)mtg0001
Co-authored-by: Gilcimar Duarte <ti@gpm.infra>
2026-08-01wallet: walk the chain for coins a key owns but the wallet never saw (#88)Bitflash
A transaction only entered the wallet when its block arrived over the network, so a key added afterwards was invisible: coins on the chain, private key in wallet.dat, balance zero. /importwallet has always ended that way -- it writes the keys and the money never shows up, which looks exactly like a failed import. ScanForWalletTransactions walks the main chain and hands every transaction that IsMine() to AddToWalletIfMine, the same two primitives the live path uses. Wired into /importwallet, and offered as /rescan. It is also the half that any recovery-phrase feature has to stand on: deriving keys from twelve words is easy, and without this a restore restores nothing.
2026-08-01wallet: keep the default backup out of the directory it protects (#90)Bitflash
Two follow-ups to #89. The default backup path was inside the data directory -- same disk, same folder the README says to copy wholesale, same folder the user eventually deletes, which is the moment the backup was for. It now defaults to the home directory and the dialog says so when a path under the data directory is chosen anyway. And nLastWalletBackup was written by the GUI, so anyone backing up with /backupwallet -- the method the README documents first -- was told forever that they had never backed up. BackupWallet records it now, so both paths count.
2026-08-01Add wallet safety backup panel (#89)mtg0001
Co-authored-by: Gilcimar Duarte <ti@gpm.infra>