diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp --- a/src/avalanche/peermanager.cpp +++ b/src/avalanche/peermanager.cpp @@ -165,7 +165,8 @@ static bool isOrphanState(const ProofValidationState &state) { return state.GetResult() == ProofValidationResult::MISSING_UTXO || - state.GetResult() == ProofValidationResult::HEIGHT_MISMATCH; + state.GetResult() == ProofValidationResult::HEIGHT_MISMATCH || + state.GetResult() == ProofValidationResult::IMMATURE_UTXO; } bool PeerManager::updateNextPossibleConflictTime( diff --git a/src/avalanche/proof.h b/src/avalanche/proof.h --- a/src/avalanche/proof.h +++ b/src/avalanche/proof.h @@ -32,6 +32,13 @@ */ static constexpr bool AVALANCHE_DEFAULT_LEGACY_PROOF = true; +/** + * Minimum number of confirmations before a utxo is mature enough for being + * included into a proof. + * FIXME: Set to sane default after the tests have been updated. + */ +static constexpr int AVALANCHE_DEFAULT_UTXO_CONFIRMATIONS = 0; + namespace avalanche { /** Minimum amount per utxo */ diff --git a/src/avalanche/proof.cpp b/src/avalanche/proof.cpp --- a/src/avalanche/proof.cpp +++ b/src/avalanche/proof.cpp @@ -13,6 +13,7 @@ #include #include #include +#include // For g_chainman #include @@ -185,10 +186,22 @@ return false; } + int64_t activeHeight = WITH_LOCK(cs_main, return g_chainman.ActiveHeight()); + int64_t utxoMinConfirmations = gArgs.GetArg( + "-avaproofutxoconfirmations", AVALANCHE_DEFAULT_UTXO_CONFIRMATIONS); + for (const SignedStake &ss : stakes) { const Stake &s = ss.getStake(); const COutPoint &utxo = s.getUTXO(); + if ((s.getHeight() + utxoMinConfirmations) > activeHeight) { + return state.Invalid( + ProofValidationResult::IMMATURE_UTXO, "immature-utxo", + strprintf("TxId: %s, block height: %d, chaintip height: %d", + s.getUTXO().GetTxId().ToString(), s.getHeight(), + activeHeight)); + } + Coin coin; if (!view.GetCoin(utxo, coin)) { // The coins are not in the UTXO set. diff --git a/src/avalanche/test/init_tests.cpp b/src/avalanche/test/init_tests.cpp --- a/src/avalanche/test/init_tests.cpp +++ b/src/avalanche/test/init_tests.cpp @@ -39,6 +39,7 @@ BOOST_CHECK_EQUAL(args.GetBoolArg("-enableavalanche", false), true); BOOST_CHECK_EQUAL(args.GetBoolArg("-legacyavaproof", true), false); + BOOST_CHECK_EQUAL(args.GetArg("-avaproofutxoconfirmations", 42), 2016); BOOST_CHECK_EQUAL( args.GetBoolArg("-enableavalanchepeerdiscovery", false), true); BOOST_CHECK_EQUAL( @@ -60,6 +61,8 @@ BOOST_CHECK_EQUAL(args.GetBoolArg("-enableavalanche", true), false); BOOST_CHECK_EQUAL(args.GetBoolArg("-legacyavaproof", false), AVALANCHE_DEFAULT_LEGACY_PROOF); + BOOST_CHECK_EQUAL(args.GetArg("-avaproofutxoconfirmations", 42), + AVALANCHE_DEFAULT_UTXO_CONFIRMATIONS); BOOST_CHECK_EQUAL( args.GetBoolArg("-enableavalanchepeerdiscovery", true), false); BOOST_CHECK_EQUAL( @@ -79,6 +82,7 @@ ArgsManager args; args.ForceSetArg("-avalanche", "1"); args.ForceSetArg("-legacyavaproof", "1"); + args.ForceSetArg("-avaproofutxoconfirmations", "1008"); args.ForceSetArg("-enableavalancheproofreplacement", "0"); args.ForceSetArg("-automaticunparking", "1"); args.ForceSetArg("-avaminquorumstake", FormatMoney(123 * COIN)); @@ -87,6 +91,7 @@ BOOST_CHECK_EQUAL(args.GetBoolArg("-enableavalanche", false), true); BOOST_CHECK_EQUAL(args.GetBoolArg("-legacyavaproof", false), true); + BOOST_CHECK_EQUAL(args.GetArg("-avaproofutxoconfirmations", 42), 1008); BOOST_CHECK_EQUAL( args.GetBoolArg("-enableavalanchepeerdiscovery", false), true); BOOST_CHECK_EQUAL( diff --git a/src/avalanche/test/peermanager_tests.cpp b/src/avalanche/test/peermanager_tests.cpp --- a/src/avalanche/test/peermanager_tests.cpp +++ b/src/avalanche/test/peermanager_tests.cpp @@ -117,7 +117,7 @@ } // namespace avalanche namespace { -struct NoCoolDownFixture : public TestingSetup { +struct NoCoolDownFixture : public TestChain100Setup { NoCoolDownFixture() { gArgs.ForceSetArg("-avalancheconflictingproofcooldown", "0"); } @@ -127,7 +127,7 @@ }; } // namespace -BOOST_FIXTURE_TEST_SUITE(peermanager_tests, TestingSetup) +BOOST_FIXTURE_TEST_SUITE(peermanager_tests, TestChain100Setup) BOOST_AUTO_TEST_CASE(select_peer_linear) { // No peers. @@ -703,8 +703,8 @@ COutPoint outpoint3 = COutPoint(TxId(GetRandHash()), 0); const Amount v = 5 * COIN; - const int height = 1234; - const int wrongHeight = 12345; + const int height = 100; + const int wrongHeight = 99; const auto makeProof = [&](const COutPoint &outpoint, const int h) { return buildProofWithOutpoints(key, {outpoint}, v, key, 0, h); diff --git a/src/avalanche/test/proof_tests.cpp b/src/avalanche/test/proof_tests.cpp --- a/src/avalanche/test/proof_tests.cpp +++ b/src/avalanche/test/proof_tests.cpp @@ -18,7 +18,7 @@ using namespace avalanche; -BOOST_FIXTURE_TEST_SUITE(proof_tests, TestingSetup) +BOOST_FIXTURE_TEST_SUITE(proof_tests, TestChain100Setup) BOOST_AUTO_TEST_CASE(proof_random) { for (int i = 0; i < 1000; i++) { diff --git a/src/avalanche/test/proofpool_tests.cpp b/src/avalanche/test/proofpool_tests.cpp --- a/src/avalanche/test/proofpool_tests.cpp +++ b/src/avalanche/test/proofpool_tests.cpp @@ -18,7 +18,7 @@ using namespace avalanche; -BOOST_FIXTURE_TEST_SUITE(proofpool_tests, TestingSetup) +BOOST_FIXTURE_TEST_SUITE(proofpool_tests, TestChain100Setup) BOOST_AUTO_TEST_CASE(add_remove_proof_no_conflict) { ProofPool testPool; diff --git a/src/avalanche/test/util.cpp b/src/avalanche/test/util.cpp --- a/src/avalanche/test/util.cpp +++ b/src/avalanche/test/util.cpp @@ -22,7 +22,7 @@ const COutPoint o(TxId(GetRandHash()), 0); const Amount v = (int64_t(score) * COIN) / 100; - const int height = 1234; + const int height = 100; const bool is_coinbase = false; { diff --git a/src/avalanche/validation.h b/src/avalanche/validation.h --- a/src/avalanche/validation.h +++ b/src/avalanche/validation.h @@ -28,6 +28,7 @@ NON_STANDARD_DESTINATION, DESTINATION_NOT_SUPPORTED, DESTINATION_MISMATCH, + IMMATURE_UTXO, }; class ProofValidationState : public ValidationState {}; diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1378,6 +1378,12 @@ strprintf("Use the legacy avalanche proof format (default: %u)", AVALANCHE_DEFAULT_LEGACY_PROOF), ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE); + argsman.AddArg( + "-avaproofutxoconfirmations", + strprintf("Minimum number of confirmations before a utxo is mature" + " enough for being included into a proof (default: %s)", + AVALANCHE_DEFAULT_UTXO_CONFIRMATIONS), + ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE); argsman.AddArg("-avamasterkey", "Master key associated with the proof. If a proof is " "required, this is mandatory.", @@ -1656,6 +1662,10 @@ args.SoftSetBoolArg("-legacyavaproof", fAvalanche ? false : AVALANCHE_DEFAULT_LEGACY_PROOF); + args.SoftSetArg("-avaproofutxoconfirmations", + fAvalanche + ? "2016" + : ToString(AVALANCHE_DEFAULT_UTXO_CONFIRMATIONS)); args.SoftSetBoolArg("-enableavalanchepeerdiscovery", fAvalanche); args.SoftSetBoolArg("-enableavalancheproofreplacement", fAvalanche); args.SoftSetBoolArg("-automaticunparking", !fAvalanche);