bch(m5): share + pool p2p + embedded-daemon entrypoint (M5 merge — 12 commits, M3/M4 absorbed) - #133
Merged
Conversation
Lay the BCH share-layer p2p protocol skeleton against the M3 core::coin seam, mirroring the btc share layer. Source-only; no impl_bch CMake registration (bch stays skip-green, off ci-steward critical path). peer.hpp: per-connection peer state (versions, services, remote/remembered txs). messages.hpp: p2pool wire protocol (version/ping/addr*/shares/ sharereq/sharereply/bestblock/*_tx) — kept byte-compatible with p2pool-merged-v36 (p2pool/p2p.py). BCH divergence: no SegWit/witness, so message_remember_tx serializes coin::MutableTransaction plainly (no TX_WITH_WITNESS wrapper); confirmed against bitcoincash.py (no SEGWIT_ACTIVATION_VERSION) and coin/transaction.hpp.
…slice 1B) Mirrors the btc pool layer for the BCH SHA256d standalone-parent chain: - node.hpp: pool::BaseNode<Config,ShareChain,Peer> share node — share_getter ReplyMatcher, known_txs, tracker/storage wiring, verified-hash LevelDB persistence via m_on_share_verified. SHA256d share-PoW verify (no scrypt); NO merged-mining / AuxPoW path (BCH is a standalone parent). - pool_monitor.hpp: hashrate + difficulty monitor; coin-independent, drives off get_pool_attempts_per_second. - share_tracker.hpp: add verified sub-chain (known_verified mirror) + node callback hooks (m_on_share_verified / m_on_block_found / m_on_share_difficulty; no merged-block callback — BCH has no aux leg) + port get_pool_attempts_per_second (p2pool-faithful work/target math). Source-only; no impl_bch CMake registration (bch stays skip-green). All three units -fsyntax-only clean under -std=c++20.
Re-home the ABLA size feed to the full-block/daemon layer so the template builder builds to the live CHIP-2023-01 block-size limit, not just the 32 MB floor. ABLA is advanced by each block s actual serialized size, which the headers-only SPV header_chain does not carry (M4 s3 pin); the running State therefore cannot derive from the header chain and must be fed real per-block sizes as full blocks arrive at the daemon layer. - abla_tracker.hpp (new): AblaTracker folds live contiguous block sizes through abla::State::NextBlockState, keeps the running tip State, and hands a tip State* to build_template. Detects size-feed gaps and marks itself stale so the builder falls back to the floor until reanchor(). floor_anchored() gives a safe cold start at the 32 MB activation State. - template_builder.hpp: EmbeddedCoinNode gains an optional AblaTracker; getwork() passes the per-tip ABLA State to build_template when the feed is current at the tip, else nullptr -> the existing 32 MB floor budget. Hard floor invariant: the dynamic budget is always >= the 32 MB floor (ABLA only raises; absent/stale feed falls back outright). Validated by roundtrip: cold start 32 MB, 5000 full blocks grow to 39.48 MB (matches M4 s3 replay), mixed load never < floor, gap -> floor, testnet fixed 32 MB. LOCAL build budget only: zero p2pool-merged-v36 surface (no PoW/share/ coinbase/PPLNS touch). Source-only, scope confined to src/impl/bch/coin/, no impl_bch CMake registration (bch stays skip-green).
Closes the gap left by M5 slice C: AblaTracker::record_block_size existed but nothing in-tree called it. AblaBlockFeed subscribes to interfaces::Node::full_block and folds each best-chain block.s actual serialized size into the tracker, resolving height from the HeaderChain best-chain index (header sync precedes full-block download per BIP130). Contiguous-tip-only by construction: blocks not on the indexed best chain are skipped, and the tracker.s own gap-detect drops the budget to the 32 MB floor rather than fold an untrusted height. Reorg/skip can only ever lower the budget to the safe floor, never raise it on bad data (never-undercut invariant). Reanchor is a separate path. p2pool-merged-v36 surface: NONE — local build-time block-size budget only; never touches PoW hash, share format, coinbase commitment, or PPLNS. Per-coin isolation: src/impl/bch/coin/ only. Header-only, build-inert (no impl_bch CMake registration; bch stays skip-green). -fsyntax-only OK (g++ -std=c++20).
The compact-block + getblocktxn round-trip reconstructed and delivered the full block but never fired interfaces::Node::full_block, unlike the direct block handler and the complete-path cmpctblock handler. Blocks arriving via that path therefore never reached AblaBlockFeed, producing a height gap that dropped the template size budget to the 32 MB safe floor. Emit the reconstructed block on full_block so the live ABLA size feed covers all three delivery paths (block / cmpctblock-complete / cmpctblock+blocktxn).
Record the emit-completeness conformance verdict in the p2p_node header. The frstrtr/p2poolBCH baseline (@6603b79) delivers block arrivals via exactly two events: handle_inv -> new_block.happened(hash) and handle_block -> get_block.got_response(hash, block) -- a single full-block path, no broadcast event, no compact-block handling. Our direct block handler mirrors that ReplyMatcher surface; the two compact-block paths (cmpctblock-complete and cmpctblock+blocktxn) are additive embedded-daemon coverage with no Python analog, funneling into the same full_block sink. full_block is internal ABLA/ mempool plumbing -- zero p2pool-merged-v36 surface. Conformance documentation only; no behavioral change.
….hpp Validate every received/reconstructed block against its header merkle commitment before publishing it on full_block. All three delivery paths (direct block msg, compact-block-complete, compact+getblocktxn round-trip) now funnel through emit_full_block(), which recomputes the SHA256d tx merkle (BCH==BTC rule; CTOR only fixes order, already carried) and drops any header/txs pair that does not cohere -- so a bogus serialized size can never be folded into the ABLA feed. Mirrors BCHN CheckMerkleRoot at accept. Extract merkle_hash_pair/compute_merkle_root into coin/merkle.hpp as the single source shared by the GBT template builder (producer) and the full-block accept path (consumer); template_builder.hpp now includes it. Source-only / build-inert (no impl_bch CMake reg). p2pool-merged-v36 surface: NONE -- local parent-chain accept gate, no share/PoW/coinbase commitment/PPLNS touch.
BCH rejected SegWit at the 2017 fork and the p2poolBCH oracle never emits a segwit_data field in any share version: data.py:63-66 derives is_segwit_activated from getattr(net, SEGWIT_ACTIVATION_VERSION, 0) and bitcoincash[_testnet].py define no SEGWIT_ACTIVATION_VERSION, so it is False for all versions. c2pool-bch had SEGWIT_ACTIVATION_VERSION = 17 (a stray BTC/LTC port), which forced segwit_data into the share_info serialization and into the ref_hash for every v17..v35 share -- a guaranteed byte-level divergence from p2poolBCH and thus a sharechain fork against existing BCH history. Set the sentinel to 0 (disabled) and gate is_segwit_activated() on it, mirroring the oracle guard. Route the 6 raw ver >= SEGWIT_ACTIVATION_VERSION comparisons in share_check.hpp through is_segwit_activated(ver) so the disabled sentinel is honored consistently (serialization, ref_hash, gentx output count, merkle root). Non-segwit fallback paths already exist at every site. Conformance fix; v17..v35 now byte-match the oracle. Legacy-version conformance only; v36 segwit handling tracked separately. Zero p2pool-v36 surface beyond restoring it.
WorkData struct + getwork() GBT m_data emission feed the coin-agnostic core/stratum_server notify path 1:1 with p2poolBCH stratum.py/data.py. BCH-specific inputs (version=4 floor, 8-char-hex bits, no-segwit coinb2) confirmed non-impacting on the notify wire-shape. Conformance-fixture only; zero compile surface, zero p2pool-v36 surface. M5 sweep COMPLETE.
Owner that constructs the AblaTracker + AblaBlockFeed and wires them to a
live interfaces::Node and EmbeddedCoinNode. The four pieces existed in
isolation (tracker folds sizes, feed subscribes to full_block, embedded
node consumes the budget, full_block fires post merkle-accept-gate) but
nothing tied them together. After wire():
full_block -> AblaBlockFeed -> AblaTracker -> EmbeddedCoinNode::getwork
-> TemplateBuilder dynamic ABLA budget (else 32 MB floor).
Cold-start floor anchor (never undercuts the 32 MB floor; live sizes only
raise it); BCHN-pinned-anchor ctor + reanchor() passthrough for gap/reorg.
Tracker declared before feed so lifetimes are correct. Single-coin,
src/impl/bch/coin/ only; zero p2pool-merged-v36 surface. Source-only,
header-only, no impl_bch CMake registration (bch stays skip-green).
…eddedCoinNode/AblaRuntime One owner per embedded daemon instance: constructs HeaderChain, Mempool, EmbeddedCoinNode, coin::Node<Config> and AblaRuntime in lifetime order, runs the node, and closes the ABLA size loop via AblaRuntime::wire(). Embedded getwork() is the primary work source; the external BCHN-RPC fallback path (coin::Node::init_rpc/m_rpc) is retained alongside per the per-coin external_fallback invariant. Cold-start floor-anchored ABLA; the VM300 BCHN-pin reanchor is exposed as apply_bchn_anchor() and left for the operator-gated anchor-capture step. Header-only, build-inert, src/impl/bch only, zero p2pool-merged-v36 surface.
frstrtr
force-pushed
the
bch/m5-share-layer
branch
from
June 17, 2026 18:56
5adcbfa to
a77ccb9
Compare
…RPC fallback EmbeddedDaemon owned EmbeddedCoinNode (work source) and Node (P2P + external BCHN-RPC) but nothing constructed the core::coin::ICoinNode the pool/web_server consumes. Build the CoinNode in run() once Node::rpc() is live: embedded work source = primary, external BCHN-RPC = retained fallback (v36 external_fallback invariant). Add Node::rpc() accessor. Header-only, build-inert (bch skip-green); zero p2pool-v36 surface; src/impl/bch/ only.
…300 anchor Add BchnAnchorRecord: the static, read-only VM300 bchn-bch capture @955700 (height/hash/chainwork/time + ABLA control state, recorded once; the live VM is never read here). EmbeddedDaemon::dry_run_bchn_anchor() logs exactly what apply_bchn_anchor() would pin and confirms the recorded control state is still at the 32 MB floor, so cold-start origin is the recorded anchor (not genesis) while the AblaRuntime replay stays pinned to the safe floor. Real reanchor stays operator-gated; surfaces a non-floor budget if a future capture shows one. Embedded-internal, src/impl/bch/ only, zero p2pool-v36 / bitcoin_family surface.
…k invariants Validates the three ICoinNode invariants web_server depends on against coin_node.cpp: (1) no work source -> empty WorkView, no throw; (2) embedded present -> WorkView sourced from embedded (is_embedded, !has_rpc); (3) no RPC sink -> submit_block_hex returns false via guard, not a throw. The external NodeRPC fallback leg is integration-only-by-design (not faked, to keep BCH NodeRPC a 1:1 btc mirror). Source-only / build-inert: -fsyntax-only verified, impl_bch stays unregistered (skip-green).
…s 32 MB floor Pins the safety claim EmbeddedDaemon cold-start rests on: the dynamic build budget can only EQUAL-OR-EXCEED the 32 MB activation/floor limit. Asserts against AblaTracker: (1) floor_anchored cold-start budget == floor exactly; (2) contiguous record advances cursor and budget stays >= floor (ABLA only raises); (3) a height gap marks the cursor stale -> state_for_tip()==nullptr, budget falls back to the floor, and further records are ignored until reanchor(); (4) reanchor() restores a current State and re-enables folding. Mainnet (growing) and testnet (fixed 32 MB no-op) both covered. Source-only / build-inert: impl_bch stays unregistered (bch=skip-green). Verified compile+run out of tree (ALL PASS), header-only against coin/abla*.hpp — no node/RPC/boost. p2pool-merged-v36 surface: NONE.
Faithful header-only port of BCHN cashaddr.cpp + cashaddrenc.cpp into
bch::coin::cashaddr: base32 + BCH-code 40-bit PolyMod checksum, version-byte
{type,size} packing, ConvertBits 8<->5, and the bitcoincash/bchtest/bchreg
prefixes. Self-contained at the {type,hash}<->string seam (CashAddrContent) --
no chainparams/pubkey/script graph, so it is build-inert and unit-testable
out of tree.
BCH divergences carried: CashTokens token-aware address types (z/r,
CHIP-2022-02) and P2SH32 32-byte hashes (CHIP, May 2023) via the version
size bits, alongside legacy 20-byte P2PKH/P2SH.
cashaddr_test.cpp: 34 checks, vectors lifted verbatim from BCHN
cashaddrenc_tests.cpp + cashaddr_tests.cpp (the conformance oracle) --
anchored encode strings (P2PKH/P2SH + token variants), 20B/32B roundtrip,
rejection paths (corrupt checksum / wrong prefix / mixed case / bad length),
and checksum-only valid_strings. Compiles + runs ALL PASS standalone.
Operator/payout-address codec only -- share, sharechain, coinbase-commitment
and PPLNS layers serialize scripts, not address strings. p2pool-merged-v36
surface: NONE. impl_bch stays unregistered (bch = skip-green).
… pin raises Pins the two claims EmbeddedDaemon::apply_bchn_anchor()/dry_run_bchn_anchor() (48a4534) rest on: (A) the static VM300 capture @955700 is at the 32 MB floor, so pinning it is a no-op vs the cold-start floor — is_floor() agrees with the rebuilt abla::State on mainnet+testnet and the recorded limit field; (B) a future ABOVE-floor capture, when reanchored, RAISES the build budget to its live limit and never undercuts the floor. Standalone (abla*.hpp + bchn_anchor_record.hpp; no HeaderChain/Node/RPC). Build-inert, impl_bch unregistered. p2pool-merged-v36 surface: NONE.
… + off-chain-skip + dup-idempotent)
Pins the two load-bearing decisions abla_floor_invariant_test does not
exercise (it drives AblaTracker directly, bypassing the feed):
(a) height resolved from HeaderChain index, never guessed
(b) off-chain block (unindexed hash) dropped with NO record_block_size
-> cursor never advanced on a height we cannot trust
Drives on_full_block against a real in-memory HeaderChain seeded so one
crafted block resolves to a known height: contiguous fold raises budget
(>= 32 MB floor), off-chain block = zero state change, duplicate = clean
idempotent ignore. Source-only (impl_bch unregistered; bch=skip-green).
compile+link+run ALL PASS. p2pool-merged-v36 surface: NONE.
…happy-path + ABLA at-tip/stale floor + real-node ICoinNode seam Closes the getwork() coverage gap (seam test drove a fake; this exercises the REAL EmbeddedCoinNode end-to-end against in-memory HeaderChain + empty Mempool). Source-only / build-inert (impl_bch unregistered, bch=skip-green). p2pool-v36 surface: NONE — emits the coin-agnostic WorkData the sweep already pinned.
frstrtr
marked this pull request as ready for review
June 18, 2026 08:08
frstrtr
added a commit
that referenced
this pull request
Jun 18, 2026
…ed-body lane Non-destructive merge of origin/master into bch/m5-embedded-body-wip to keep the embedded daemon body lane on current master. No force-push, shared history preserved. External-RPC fallback retained.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
bch(m5): share + pool p2p + embedded-daemon entrypoint — purpose-built merge PR
Repurposed from the prior "held pile" framing. This PR is scoped to exactly the M5 code that should land on master. M3 and M4 are already merged, so nothing from those milestones lands here.
Verified bookkeeping
bch/m3-coin-node, M3 SPV+P2P) — MERGED 2026-06-16.bch/m4-template-builder, M4 ABLA template) — MERGED at6336679a.git log master..HEADbelow is M5-only.Exactly what lands (
origin/master..6afa896d, 12 commits, allsrc/impl/bch/+ sharedsrc/core/coin/merkle.hpp)Properties
6afa896d, basemaster, mergeStateStatus CLEAN / MERGEABLE.