| Age | Commit message (Collapse) | Author |
|
Brings the nine commits that are actually the latest on our lineage:
isolated testnet parameters and genesis, optional 2 MB large pages for
RandomX, and five README changes. This is what "up to date" means here --
not the 1.2.2x tags, which carry higher version numbers but were cut
2026-07-30, three weeks before the line we ship, and which upstream/main
does not contain.
Three conflicts, all resolved toward upstream's structure while keeping
what this fork knows:
main.cpp -- upstream moved genesis construction into BuildGenesisBlock() so
mainnet and the new testnet can share it. Verified byte-for-byte before
taking it: MAINNET_GENESIS_TIMESTAMP, _TIME (1753315200), _NONCE (3141),
_HASH, _MERKLE, the scriptPubKey, nBits and nVersion all equal what this
tree built inline. The mainnet genesis hash is unchanged and the asserts
still guard it. Kept our warning about never editing those inputs, and kept
our note that 1753315200 is 2025-07-24, not 2026 -- upstream's comment on
that constant states the wrong year.
net.h -- DEFAULT_PORT = htons(8433) becomes MAINNET_PORT/TESTNET_PORT in
host order. Nothing is lost: net.cpp's GetDefaultPort() applies htons()
itself and is what every caller uses, so the value reaching a sockaddr is
unchanged and mainnet stays on 8433.
selftest.cpp -- the known-tests string had diverged in both directions.
Took the union: upstream's network-params alongside our payout-ledger,
rather than dropping either.
Gates: 15/15 selftests (our 14 plus upstream's network-params), consensus
constants unchanged against e4f898c except the pre-existing
MAX_BLOCK_SIZE_GEN, mainnet genesis intact, compactblock's 17 checks still
pass, money-log check ok, GNU_STACK RW.
|
|
std::thread throws std::system_error when pthread_create is refused
(RLIMIT_NPROC, a container's pids cgroup, memory pressure), and nothing
caught it, so the node died by std::terminate. Threads are spawned per
peer connection, so the pressure that triggers this scales with peer
count, which is attacker-influenced. Observed killing a healthy
participant worker mid-mining.
Every spawn site in the node build, and what a failure now does:
- btftunnel.cpp: both tunnel pumps are guarded. The inbound pump starts
first because it owns cleanup of the pump end and rendezvous socket;
if the second spawn is refused, shutdown() wakes the first out of its
read and it cleans up itself -- closing those descriptors from the
failure path instead would race it onto reused descriptors. Both
paths return INVALID_SOCKET, which every caller already treats as
"this connection failed".
- btfrv.cpp: the relay's accept loop drops the connection it cannot
serve and stops accepting for a moment -- longer after consecutive
failures, capped at 5s -- instead of dying at an arbitrary one. The
per-pair second pump now runs on the handling thread itself, leaving
exactly one spawn to guard and nothing running yet to race.
- main.cpp: a participant that cannot spawn its hash workers stops the
ones that did start, drops the pool connection, and returns to the
reconnect loop, the same way an ordinary disconnect does.
- randomx_pow.cpp: dataset init joins the workers it got, releases the
dataset, and stays in light mode -- the same trade the allocation
failure path above it already makes.
- rpc.cpp, net.cpp: the remaining unchecked _beginthread calls (which
report failure by value, they cannot throw) now say precisely what
did not start. The pool's accept and payout threads failing silently
left the pool running deaf -- nobody accepts, nobody gets paid, and
nothing in the log says why.
Left alone deliberately: the per-relay accept spawns (rpc.cpp) already
check, log and retry on the next poll; startup-critical spawns in
net.cpp keep failing hard; the selftest-only std::thread uses may abort
a selftest run, which is acceptable where dying over one peer is not.
Verified: full node rebuilds clean and links; pool-stratum selftest
passes; a scratch harness under RLIMIT_NPROC=1 confirms _beginthread
reports -1 and std::thread throws system_error, the premises of every
path above.
|
|
The reservation instruction printed when a large-page allocation falls back was
Linux-only (sysctl vm.nr_hugepages). Route it through a small helper so a
Windows build points at the "Lock pages in memory" privilege instead.
|
|
RandomX reads its 256 MB cache, 2 GB dataset and per-VM scratchpads at random,
so most accesses miss the TLB on 4 KB pages. Requesting 2 MB pages recovers
roughly a tenth of the hash rate. Each allocation tries large pages first and
falls back to normal pages when the pool is not reserved, so nothing regresses
when hugepages are unavailable. -nolargepages opts out, and the diagnostics
report which allocations got large pages.
|
|
|
|
Every miner thread calls RandomXInitDataset at startup. Its only guard is
g_fFast, which is set at the very end -- after the ~2 GB dataset has been
filled, which takes tens of seconds. So all of them sailed past the guard
together and each allocated its own dataset.
Measured in production on a 32-core machine running 31 miner threads: the
log says "initializing ~2 GB dataset" 34 times, and the process holds
65,127 MB of private pages while its own startup line promises 2142 MB
"(~2080 MB shared RandomX dataset + ~2 MB per thread)". The word shared
was not true. A 4-core machine showed the same thing at its own scale: 4
allocations for 3 threads.
The waste was the mild half. g_dataset is assigned the moment memory is
allocated, before it holds anything, so the last thread to allocate swung
the pointer out from under threads still filling their own. A miner could
then hash against a dataset another thread had not finished writing, and
proof of work computed that way is not reproducible by any other node --
the block would be found invalid by everyone, including this node's own
verifier, which uses the cache rather than the dataset.
This only became reachable with multi-threaded mining (#52); with one
thread there was nobody to race.
Holding the lock across the whole initialisation is deliberate: a thread
arriving mid-init has nothing useful to do until the dataset exists, and
blocking is how it waits for exactly that.
Verified with three mining threads: one "initializing ~2 GB dataset", one
"dataset ready", and 2362 MB of private memory against 2086 MB advertised
-- the difference being the 256 MB cache. Before, three threads meant three
datasets.
|
|
Satoshi's bitcoin-0.1.0 reborn with RandomX CPU proof of work, anonymous .btf
addressing over Nostr with CGNAT rendezvous, automatic peer discovery, a
headless Linux node, and Windows GUI wallet. Fair launch, no premine, 21M cap.
|