// Copyright (c) 2014-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include BOOST_FIXTURE_TEST_SUITE(validation_tests, TestingSetup) static void TestBlockSubsidyHalvings(const Consensus::Params& consensusParams) { int maxHalvings = 64; CAmount nInitialSubsidy = 50 * COIN; CAmount nPreviousSubsidy = nInitialSubsidy * 2; // for height == 0 BOOST_CHECK_EQUAL(nPreviousSubsidy, nInitialSubsidy * 2); for (int nHalvings = 0; nHalvings < maxHalvings; nHalvings++) { int nHeight = nHalvings * consensusParams.nSubsidyHalvingInterval; CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams); BOOST_CHECK(nSubsidy <= nInitialSubsidy); BOOST_CHECK_EQUAL(nSubsidy, nPreviousSubsidy / 2); nPreviousSubsidy = nSubsidy; } BOOST_CHECK_EQUAL(GetBlockSubsidy(maxHalvings * consensusParams.nSubsidyHalvingInterval, consensusParams), 0); } static void TestBlockSubsidyHalvings(int nSubsidyHalvingInterval) { Consensus::Params consensusParams; consensusParams.nSubsidyHalvingInterval = nSubsidyHalvingInterval; TestBlockSubsidyHalvings(consensusParams); } BOOST_AUTO_TEST_CASE(block_subsidy_test) { const auto chainParams = CreateChainParams(*m_node.args, ChainType::MAIN); TestBlockSubsidyHalvings(chainParams->GetConsensus()); // As in main TestBlockSubsidyHalvings(150); // As in regtest TestBlockSubsidyHalvings(1000); // Just another interval } BOOST_AUTO_TEST_CASE(subsidy_limit_test) { const auto chainParams = CreateChainParams(*m_node.args, ChainType::MAIN); CAmount nSum = 0; for (int nHeight = 0; nHeight < 14000000; nHeight += 1000) { CAmount nSubsidy = GetBlockSubsidy(nHeight, chainParams->GetConsensus()); BOOST_CHECK(nSubsidy <= 50 * COIN); nSum += nSubsidy * 1000; BOOST_CHECK(MoneyRange(nSum)); } BOOST_CHECK_EQUAL(nSum, CAmount{2099999997690000}); } BOOST_AUTO_TEST_CASE(signet_parse_tests) { ArgsManager signet_argsman; signet_argsman.ForceSetArg("-signetchallenge", "51"); // set challenge to OP_TRUE const auto signet_params = CreateChainParams(signet_argsman, ChainType::SIGNET); CBlock block; BOOST_CHECK(signet_params->GetConsensus().signet_challenge == std::vector{OP_TRUE}); CScript challenge{OP_TRUE}; // empty block is invalid BOOST_CHECK(!SignetTxs::Create(block, challenge)); BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus())); // no witness commitment CMutableTransaction cb; cb.vout.emplace_back(0, CScript{}); block.vtx.push_back(MakeTransactionRef(cb)); block.vtx.push_back(MakeTransactionRef(cb)); // Add dummy tx to exercise merkle root code BOOST_CHECK(!SignetTxs::Create(block, challenge)); BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus())); // no header is treated valid std::vector witness_commitment_section_141{0xaa, 0x21, 0xa9, 0xed}; for (int i = 0; i < 32; ++i) { witness_commitment_section_141.push_back(0xff); } cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141; block.vtx.at(0) = MakeTransactionRef(cb); BOOST_CHECK(SignetTxs::Create(block, challenge)); BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus())); // no data after header, valid std::vector witness_commitment_section_325{0xec, 0xc7, 0xda, 0xa2}; cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325; block.vtx.at(0) = MakeTransactionRef(cb); BOOST_CHECK(SignetTxs::Create(block, challenge)); BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus())); // Premature end of data, invalid witness_commitment_section_325.push_back(0x01); witness_commitment_section_325.push_back(0x51); cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325; block.vtx.at(0) = MakeTransactionRef(cb); BOOST_CHECK(!SignetTxs::Create(block, challenge)); BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus())); // has data, valid witness_commitment_section_325.push_back(0x00); cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325; block.vtx.at(0) = MakeTransactionRef(cb); BOOST_CHECK(SignetTxs::Create(block, challenge)); BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus())); // Extraneous data, invalid witness_commitment_section_325.push_back(0x00); cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325; block.vtx.at(0) = MakeTransactionRef(cb); BOOST_CHECK(!SignetTxs::Create(block, challenge)); BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus())); } //! Test retrieval of valid assumeutxo values. BOOST_AUTO_TEST_CASE(test_assumeutxo) { const auto params = CreateChainParams(*m_node.args, ChainType::REGTEST); // These heights don't have assumeutxo configurations associated, per the contents // of kernel/chainparams.cpp. std::vector bad_heights{0, 100, 111, 115, 209, 211}; for (auto empty : bad_heights) { const auto out = params->AssumeutxoForHeight(empty); BOOST_CHECK(!out); } const auto out110 = *params->AssumeutxoForHeight(110); BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "6657b736d4fe4db0cbc796789e812d5dba7f5c143764b1b6905612f1830609d1"); BOOST_CHECK_EQUAL(out110.nChainTx, 111U); const auto out110_2 = *params->AssumeutxoForBlockhash(uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c")); BOOST_CHECK_EQUAL(out110_2.hash_serialized.ToString(), "6657b736d4fe4db0cbc796789e812d5dba7f5c143764b1b6905612f1830609d1"); BOOST_CHECK_EQUAL(out110_2.nChainTx, 111U); } BOOST_AUTO_TEST_CASE(block_malleation) { // Test utilities that calls `IsBlockMutated` and then clears the validity // cache flags on `CBlock`. auto is_mutated = [](CBlock& block, bool check_witness_root) { bool mutated{IsBlockMutated(block, check_witness_root)}; block.fChecked = false; block.m_checked_witness_commitment = false; block.m_checked_merkle_root = false; return mutated; }; auto is_not_mutated = [&is_mutated](CBlock& block, bool check_witness_root) { return !is_mutated(block, check_witness_root); }; // Test utilities to create coinbase transactions and insert witness // commitments. // // Note: this will not include the witness stack by default to avoid // triggering the "no witnesses allowed for blocks that don't commit to // witnesses" rule when testing other malleation vectors. auto create_coinbase_tx = [](bool include_witness = false) { CMutableTransaction coinbase; coinbase.vin.resize(1); if (include_witness) { coinbase.vin[0].scriptWitness.stack.resize(1); coinbase.vin[0].scriptWitness.stack[0] = std::vector(32, 0x00); } coinbase.vout.resize(1); coinbase.vout[0].scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT); coinbase.vout[0].scriptPubKey[0] = OP_RETURN; coinbase.vout[0].scriptPubKey[1] = 0x24; coinbase.vout[0].scriptPubKey[2] = 0xaa; coinbase.vout[0].scriptPubKey[3] = 0x21; coinbase.vout[0].scriptPubKey[4] = 0xa9; coinbase.vout[0].scriptPubKey[5] = 0xed; auto tx = MakeTransactionRef(coinbase); assert(tx->IsCoinBase()); return tx; }; auto insert_witness_commitment = [](CBlock& block, uint256 commitment) { assert(!block.vtx.empty() && block.vtx[0]->IsCoinBase() && !block.vtx[0]->vout.empty()); CMutableTransaction mtx{*block.vtx[0]}; CHash256().Write(commitment).Write(std::vector(32, 0x00)).Finalize(commitment); memcpy(&mtx.vout[0].scriptPubKey[6], commitment.begin(), 32); block.vtx[0] = MakeTransactionRef(mtx); }; { CBlock block; // Empty block is expected to have merkle root of 0x0. BOOST_CHECK(block.vtx.empty()); block.hashMerkleRoot = uint256{1}; BOOST_CHECK(is_mutated(block, /*check_witness_root=*/false)); block.hashMerkleRoot = uint256{}; BOOST_CHECK(is_not_mutated(block, /*check_witness_root=*/false)); // Block with a single coinbase tx is mutated if the merkle root is not // equal to the coinbase tx's hash. block.vtx.push_back(create_coinbase_tx()); BOOST_CHECK(block.vtx[0]->GetHash() != block.hashMerkleRoot); BOOST_CHECK(is_mutated(block, /*check_witness_root=*/false)); block.hashMerkleRoot = block.vtx[0]->GetHash(); BOOST_CHECK(is_not_mutated(block, /*check_witness_root=*/false)); // Block with two transactions is mutated if the merkle root does not // match the double sha256 of the concatenation of the two transaction // hashes. block.vtx.push_back(MakeTransactionRef(CMutableTransaction{})); BOOST_CHECK(is_mutated(block, /*check_witness_root=*/false)); HashWriter hasher; hasher.write(block.vtx[0]->GetHash()); hasher.write(block.vtx[1]->GetHash()); block.hashMerkleRoot = hasher.GetHash(); BOOST_CHECK(is_not_mutated(block, /*check_witness_root=*/false)); // Block with two transactions is mutated if any node is duplicate. { block.vtx[1] = block.vtx[0]; HashWriter hasher; hasher.write(block.vtx[0]->GetHash()); hasher.write(block.vtx[1]->GetHash()); block.hashMerkleRoot = hasher.GetHash(); BOOST_CHECK(is_mutated(block, /*check_witness_root=*/false)); } // Blocks with 64-byte coinbase transactions are not considered mutated block.vtx.clear(); { CMutableTransaction mtx; mtx.vin.resize(1); mtx.vout.resize(1); mtx.vout[0].scriptPubKey.resize(4); block.vtx.push_back(MakeTransactionRef(mtx)); block.hashMerkleRoot = block.vtx.back()->GetHash(); assert(block.vtx.back()->IsCoinBase()); assert(GetSerializeSize(TX_NO_WITNESS(block.vtx.back())) == 64); } BOOST_CHECK(is_not_mutated(block, /*check_witness_root=*/false)); } { // Test merkle root malleation // Pseudo code to mine transactions tx{1,2,3}: // // ``` // loop { // tx1 = random_tx() // tx2 = random_tx() // tx3 = deserialize_tx(txid(tx1) || txid(tx2)); // if serialized_size_without_witness(tx3) == 64 { // print(hex(tx3)) // break // } // } // ``` // // The `random_tx` function used to mine the txs below simply created // empty transactions with a random version field. CMutableTransaction tx1; BOOST_CHECK(DecodeHexTx(tx1, "ff204bd0000000000000", /*try_no_witness=*/true, /*try_witness=*/false)); CMutableTransaction tx2; BOOST_CHECK(DecodeHexTx(tx2, "8ae53c92000000000000", /*try_no_witness=*/true, /*try_witness=*/false)); CMutableTransaction tx3; BOOST_CHECK(DecodeHexTx(tx3, "cdaf22d00002c6a7f848f8ae4d30054e61dcf3303d6fe01d282163341f06feecc10032b3160fcab87bdfe3ecfb769206ef2d991b92f8a268e423a6ef4d485f06", /*try_no_witness=*/true, /*try_witness=*/false)); { // Verify that double_sha256(txid1||txid2) == txid3 HashWriter hasher; hasher.write(tx1.GetHash()); hasher.write(tx2.GetHash()); assert(hasher.GetHash() == tx3.GetHash()); // Verify that tx3 is 64 bytes in size (without witness). assert(GetSerializeSize(TX_NO_WITNESS(tx3)) == 64); } CBlock block; block.vtx.push_back(MakeTransactionRef(tx1)); block.vtx.push_back(MakeTransactionRef(tx2)); uint256 merkle_root = block.hashMerkleRoot = BlockMerkleRoot(block); BOOST_CHECK(is_not_mutated(block, /*check_witness_root=*/false)); // Mutate the block by replacing the two transactions with one 64-byte // transaction that serializes into the concatenation of the txids of // the transactions in the unmutated block. block.vtx.clear(); block.vtx.push_back(MakeTransactionRef(tx3)); BOOST_CHECK(!block.vtx.back()->IsCoinBase()); BOOST_CHECK(BlockMerkleRoot(block) == merkle_root); BOOST_CHECK(is_mutated(block, /*check_witness_root=*/false)); } { CBlock block; block.vtx.push_back(create_coinbase_tx(/*include_witness=*/true)); { CMutableTransaction mtx; mtx.vin.resize(1); mtx.vin[0].scriptWitness.stack.resize(1); mtx.vin[0].scriptWitness.stack[0] = {0}; block.vtx.push_back(MakeTransactionRef(mtx)); } block.hashMerkleRoot = BlockMerkleRoot(block); // Block with witnesses is considered mutated if the witness commitment // is not validated. BOOST_CHECK(is_mutated(block, /*check_witness_root=*/false)); // Block with invalid witness commitment is considered mutated. BOOST_CHECK(is_mutated(block, /*check_witness_root=*/true)); // Block with valid commitment is not mutated { auto commitment{BlockWitnessMerkleRoot(block)}; insert_witness_commitment(block, commitment); block.hashMerkleRoot = BlockMerkleRoot(block); } BOOST_CHECK(is_not_mutated(block, /*check_witness_root=*/true)); // Malleating witnesses should be caught by `IsBlockMutated`. { CMutableTransaction mtx{*block.vtx[1]}; assert(!mtx.vin[0].scriptWitness.stack[0].empty()); ++mtx.vin[0].scriptWitness.stack[0][0]; block.vtx[1] = MakeTransactionRef(mtx); } // Without also updating the witness commitment, the merkle root should // not change when changing one of the witnesses. BOOST_CHECK(block.hashMerkleRoot == BlockMerkleRoot(block)); BOOST_CHECK(is_mutated(block, /*check_witness_root=*/true)); { auto commitment{BlockWitnessMerkleRoot(block)}; insert_witness_commitment(block, commitment); block.hashMerkleRoot = BlockMerkleRoot(block); } BOOST_CHECK(is_not_mutated(block, /*check_witness_root=*/true)); // Test malleating the coinbase witness reserved value { CMutableTransaction mtx{*block.vtx[0]}; mtx.vin[0].scriptWitness.stack.resize(0); block.vtx[0] = MakeTransactionRef(mtx); block.hashMerkleRoot = BlockMerkleRoot(block); } BOOST_CHECK(is_mutated(block, /*check_witness_root=*/true)); } } BOOST_AUTO_TEST_SUITE_END()