diff options
author | AngusP <angus@toaster.cc> | 2024-06-04 19:27:30 +0100 |
---|---|---|
committer | AngusP <angus@toaster.cc> | 2024-06-06 13:08:17 +0100 |
commit | 4c99301220ab44e98d0d0e1cc8d774d96a25b7aa (patch) | |
tree | db805a0ca489918fee31fabe2a9088d74cbd4f5b | |
parent | 4621e7cc8f8e2b71393a2b30d5dbe56165bfb854 (diff) | |
download | bitcoin-4c99301220ab44e98d0d0e1cc8d774d96a25b7aa.tar.xz |
test: Add ReceiveWithExtraTransactions Compact Block receive test.
This new test uses the `vExtraTxnForCompact` (`extra_txn`) vector of
optional orphan/conflicted/etc. transactions to provide a transaction
in a compact block that was not otherwise present in our mempool.
This also covers an improbable nullptr deref bug addressed in
bf031a517c79cec5b43420bcd40291ab0e9f68a8 (#29752) where the
`extra_txn` vec/circular-buffer was sometimes null-initialized and
not yet filled when dereferenced in `PartiallyDownloadedBlock::InitData`.
-rw-r--r-- | src/blockencodings.h | 2 | ||||
-rw-r--r-- | src/test/blockencodings_tests.cpp | 60 |
2 files changed, 54 insertions, 8 deletions
diff --git a/src/blockencodings.h b/src/blockencodings.h index 2b1fabadd6..01594db527 100644 --- a/src/blockencodings.h +++ b/src/blockencodings.h @@ -141,7 +141,7 @@ public: explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {} - // extra_txn is a list of extra transactions to look at, in <witness hash, reference> form + // extra_txn is a list of extra orphan/conflicted/etc transactions to look at ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<CTransactionRef>& extra_txn); bool IsTxAvailable(size_t index) const; ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing); diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index 2388fed881..ff854c16eb 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -18,13 +18,18 @@ const std::vector<CTransactionRef> empty_extra_txn; BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup) -static CBlock BuildBlockTestCase() { - CBlock block; +static CMutableTransaction BuildTransactionTestCase() { CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].scriptSig.resize(10); tx.vout.resize(1); tx.vout[0].nValue = 42; + return tx; +} + +static CBlock BuildBlockTestCase() { + CBlock block; + CMutableTransaction tx = BuildTransactionTestCase(); block.vtx.resize(3); block.vtx[0] = MakeTransactionRef(tx); @@ -261,11 +266,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) { CTxMemPool& pool = *Assert(m_node.mempool); - CMutableTransaction coinbase; - coinbase.vin.resize(1); - coinbase.vin[0].scriptSig.resize(10); - coinbase.vout.resize(1); - coinbase.vout[0].nValue = 42; + CMutableTransaction coinbase = BuildTransactionTestCase(); CBlock block; block.vtx.resize(1); @@ -302,6 +303,51 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) } } +BOOST_AUTO_TEST_CASE(ReceiveWithExtraTransactions) { + CTxMemPool& pool = *Assert(m_node.mempool); + TestMemPoolEntryHelper entry; + const CBlock block(BuildBlockTestCase()); + std::vector<CTransactionRef> extra_txn; + extra_txn.resize(10); + + CMutableTransaction mtx = BuildTransactionTestCase(); + mtx.vin[0].prevout.hash = Txid::FromUint256(InsecureRand256()); + mtx.vin[0].prevout.n = 0; + const CTransactionRef non_block_tx = MakeTransactionRef(std::move(mtx)); + + LOCK2(cs_main, pool.cs); + pool.addUnchecked(entry.FromTx(block.vtx[2])); + BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0); + // Ensure the non_block_tx is actually not in the block + for (const auto &block_tx : block.vtx) { + BOOST_CHECK_NE(block_tx->GetHash(), non_block_tx->GetHash()); + } + // Ensure block.vtx[1] is not in pool + BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()), nullptr); + + { + const CBlockHeaderAndShortTxIDs cmpctblock{block}; + PartiallyDownloadedBlock partial_block(&pool); + PartiallyDownloadedBlock partial_block_with_extra(&pool); + + BOOST_CHECK(partial_block.InitData(cmpctblock, extra_txn) == READ_STATUS_OK); + BOOST_CHECK( partial_block.IsTxAvailable(0)); + BOOST_CHECK(!partial_block.IsTxAvailable(1)); + BOOST_CHECK( partial_block.IsTxAvailable(2)); + + // Add an unrelated tx to extra_txn: + extra_txn[0] = non_block_tx; + // and a tx from the block that's not in the mempool: + extra_txn[1] = block.vtx[1]; + + BOOST_CHECK(partial_block_with_extra.InitData(cmpctblock, extra_txn) == READ_STATUS_OK); + BOOST_CHECK(partial_block_with_extra.IsTxAvailable(0)); + // This transaction is now available via extra_txn: + BOOST_CHECK(partial_block_with_extra.IsTxAvailable(1)); + BOOST_CHECK(partial_block_with_extra.IsTxAvailable(2)); + } +} + BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) { BlockTransactionsRequest req1; req1.blockhash = InsecureRand256(); |