|
| 1 | +#pragma once |
| 2 | +// --------------------------------------------------------------------------- |
| 3 | +// bch::coin::AblaRuntime -- M5: the CLOSING assembly of the embedded-daemon |
| 4 | +// ABLA size loop. The four pieces existed in isolation after slices C/sG: |
| 5 | +// AblaTracker -- folds each best-chain block size, holds running State |
| 6 | +// AblaBlockFeed -- subscribes to interfaces::Node::full_block, sinks size |
| 7 | +// EmbeddedCoinNode -- consumes the tracker for the dynamic build budget |
| 8 | +// full_block event -- fires from all 3 p2p delivery paths (direct / cmpct / |
| 9 | +// blocktxn) AFTER the merkle-root accept gate (sG) |
| 10 | +// ...but nothing OWNED a tracker, owned a feed, and tied them to a live node + |
| 11 | +// EmbeddedCoinNode. This runtime is that owner: one object the embedded-daemon |
| 12 | +// front-end (coin::Node) holds, constructed once the HeaderChain exists and |
| 13 | +// wired once the node + EmbeddedCoinNode exist. After wire(): |
| 14 | +// full_block --> AblaBlockFeed --> AblaTracker --> EmbeddedCoinNode::getwork |
| 15 | +// --> TemplateBuilder dynamic ABLA budget (else 32 MB floor). |
| 16 | +// |
| 17 | +// COLD START = floor anchor. Absent a BCHN-pinned {height,State}, we anchor at |
| 18 | +// the activation/floor State (limit == the 32 MB floor); folding live sizes |
| 19 | +// forward can only RAISE the budget, never undercut the floor -- the |
| 20 | +// never-undercut invariant holds from the first block. A later BCHN pin is a |
| 21 | +// reanchor() passthrough, no reconstruction. |
| 22 | +// |
| 23 | +// LIFETIME: m_tracker is declared before m_feed so the feed`s AblaTracker& |
| 24 | +// binds to a fully-constructed member, and the tracker outlives both the feed |
| 25 | +// subscription and the raw pointer handed to EmbeddedCoinNode. The runtime must |
| 26 | +// outlive the node it wired (the daemon owns both for its whole run). |
| 27 | +// |
| 28 | +// p2pool-merged-v36 SURFACE: NONE. Pure local build-time block-size budget -- |
| 29 | +// no PoW hash, share format, coinbase commitment, or PPLNS math is touched. |
| 30 | +// PER-COIN ISOLATION: src/impl/bch/coin/ only; every type reached is bch-owned. |
| 31 | +// Build-INERT / source-only: header-only, no impl_bch CMake registration |
| 32 | +// (bch stays skip-green; don`t race ci-steward). |
| 33 | +// --------------------------------------------------------------------------- |
| 34 | + |
| 35 | +#include "abla.hpp" // abla::State (reanchor / pinned-anchor ctor) |
| 36 | +#include "abla_tracker.hpp" // AblaTracker |
| 37 | +#include "abla_block_feed.hpp" // AblaBlockFeed |
| 38 | +#include "header_chain.hpp" // HeaderChain (height source for the feed) |
| 39 | +#include "node_interface.hpp" // bch::interfaces::Node (full_block source) |
| 40 | +#include "template_builder.hpp" // EmbeddedCoinNode (set_abla_tracker sink) |
| 41 | + |
| 42 | +#include <core/log.hpp> |
| 43 | + |
| 44 | +#include <cstdint> |
| 45 | + |
| 46 | +namespace bch { |
| 47 | +namespace coin { |
| 48 | + |
| 49 | +/// Owns the ABLA tracker + full_block feed for one embedded daemon instance and |
| 50 | +/// wires them to a live node + EmbeddedCoinNode. Single-threaded use from the |
| 51 | +/// daemon block-processing context (same contract as AblaTracker / feed). |
| 52 | +class AblaRuntime { |
| 53 | +public: |
| 54 | + /// Cold-start: anchor at the activation/floor State for `anchor_height`. |
| 55 | + /// Safe default when no BCHN-pinned anchor is available yet. |
| 56 | + AblaRuntime(bool is_testnet, uint32_t anchor_height, const HeaderChain& chain) |
| 57 | + : m_tracker(AblaTracker::floor_anchored(is_testnet, anchor_height)), |
| 58 | + m_feed(m_tracker, chain) {} |
| 59 | + |
| 60 | + /// BCHN-pinned anchor: start from a known-good {height, State} captured at |
| 61 | + /// daemon start. Lets the live budget track the real consensus limit from |
| 62 | + /// block one instead of climbing from the floor. |
| 63 | + AblaRuntime(bool is_testnet, uint32_t anchor_height, abla::State anchor_state, |
| 64 | + const HeaderChain& chain) |
| 65 | + : m_tracker(is_testnet, anchor_height, anchor_state), |
| 66 | + m_feed(m_tracker, chain) {} |
| 67 | + |
| 68 | + AblaRuntime(const AblaRuntime&) = delete; |
| 69 | + AblaRuntime& operator=(const AblaRuntime&) = delete; |
| 70 | + |
| 71 | + /// Close the loop: attach the feed to the node`s full_block event and hand |
| 72 | + /// the tracker to the EmbeddedCoinNode. Call once, after both exist. |
| 73 | + void wire(bch::interfaces::Node& node, EmbeddedCoinNode& embedded) { |
| 74 | + m_feed.attach(node); |
| 75 | + embedded.set_abla_tracker(&m_tracker); |
| 76 | + LOG_INFO << "[EMB-BCH] ABLA runtime wired: full_block -> feed -> tracker" |
| 77 | + << " -> template builder (anchor_height=" << m_tracker.cursor_height() |
| 78 | + << ", budget=" << m_tracker.budget_for_tip(m_tracker.cursor_height()) |
| 79 | + << ", " << (m_tracker.is_stale() ? "stale" : "current") << ")"; |
| 80 | + } |
| 81 | + |
| 82 | + /// Re-establish a known-good anchor after a gap/reorg or on a fresh BCHN |
| 83 | + /// pin. Thin passthrough to the tracker; the feed keeps its subscription. |
| 84 | + void reanchor(uint32_t height, abla::State state) { |
| 85 | + m_tracker.reanchor(height, state); |
| 86 | + LOG_INFO << "[EMB-BCH] ABLA runtime reanchored at height=" << height |
| 87 | + << " budget=" << m_tracker.budget_for_tip(height); |
| 88 | + } |
| 89 | + |
| 90 | + AblaTracker& tracker() { return m_tracker; } |
| 91 | + const AblaTracker& tracker() const { return m_tracker; } |
| 92 | + bool is_wired() const { return m_feed.is_attached(); } |
| 93 | + |
| 94 | +private: |
| 95 | + AblaTracker m_tracker; // declared first: outlives m_feed + the EmbeddedCoinNode pointer |
| 96 | + AblaBlockFeed m_feed; |
| 97 | +}; |
| 98 | + |
| 99 | +} // namespace coin |
| 100 | +} // namespace bch |
0 commit comments