aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAngusP <angus@toaster.cc>2024-06-04 19:27:30 +0100
committerAngusP <angus@toaster.cc>2024-06-06 13:08:17 +0100
commit4c99301220ab44e98d0d0e1cc8d774d96a25b7aa (patch)
treedb805a0ca489918fee31fabe2a9088d74cbd4f5b
parent4621e7cc8f8e2b71393a2b30d5dbe56165bfb854 (diff)
downloadbitcoin-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.h2
-rw-r--r--src/test/blockencodings_tests.cpp60
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();