@@ -361,5 +361,30 @@ TEST(NmcAuxChainEmbedded, SubmitBlockNeverSilentDropOnZeroPeers)
361361 EXPECT_EQ (backend.get_block_hex (" any" ), hex); // hex still cached
362362}
363363
364+ // PE dual-path gate (integrator note #3): the FALLBACK leg. submit_block() above
365+ // is the P2P-primary route; submit_aux_block() is the submitauxblock RPC fallback.
366+ // In embedded mode there is no daemon to RPC, so the fallback acknowledges (the
367+ // P2P relay leg is authoritative) WITHOUT claiming a daemon submission - and,
368+ // crucially, unlike submit_block() it does NOT populate the block-hex cache.
369+ // This pins the two legs as DISTINCT (P2P-primary cached vs RPC-fallback no-cache),
370+ // the both-paths-fire gate proven at unit level ahead of the live .140 won-block
371+ // soak (which is daemon-gated). A coin is not block-viable until both legs exist.
372+ TEST (NmcAuxChainEmbedded, SubmitAuxBlockFallbackLegIsDistinctFromP2PRelay)
373+ {
374+ HeaderChain chain (params_pinned ());
375+ Mempool pool;
376+ AuxChainEmbedded backend (chain, pool, params_pinned (), nmc_aux_config ());
377+
378+ uint256 aux_hash; aux_hash.SetNull ();
379+ const std::string auxpow_hex (120 , static_cast <char >(0x64 ));
380+
381+ // Fallback leg acknowledges: embedded has no daemon, P2P relay is primary,
382+ // so the caller is not hard-failed (the block already went out over P2P).
383+ EXPECT_TRUE (backend.submit_aux_block (aux_hash, auxpow_hex));
384+ // ...but the RPC-fallback leg leaves the P2P submit_block() hex cache UNTOUCHED:
385+ // the two broadcaster legs are independent, not aliases of one path.
386+ EXPECT_EQ (backend.get_block_hex (" any" ), std::string (" " ));
387+ }
388+
364389
365390} // namespace
0 commit comments