Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/impl/nmc/coin/header_chain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,32 @@ class HeaderChain {
header.m_bits);
}

/// P1e: verdict for the activation-height admission gate.
enum class AdmitResult {
ADMIT, // height / auxpow-presence consistent (proof checked separately)
REJECT_PREMATURE_AUXPOW, // header carries an AuxPow below the activation height
REJECT_MISSING_AUXPOW, // no AuxPow at/after activation height (mainnet requirement)
REJECT_UNPINNED, // activation height still the -1 sentinel - refuse to judge
};

/// P1e: the activation-height GATE, factored out for unit-testing
/// independent of the (still-deferred) storage path - the same pattern as
/// verify_auxpow_header(). Decides admissibility from the connected `height`
/// and whether the header carries an AuxPow, BEFORE the four-leg proof is
/// walked: below activation an AuxPow is premature, at/after activation
/// (mainnet) one is mandatory. While auxpow_activation_height is the
/// unpinned -1 sentinel it refuses to judge (REJECT_UNPINNED) so the P1 leaf
/// never renders an activation verdict off a placeholder constant.
AdmitResult check_activation_gate(int32_t height, bool has_auxpow) const {
if (m_params.auxpow_activation_height < 0)
return AdmitResult::REJECT_UNPINNED; // TO-CONFIRM unpinned
if (!m_params.is_auxpow_active(height))
return has_auxpow ? AdmitResult::REJECT_PREMATURE_AUXPOW
: AdmitResult::ADMIT;
return has_auxpow ? AdmitResult::ADMIT
: AdmitResult::REJECT_MISSING_AUXPOW;
}

/// Add a header that carries a merge-mining AuxPow proof.
/// P1d: the AuxPow VERIFICATION GATE is now wired - check_proof() must
/// return VALID or the header is rejected, so the (future) accept path can
Expand Down
61 changes: 61 additions & 0 deletions src/impl/nmc/test/auxpow_merkle_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,4 +635,65 @@ TEST(NmcP1dGate, MissingAuxBitsKeepsProofIncompleteAndRejected)
EXPECT_FALSE(chain_.add_auxpow_header(h, ap));
}

// ---------------------------------------------------------------------------
// P1e: activation-height admission gate KATs (NmcP1eActivationGate).
// Exercise HeaderChain::check_activation_gate() - the height-derived MM
// activation gate, factored like the P1d proof gate so it is testable without
// the deferred storage path. The production activation height stays the -1
// sentinel; these fixtures pin a TEST-only height (19200, the historically
// cited NMC mainnet value) purely to drive the gate - no consensus promotion.
// ---------------------------------------------------------------------------
static NMCChainParams params_activation(int32_t act_height)
{
NMCChainParams p = NMCChainParams::mainnet();
p.aux_chain_id = 1;
p.auxpow_activation_height = act_height; // TEST-only pin; production stays -1
return p;
}

TEST(NmcP1eActivationGate, UnpinnedActivationRefusesToJudge)
{
// mainnet() leaves auxpow_activation_height at -1: the gate must refuse
// rather than guess, regardless of whether the header carries an AuxPow.
HeaderChain chain_(params_chain_id_1());
EXPECT_EQ(chain_.check_activation_gate(50000, /*has_auxpow=*/true),
HeaderChain::AdmitResult::REJECT_UNPINNED);
EXPECT_EQ(chain_.check_activation_gate(50000, /*has_auxpow=*/false),
HeaderChain::AdmitResult::REJECT_UNPINNED);
}

TEST(NmcP1eActivationGate, AuxPowBelowActivationIsPremature)
{
HeaderChain chain_(params_activation(19200));
EXPECT_EQ(chain_.check_activation_gate(19199, /*has_auxpow=*/true),
HeaderChain::AdmitResult::REJECT_PREMATURE_AUXPOW);
// a plain (non-MM) header below activation is admissible.
EXPECT_EQ(chain_.check_activation_gate(19199, /*has_auxpow=*/false),
HeaderChain::AdmitResult::ADMIT);
}

TEST(NmcP1eActivationGate, AuxPowRequiredAtAndAfterActivation)
{
HeaderChain chain_(params_activation(19200));
// exactly at the activation height the AuxPow becomes mandatory.
EXPECT_EQ(chain_.check_activation_gate(19200, /*has_auxpow=*/false),
HeaderChain::AdmitResult::REJECT_MISSING_AUXPOW);
EXPECT_EQ(chain_.check_activation_gate(19200, /*has_auxpow=*/true),
HeaderChain::AdmitResult::ADMIT);
// and well past it.
EXPECT_EQ(chain_.check_activation_gate(250000, /*has_auxpow=*/true),
HeaderChain::AdmitResult::ADMIT);
}

TEST(NmcP1eActivationGate, ActivationBoundaryIsInclusive)
{
HeaderChain chain_(params_activation(19200));
// height == activation-1 is still pre-activation (MM not yet required);
// height == activation flips MM to mandatory.
EXPECT_EQ(chain_.check_activation_gate(19199, /*has_auxpow=*/false),
HeaderChain::AdmitResult::ADMIT);
EXPECT_EQ(chain_.check_activation_gate(19200, /*has_auxpow=*/false),
HeaderChain::AdmitResult::REJECT_MISSING_AUXPOW);
}

} // namespace