diff --git a/src/impl/nmc/coin/aux_chain_embedded.hpp b/src/impl/nmc/coin/aux_chain_embedded.hpp index 0e50c9878..33a0cfe6d 100644 --- a/src/impl/nmc/coin/aux_chain_embedded.hpp +++ b/src/impl/nmc/coin/aux_chain_embedded.hpp @@ -151,13 +151,19 @@ class AuxChainEmbedded : public c2pool::merged::IAuxChainBackend } bool submit_aux_block(const uint256& block_hash, const std::string& auxpow_hex) override { - // Fallback (submitauxblock) path: embedded mode has no daemon to RPC, - // the frozen-block submit_block() + P2P relay is the primary route. + // PE broadcaster gate (integrator UID1610): the submitauxblock RPC route is + // external-namecoind-only and conditional - it is NOT a same-process fallback + // like BTC submitblock-to-bitcoind, so it does NOT satisfy the embedded leg. + // Embedded mode has no daemon to RPC, therefore this path cannot itself + // broadcast and MUST NOT claim success: the P2P relay via submit_block() is + // the one authoritative embedded route. Same never-silent-drop invariant as + // submit_block() - never claim a broadcast that did not occur (#162). LOG_WARNING << "[MM:" << m_config.symbol << "] Embedded: submit_aux_block called" - << " (fallback path) hash=" << block_hash.GetHex().substr(0, 16) << "..." + << " (RPC fallback) hash=" << block_hash.GetHex().substr(0, 16) << "..." << " auxpow=" << auxpow_hex.size() / 2 << " bytes" - << " - no daemon to submit to, relying on P2P relay"; - return true; + << " - no daemon to submit to; embedded RPC leg cannot broadcast," + << " returning false (P2P relay via submit_block is authoritative)"; + return false; } std::string get_block_hex(const std::string& /*block_hash*/) override { diff --git a/src/impl/nmc/test/nmc_template_builder_test.cpp b/src/impl/nmc/test/nmc_template_builder_test.cpp index 321d60c6a..7baddb696 100644 --- a/src/impl/nmc/test/nmc_template_builder_test.cpp +++ b/src/impl/nmc/test/nmc_template_builder_test.cpp @@ -361,15 +361,17 @@ TEST(NmcAuxChainEmbedded, SubmitBlockNeverSilentDropOnZeroPeers) EXPECT_EQ(backend.get_block_hex("any"), hex); // hex still cached } -// PE dual-path gate (integrator note #3): the FALLBACK leg. submit_block() above -// is the P2P-primary route; submit_aux_block() is the submitauxblock RPC fallback. -// In embedded mode there is no daemon to RPC, so the fallback acknowledges (the -// P2P relay leg is authoritative) WITHOUT claiming a daemon submission - and, -// crucially, unlike submit_block() it does NOT populate the block-hex cache. -// This pins the two legs as DISTINCT (P2P-primary cached vs RPC-fallback no-cache), -// the both-paths-fire gate proven at unit level ahead of the live .140 won-block -// soak (which is daemon-gated). A coin is not block-viable until both legs exist. -TEST(NmcAuxChainEmbedded, SubmitAuxBlockFallbackLegIsDistinctFromP2PRelay) +// PE broadcaster gate (integrator UID1610, supersedes note #3): the submitauxblock +// RPC leg. submit_block() above is the P2P-primary route. The submitauxblock RPC +// route is external-namecoind-only - it is NOT a same-process fallback like BTC +// submitblock-to-bitcoind, so it does NOT satisfy the embedded leg. In embedded +// mode there is no daemon to RPC, so this leg cannot itself broadcast and MUST +// surface false (never claim a broadcast that did not occur - same never-silent- +// drop invariant as submit_block). It also leaves the P2P block-hex cache untouched: +// the two legs are DISTINCT, not aliases. A won-block soak asserting green while +// this leg returned an unconditional true would be green-by-construction - the +// precise failure the broadcaster gate exists to catch. +TEST(NmcAuxChainEmbedded, SubmitAuxBlockRpcLegSurfacesFalseInEmbeddedMode) { HeaderChain chain(params_pinned()); Mempool pool; @@ -378,11 +380,11 @@ TEST(NmcAuxChainEmbedded, SubmitAuxBlockFallbackLegIsDistinctFromP2PRelay) uint256 aux_hash; aux_hash.SetNull(); const std::string auxpow_hex(120, static_cast(0x64)); - // Fallback leg acknowledges: embedded has no daemon, P2P relay is primary, - // so the caller is not hard-failed (the block already went out over P2P). - EXPECT_TRUE(backend.submit_aux_block(aux_hash, auxpow_hex)); - // ...but the RPC-fallback leg leaves the P2P submit_block() hex cache UNTOUCHED: - // the two broadcaster legs are independent, not aliases of one path. + // Embedded RPC-fallback leg has no daemon to submit to: it cannot broadcast, + // so it MUST NOT claim success. P2P relay via submit_block() is authoritative. + EXPECT_FALSE(backend.submit_aux_block(aux_hash, auxpow_hex)); + // ...and it leaves the P2P submit_block() hex cache UNTOUCHED: the two + // broadcaster legs are independent, not aliases of one path. EXPECT_EQ(backend.get_block_hex("any"), std::string("")); }