Skip to content

Commit 0bd2bd1

Browse files
committed
Merge bitcoin#30237: test: Add Compact Block Encoding test ReceiveWithExtraTransactions covering non-empty extra_txn
55eea00 test: Make blockencodings_tests deterministic (AngusP) 4c99301 test: Add ReceiveWithExtraTransactions Compact Block receive test. (AngusP) 4621e7c test: refactor: Rename extra_txn to const empty_extra_txn as it is empty in all test cases (AngusP) Pull request description: This test uses the `extra_txn` (`vExtraTxnForCompact`) vector of optional orphan/conflicted/etc. transactions to provide transactions to a PartiallyDownloadedBlock that are not otherwise present in the mempool, and check that they are used. This also covers a former nullptr deref bug that was fixed in bitcoin#29752 (bf031a5) where the `extra_txn` vec/circular-buffer was null-initialized and not yet filled when dereferenced in `PartiallyDownloadedBlock::InitData`. ACKs for top commit: marcofleon: Code review ACK 55eea00. I ran the `blockencodings` unit test and no issues with the new test case. dergoegge: Code review ACK 55eea00 glozow: ACK 55eea00 Tree-SHA512: d7909c212bb069e1f6184b26390a5000dcc5f2b18e49b86cceccb9f1ec4f874dd43bc9bc92abd4207c71dd78112ba58400042c230c42e93afe55ba51b943262c
2 parents 4c573e5 + 55eea00 commit 0bd2bd1

File tree

5 files changed

+91
-34
lines changed

5 files changed

+91
-34
lines changed

src/blockencodings.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
#include <unordered_map>
1919

20-
CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block) :
21-
nonce(GetRand<uint64_t>()),
20+
CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, const uint64_t nonce) :
21+
nonce(nonce),
2222
shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) {
2323
FillShortTxIDSelector();
2424
//TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase

src/blockencodings.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,15 @@ class CBlockHeaderAndShortTxIDs {
106106

107107
CBlockHeader header;
108108

109-
// Dummy for deserialization
109+
/**
110+
* Dummy for deserialization
111+
*/
110112
CBlockHeaderAndShortTxIDs() {}
111113

112-
CBlockHeaderAndShortTxIDs(const CBlock& block);
114+
/**
115+
* @param[in] nonce This should be randomly generated, and is used for the siphash secret key
116+
*/
117+
CBlockHeaderAndShortTxIDs(const CBlock& block, const uint64_t nonce);
113118

114119
uint64_t GetShortID(const Wtxid& wtxid) const;
115120

@@ -141,7 +146,7 @@ class PartiallyDownloadedBlock {
141146

142147
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
143148

144-
// extra_txn is a list of extra transactions to look at, in <witness hash, reference> form
149+
// extra_txn is a list of extra orphan/conflicted/etc transactions to look at
145150
ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<CTransactionRef>& extra_txn);
146151
bool IsTxAvailable(size_t index) const;
147152
ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing);

src/net_processing.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,7 +2124,7 @@ void PeerManagerImpl::BlockDisconnected(const std::shared_ptr<const CBlock> &blo
21242124
*/
21252125
void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock)
21262126
{
2127-
auto pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs>(*pblock);
2127+
auto pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs>(*pblock, GetRand<uint64_t>());
21282128

21292129
LOCK(cs_main);
21302130

@@ -2522,7 +2522,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
25222522
if (a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
25232523
MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, *a_recent_compact_block);
25242524
} else {
2525-
CBlockHeaderAndShortTxIDs cmpctblock{*pblock};
2525+
CBlockHeaderAndShortTxIDs cmpctblock{*pblock, GetRand<uint64_t>()};
25262526
MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, cmpctblock);
25272527
}
25282528
} else {
@@ -5984,7 +5984,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
59845984
CBlock block;
59855985
const bool ret{m_chainman.m_blockman.ReadBlockFromDisk(block, *pBestIndex)};
59865986
assert(ret);
5987-
CBlockHeaderAndShortTxIDs cmpctblock{block};
5987+
CBlockHeaderAndShortTxIDs cmpctblock{block, GetRand<uint64_t>()};
59885988
MakeAndPushMessage(*pto, NetMsgType::CMPCTBLOCK, cmpctblock);
59895989
}
59905990
state.pindexBestHeaderSent = pBestIndex;

src/test/blockencodings_tests.cpp

Lines changed: 77 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,36 @@
1414

1515
#include <boost/test/unit_test.hpp>
1616

17-
std::vector<CTransactionRef> extra_txn;
17+
const std::vector<CTransactionRef> empty_extra_txn;
1818

1919
BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup)
2020

21-
static CBlock BuildBlockTestCase() {
22-
CBlock block;
21+
static CMutableTransaction BuildTransactionTestCase() {
2322
CMutableTransaction tx;
2423
tx.vin.resize(1);
2524
tx.vin[0].scriptSig.resize(10);
2625
tx.vout.resize(1);
2726
tx.vout[0].nValue = 42;
27+
return tx;
28+
}
29+
30+
static CBlock BuildBlockTestCase(FastRandomContext& ctx) {
31+
CBlock block;
32+
CMutableTransaction tx = BuildTransactionTestCase();
2833

2934
block.vtx.resize(3);
3035
block.vtx[0] = MakeTransactionRef(tx);
3136
block.nVersion = 42;
32-
block.hashPrevBlock = InsecureRand256();
37+
block.hashPrevBlock = ctx.rand256();
3338
block.nBits = 0x207fffff;
3439

35-
tx.vin[0].prevout.hash = Txid::FromUint256(InsecureRand256());
40+
tx.vin[0].prevout.hash = Txid::FromUint256(ctx.rand256());
3641
tx.vin[0].prevout.n = 0;
3742
block.vtx[1] = MakeTransactionRef(tx);
3843

3944
tx.vin.resize(10);
4045
for (size_t i = 0; i < tx.vin.size(); i++) {
41-
tx.vin[i].prevout.hash = Txid::FromUint256(InsecureRand256());
46+
tx.vin[i].prevout.hash = Txid::FromUint256(ctx.rand256());
4247
tx.vin[i].prevout.n = 0;
4348
}
4449
block.vtx[2] = MakeTransactionRef(tx);
@@ -58,15 +63,16 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
5863
{
5964
CTxMemPool& pool = *Assert(m_node.mempool);
6065
TestMemPoolEntryHelper entry;
61-
CBlock block(BuildBlockTestCase());
66+
auto rand_ctx(FastRandomContext(uint256{42}));
67+
CBlock block(BuildBlockTestCase(rand_ctx));
6268

6369
LOCK2(cs_main, pool.cs);
6470
pool.addUnchecked(entry.FromTx(block.vtx[2]));
6571
BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
6672

6773
// Do a simple ShortTxIDs RT
6874
{
69-
CBlockHeaderAndShortTxIDs shortIDs{block};
75+
CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
7076

7177
DataStream stream{};
7278
stream << shortIDs;
@@ -75,7 +81,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
7581
stream >> shortIDs2;
7682

7783
PartiallyDownloadedBlock partialBlock(&pool);
78-
BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
84+
BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
7985
BOOST_CHECK( partialBlock.IsTxAvailable(0));
8086
BOOST_CHECK(!partialBlock.IsTxAvailable(1));
8187
BOOST_CHECK( partialBlock.IsTxAvailable(2));
@@ -123,8 +129,8 @@ class TestHeaderAndShortIDs {
123129
stream << orig;
124130
stream >> *this;
125131
}
126-
explicit TestHeaderAndShortIDs(const CBlock& block) :
127-
TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs{block}) {}
132+
explicit TestHeaderAndShortIDs(const CBlock& block, FastRandomContext& ctx) :
133+
TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs{block, ctx.rand64()}) {}
128134

129135
uint64_t GetShortID(const Wtxid& txhash) const {
130136
DataStream stream{};
@@ -141,7 +147,8 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
141147
{
142148
CTxMemPool& pool = *Assert(m_node.mempool);
143149
TestMemPoolEntryHelper entry;
144-
CBlock block(BuildBlockTestCase());
150+
auto rand_ctx(FastRandomContext(uint256{42}));
151+
CBlock block(BuildBlockTestCase(rand_ctx));
145152

146153
LOCK2(cs_main, pool.cs);
147154
pool.addUnchecked(entry.FromTx(block.vtx[2]));
@@ -151,7 +158,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
151158

152159
// Test with pre-forwarding tx 1, but not coinbase
153160
{
154-
TestHeaderAndShortIDs shortIDs(block);
161+
TestHeaderAndShortIDs shortIDs(block, rand_ctx);
155162
shortIDs.prefilledtxn.resize(1);
156163
shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
157164
shortIDs.shorttxids.resize(2);
@@ -165,7 +172,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
165172
stream >> shortIDs2;
166173

167174
PartiallyDownloadedBlock partialBlock(&pool);
168-
BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
175+
BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
169176
BOOST_CHECK(!partialBlock.IsTxAvailable(0));
170177
BOOST_CHECK( partialBlock.IsTxAvailable(1));
171178
BOOST_CHECK( partialBlock.IsTxAvailable(2));
@@ -211,7 +218,8 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
211218
{
212219
CTxMemPool& pool = *Assert(m_node.mempool);
213220
TestMemPoolEntryHelper entry;
214-
CBlock block(BuildBlockTestCase());
221+
auto rand_ctx(FastRandomContext(uint256{42}));
222+
CBlock block(BuildBlockTestCase(rand_ctx));
215223

216224
LOCK2(cs_main, pool.cs);
217225
pool.addUnchecked(entry.FromTx(block.vtx[1]));
@@ -221,7 +229,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
221229

222230
// Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
223231
{
224-
TestHeaderAndShortIDs shortIDs(block);
232+
TestHeaderAndShortIDs shortIDs(block, rand_ctx);
225233
shortIDs.prefilledtxn.resize(2);
226234
shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
227235
shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
@@ -235,7 +243,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
235243
stream >> shortIDs2;
236244

237245
PartiallyDownloadedBlock partialBlock(&pool);
238-
BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
246+
BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
239247
BOOST_CHECK( partialBlock.IsTxAvailable(0));
240248
BOOST_CHECK( partialBlock.IsTxAvailable(1));
241249
BOOST_CHECK( partialBlock.IsTxAvailable(2));
@@ -261,17 +269,14 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
261269
BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
262270
{
263271
CTxMemPool& pool = *Assert(m_node.mempool);
264-
CMutableTransaction coinbase;
265-
coinbase.vin.resize(1);
266-
coinbase.vin[0].scriptSig.resize(10);
267-
coinbase.vout.resize(1);
268-
coinbase.vout[0].nValue = 42;
272+
CMutableTransaction coinbase = BuildTransactionTestCase();
269273

270274
CBlock block;
275+
auto rand_ctx(FastRandomContext(uint256{42}));
271276
block.vtx.resize(1);
272277
block.vtx[0] = MakeTransactionRef(std::move(coinbase));
273278
block.nVersion = 42;
274-
block.hashPrevBlock = InsecureRand256();
279+
block.hashPrevBlock = rand_ctx.rand256();
275280
block.nBits = 0x207fffff;
276281

277282
bool mutated;
@@ -281,7 +286,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
281286

282287
// Test simple header round-trip with only coinbase
283288
{
284-
CBlockHeaderAndShortTxIDs shortIDs{block};
289+
CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
285290

286291
DataStream stream{};
287292
stream << shortIDs;
@@ -290,7 +295,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
290295
stream >> shortIDs2;
291296

292297
PartiallyDownloadedBlock partialBlock(&pool);
293-
BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
298+
BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
294299
BOOST_CHECK(partialBlock.IsTxAvailable(0));
295300

296301
CBlock block2;
@@ -302,6 +307,53 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
302307
}
303308
}
304309

310+
BOOST_AUTO_TEST_CASE(ReceiveWithExtraTransactions) {
311+
CTxMemPool& pool = *Assert(m_node.mempool);
312+
TestMemPoolEntryHelper entry;
313+
auto rand_ctx(FastRandomContext(uint256{42}));
314+
315+
CMutableTransaction mtx = BuildTransactionTestCase();
316+
mtx.vin[0].prevout.hash = Txid::FromUint256(rand_ctx.rand256());
317+
mtx.vin[0].prevout.n = 0;
318+
const CTransactionRef non_block_tx = MakeTransactionRef(std::move(mtx));
319+
320+
CBlock block(BuildBlockTestCase(rand_ctx));
321+
std::vector<CTransactionRef> extra_txn;
322+
extra_txn.resize(10);
323+
324+
LOCK2(cs_main, pool.cs);
325+
pool.addUnchecked(entry.FromTx(block.vtx[2]));
326+
BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
327+
// Ensure the non_block_tx is actually not in the block
328+
for (const auto &block_tx : block.vtx) {
329+
BOOST_CHECK_NE(block_tx->GetHash(), non_block_tx->GetHash());
330+
}
331+
// Ensure block.vtx[1] is not in pool
332+
BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()), nullptr);
333+
334+
{
335+
const CBlockHeaderAndShortTxIDs cmpctblock{block, rand_ctx.rand64()};
336+
PartiallyDownloadedBlock partial_block(&pool);
337+
PartiallyDownloadedBlock partial_block_with_extra(&pool);
338+
339+
BOOST_CHECK(partial_block.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
340+
BOOST_CHECK( partial_block.IsTxAvailable(0));
341+
BOOST_CHECK(!partial_block.IsTxAvailable(1));
342+
BOOST_CHECK( partial_block.IsTxAvailable(2));
343+
344+
// Add an unrelated tx to extra_txn:
345+
extra_txn[0] = non_block_tx;
346+
// and a tx from the block that's not in the mempool:
347+
extra_txn[1] = block.vtx[1];
348+
349+
BOOST_CHECK(partial_block_with_extra.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
350+
BOOST_CHECK(partial_block_with_extra.IsTxAvailable(0));
351+
// This transaction is now available via extra_txn:
352+
BOOST_CHECK(partial_block_with_extra.IsTxAvailable(1));
353+
BOOST_CHECK(partial_block_with_extra.IsTxAvailable(2));
354+
}
355+
}
356+
305357
BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
306358
BlockTransactionsRequest req1;
307359
req1.blockhash = InsecureRand256();

src/test/fuzz/partially_downloaded_block.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
5252
return;
5353
}
5454

55-
CBlockHeaderAndShortTxIDs cmpctblock{*block};
55+
CBlockHeaderAndShortTxIDs cmpctblock{*block, fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
5656

5757
bilingual_str error;
5858
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};

0 commit comments

Comments
 (0)