Branch data Line data Source code
# 1 : : // Copyright (c) 2011-2020 The Bitcoin Core developers
# 2 : : // Distributed under the MIT software license, see the accompanying
# 3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
# 4 : :
# 5 : : #include <blockencodings.h>
# 6 : : #include <chainparams.h>
# 7 : : #include <consensus/merkle.h>
# 8 : : #include <pow.h>
# 9 : : #include <streams.h>
# 10 : :
# 11 : : #include <test/util/setup_common.h>
# 12 : :
# 13 : : #include <boost/test/unit_test.hpp>
# 14 : :
# 15 : : std::vector<std::pair<uint256, CTransactionRef>> extra_txn;
# 16 : :
# 17 : : BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup)
# 18 : :
# 19 : 6 : static CBlock BuildBlockTestCase() {
# 20 : 6 : CBlock block;
# 21 : 6 : CMutableTransaction tx;
# 22 : 6 : tx.vin.resize(1);
# 23 : 6 : tx.vin[0].scriptSig.resize(10);
# 24 : 6 : tx.vout.resize(1);
# 25 : 6 : tx.vout[0].nValue = 42;
# 26 : :
# 27 : 6 : block.vtx.resize(3);
# 28 : 6 : block.vtx[0] = MakeTransactionRef(tx);
# 29 : 6 : block.nVersion = 42;
# 30 : 6 : block.hashPrevBlock = InsecureRand256();
# 31 : 6 : block.nBits = 0x207fffff;
# 32 : :
# 33 : 6 : tx.vin[0].prevout.hash = InsecureRand256();
# 34 : 6 : tx.vin[0].prevout.n = 0;
# 35 : 6 : block.vtx[1] = MakeTransactionRef(tx);
# 36 : :
# 37 : 6 : tx.vin.resize(10);
# 38 [ + + ]: 66 : for (size_t i = 0; i < tx.vin.size(); i++) {
# 39 : 60 : tx.vin[i].prevout.hash = InsecureRand256();
# 40 : 60 : tx.vin[i].prevout.n = 0;
# 41 : 60 : }
# 42 : 6 : block.vtx[2] = MakeTransactionRef(tx);
# 43 : :
# 44 : 6 : bool mutated;
# 45 : 6 : block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
# 46 : 6 : assert(!mutated);
# 47 [ + + ]: 12 : while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
# 48 : 6 : return block;
# 49 : 6 : }
# 50 : :
# 51 : : // Number of shared use_counts we expect for a tx we haven't touched
# 52 : : // (block + mempool + our copy from the GetSharedTx call)
# 53 : : constexpr long SHARED_TX_OFFSET{3};
# 54 : :
# 55 : : BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
# 56 : 2 : {
# 57 : 2 : CTxMemPool pool;
# 58 : 2 : TestMemPoolEntryHelper entry;
# 59 : 2 : CBlock block(BuildBlockTestCase());
# 60 : :
# 61 : 2 : LOCK2(cs_main, pool.cs);
# 62 : 2 : pool.addUnchecked(entry.FromTx(block.vtx[2]));
# 63 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
# 64 : :
# 65 : : // Do a simple ShortTxIDs RT
# 66 : 2 : {
# 67 : 2 : CBlockHeaderAndShortTxIDs shortIDs(block, true);
# 68 : :
# 69 : 2 : CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
# 70 : 2 : stream << shortIDs;
# 71 : :
# 72 : 2 : CBlockHeaderAndShortTxIDs shortIDs2;
# 73 : 2 : stream >> shortIDs2;
# 74 : :
# 75 : 2 : PartiallyDownloadedBlock partialBlock(&pool);
# 76 : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
# 77 : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(0));
# 78 : 2 : BOOST_CHECK(!partialBlock.IsTxAvailable(1));
# 79 : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(2));
# 80 : :
# 81 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
# 82 : :
# 83 : 2 : size_t poolSize = pool.size();
# 84 : 2 : pool.removeRecursive(*block.vtx[2], MemPoolRemovalReason::REPLACED);
# 85 : 2 : BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);
# 86 : :
# 87 : 2 : CBlock block2;
# 88 : 2 : {
# 89 : 2 : PartiallyDownloadedBlock tmp = partialBlock;
# 90 : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
# 91 : 2 : partialBlock = tmp;
# 92 : 2 : }
# 93 : :
# 94 : : // Wrong transaction
# 95 : 2 : {
# 96 : 2 : PartiallyDownloadedBlock tmp = partialBlock;
# 97 : 2 : partialBlock.FillBlock(block2, {block.vtx[2]}); // Current implementation doesn't check txn here, but don't require that
# 98 : 2 : partialBlock = tmp;
# 99 : 2 : }
# 100 : 2 : bool mutated;
# 101 : 2 : BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
# 102 : :
# 103 : 2 : CBlock block3;
# 104 : 2 : BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}) == READ_STATUS_OK);
# 105 : 2 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
# 106 : 2 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
# 107 : 2 : BOOST_CHECK(!mutated);
# 108 : 2 : }
# 109 : 2 : }
# 110 : :
# 111 : : class TestHeaderAndShortIDs {
# 112 : : // Utility to encode custom CBlockHeaderAndShortTxIDs
# 113 : : public:
# 114 : : CBlockHeader header;
# 115 : : uint64_t nonce;
# 116 : : std::vector<uint64_t> shorttxids;
# 117 : : std::vector<PrefilledTransaction> prefilledtxn;
# 118 : :
# 119 : 4 : explicit TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs& orig) {
# 120 : 4 : CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
# 121 : 4 : stream << orig;
# 122 : 4 : stream >> *this;
# 123 : 4 : }
# 124 : : explicit TestHeaderAndShortIDs(const CBlock& block) :
# 125 : 4 : TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block, true)) {}
# 126 : :
# 127 : 6 : uint64_t GetShortID(const uint256& txhash) const {
# 128 : 6 : CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
# 129 : 6 : stream << *this;
# 130 : 6 : CBlockHeaderAndShortTxIDs base;
# 131 : 6 : stream >> base;
# 132 : 6 : return base.GetShortID(txhash);
# 133 : 6 : }
# 134 : :
# 135 : 14 : SERIALIZE_METHODS(TestHeaderAndShortIDs, obj) { READWRITE(obj.header, obj.nonce, Using<VectorFormatter<CustomUintFormatter<CBlockHeaderAndShortTxIDs::SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn); }
# 136 : : };
# 137 : :
# 138 : : BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
# 139 : 2 : {
# 140 : 2 : CTxMemPool pool;
# 141 : 2 : TestMemPoolEntryHelper entry;
# 142 : 2 : CBlock block(BuildBlockTestCase());
# 143 : :
# 144 : 2 : LOCK2(cs_main, pool.cs);
# 145 : 2 : pool.addUnchecked(entry.FromTx(block.vtx[2]));
# 146 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
# 147 : :
# 148 : 2 : uint256 txhash;
# 149 : :
# 150 : : // Test with pre-forwarding tx 1, but not coinbase
# 151 : 2 : {
# 152 : 2 : TestHeaderAndShortIDs shortIDs(block);
# 153 : 2 : shortIDs.prefilledtxn.resize(1);
# 154 : 2 : shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
# 155 : 2 : shortIDs.shorttxids.resize(2);
# 156 : 2 : shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetHash());
# 157 : 2 : shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetHash());
# 158 : :
# 159 : 2 : CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
# 160 : 2 : stream << shortIDs;
# 161 : :
# 162 : 2 : CBlockHeaderAndShortTxIDs shortIDs2;
# 163 : 2 : stream >> shortIDs2;
# 164 : :
# 165 : 2 : PartiallyDownloadedBlock partialBlock(&pool);
# 166 : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
# 167 : 2 : BOOST_CHECK(!partialBlock.IsTxAvailable(0));
# 168 : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(1));
# 169 : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(2));
# 170 : :
# 171 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock
# 172 : :
# 173 : 2 : CBlock block2;
# 174 : 2 : {
# 175 : 2 : PartiallyDownloadedBlock tmp = partialBlock;
# 176 : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
# 177 : 2 : partialBlock = tmp;
# 178 : 2 : }
# 179 : :
# 180 : : // Wrong transaction
# 181 : 2 : {
# 182 : 2 : PartiallyDownloadedBlock tmp = partialBlock;
# 183 : 2 : partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that
# 184 : 2 : partialBlock = tmp;
# 185 : 2 : }
# 186 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
# 187 : 2 : bool mutated;
# 188 : 2 : BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
# 189 : :
# 190 : 2 : CBlock block3;
# 191 : 2 : PartiallyDownloadedBlock partialBlockCopy = partialBlock;
# 192 : 2 : BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}) == READ_STATUS_OK);
# 193 : 2 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
# 194 : 2 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
# 195 : 2 : BOOST_CHECK(!mutated);
# 196 : :
# 197 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 3); // +2 because of partialBlock and block2 and block3
# 198 : :
# 199 : 2 : txhash = block.vtx[2]->GetHash();
# 200 : 2 : block.vtx.clear();
# 201 : 2 : block2.vtx.clear();
# 202 : 2 : block3.vtx.clear();
# 203 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
# 204 : 2 : }
# 205 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
# 206 : 2 : }
# 207 : :
# 208 : : BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
# 209 : 2 : {
# 210 : 2 : CTxMemPool pool;
# 211 : 2 : TestMemPoolEntryHelper entry;
# 212 : 2 : CBlock block(BuildBlockTestCase());
# 213 : :
# 214 : 2 : LOCK2(cs_main, pool.cs);
# 215 : 2 : pool.addUnchecked(entry.FromTx(block.vtx[1]));
# 216 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
# 217 : :
# 218 : 2 : uint256 txhash;
# 219 : :
# 220 : : // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
# 221 : 2 : {
# 222 : 2 : TestHeaderAndShortIDs shortIDs(block);
# 223 : 2 : shortIDs.prefilledtxn.resize(2);
# 224 : 2 : shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
# 225 : 2 : shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
# 226 : 2 : shortIDs.shorttxids.resize(1);
# 227 : 2 : shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetHash());
# 228 : :
# 229 : 2 : CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
# 230 : 2 : stream << shortIDs;
# 231 : :
# 232 : 2 : CBlockHeaderAndShortTxIDs shortIDs2;
# 233 : 2 : stream >> shortIDs2;
# 234 : :
# 235 : 2 : PartiallyDownloadedBlock partialBlock(&pool);
# 236 : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
# 237 : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(0));
# 238 : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(1));
# 239 : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(2));
# 240 : :
# 241 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
# 242 : :
# 243 : 2 : CBlock block2;
# 244 : 2 : PartiallyDownloadedBlock partialBlockCopy = partialBlock;
# 245 : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_OK);
# 246 : 2 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
# 247 : 2 : bool mutated;
# 248 : 2 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
# 249 : 2 : BOOST_CHECK(!mutated);
# 250 : :
# 251 : 2 : txhash = block.vtx[1]->GetHash();
# 252 : 2 : block.vtx.clear();
# 253 : 2 : block2.vtx.clear();
# 254 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
# 255 : 2 : }
# 256 : 2 : BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
# 257 : 2 : }
# 258 : :
# 259 : : BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
# 260 : 2 : {
# 261 : 2 : CTxMemPool pool;
# 262 : 2 : CMutableTransaction coinbase;
# 263 : 2 : coinbase.vin.resize(1);
# 264 : 2 : coinbase.vin[0].scriptSig.resize(10);
# 265 : 2 : coinbase.vout.resize(1);
# 266 : 2 : coinbase.vout[0].nValue = 42;
# 267 : :
# 268 : 2 : CBlock block;
# 269 : 2 : block.vtx.resize(1);
# 270 : 2 : block.vtx[0] = MakeTransactionRef(std::move(coinbase));
# 271 : 2 : block.nVersion = 42;
# 272 : 2 : block.hashPrevBlock = InsecureRand256();
# 273 : 2 : block.nBits = 0x207fffff;
# 274 : :
# 275 : 2 : bool mutated;
# 276 : 2 : block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
# 277 : 2 : assert(!mutated);
# 278 [ - + ]: 2 : while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
# 279 : :
# 280 : : // Test simple header round-trip with only coinbase
# 281 : 2 : {
# 282 : 2 : CBlockHeaderAndShortTxIDs shortIDs(block, false);
# 283 : :
# 284 : 2 : CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
# 285 : 2 : stream << shortIDs;
# 286 : :
# 287 : 2 : CBlockHeaderAndShortTxIDs shortIDs2;
# 288 : 2 : stream >> shortIDs2;
# 289 : :
# 290 : 2 : PartiallyDownloadedBlock partialBlock(&pool);
# 291 : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
# 292 : 2 : BOOST_CHECK(partialBlock.IsTxAvailable(0));
# 293 : :
# 294 : 2 : CBlock block2;
# 295 : 2 : std::vector<CTransactionRef> vtx_missing;
# 296 : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
# 297 : 2 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
# 298 : 2 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
# 299 : 2 : BOOST_CHECK(!mutated);
# 300 : 2 : }
# 301 : 2 : }
# 302 : :
# 303 : 2 : BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
# 304 : 2 : BlockTransactionsRequest req1;
# 305 : 2 : req1.blockhash = InsecureRand256();
# 306 : 2 : req1.indexes.resize(4);
# 307 : 2 : req1.indexes[0] = 0;
# 308 : 2 : req1.indexes[1] = 1;
# 309 : 2 : req1.indexes[2] = 3;
# 310 : 2 : req1.indexes[3] = 4;
# 311 : :
# 312 : 2 : CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
# 313 : 2 : stream << req1;
# 314 : :
# 315 : 2 : BlockTransactionsRequest req2;
# 316 : 2 : stream >> req2;
# 317 : :
# 318 : 2 : BOOST_CHECK_EQUAL(req1.blockhash.ToString(), req2.blockhash.ToString());
# 319 : 2 : BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
# 320 : 2 : BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
# 321 : 2 : BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
# 322 : 2 : BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
# 323 : 2 : BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
# 324 : 2 : }
# 325 : :
# 326 : 2 : BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationMaxTest) {
# 327 : : // Check that the highest legal index is decoded correctly
# 328 : 2 : BlockTransactionsRequest req0;
# 329 : 2 : req0.blockhash = InsecureRand256();
# 330 : 2 : req0.indexes.resize(1);
# 331 : 2 : req0.indexes[0] = 0xffff;
# 332 : 2 : CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
# 333 : 2 : stream << req0;
# 334 : :
# 335 : 2 : BlockTransactionsRequest req1;
# 336 : 2 : stream >> req1;
# 337 : 2 : BOOST_CHECK_EQUAL(req0.indexes.size(), req1.indexes.size());
# 338 : 2 : BOOST_CHECK_EQUAL(req0.indexes[0], req1.indexes[0]);
# 339 : 2 : }
# 340 : :
# 341 : 2 : BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
# 342 : : // Any set of index deltas that starts with N values that sum to (0x10000 - N)
# 343 : : // causes the edge-case overflow that was originally not checked for. Such
# 344 : : // a request cannot be created by serializing a real BlockTransactionsRequest
# 345 : : // due to the overflow, so here we'll serialize from raw deltas.
# 346 : 2 : BlockTransactionsRequest req0;
# 347 : 2 : req0.blockhash = InsecureRand256();
# 348 : 2 : req0.indexes.resize(3);
# 349 : 2 : req0.indexes[0] = 0x7000;
# 350 : 2 : req0.indexes[1] = 0x10000 - 0x7000 - 2;
# 351 : 2 : req0.indexes[2] = 0;
# 352 : 2 : CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
# 353 : 2 : stream << req0.blockhash;
# 354 : 2 : WriteCompactSize(stream, req0.indexes.size());
# 355 : 2 : WriteCompactSize(stream, req0.indexes[0]);
# 356 : 2 : WriteCompactSize(stream, req0.indexes[1]);
# 357 : 2 : WriteCompactSize(stream, req0.indexes[2]);
# 358 : :
# 359 : 2 : BlockTransactionsRequest req1;
# 360 : 2 : try {
# 361 : 2 : stream >> req1;
# 362 : : // before patch: deserialize above succeeds and this check fails, demonstrating the overflow
# 363 : 2 : BOOST_CHECK(req1.indexes[1] < req1.indexes[2]);
# 364 : : // this shouldn't be reachable before or after patch
# 365 : 2 : BOOST_CHECK(0);
# 366 : 2 : } catch(std::ios_base::failure &) {
# 367 : : // deserialize should fail
# 368 : 2 : BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
# 369 : 2 : }
# 370 : 2 : }
# 371 : :
# 372 : : BOOST_AUTO_TEST_SUITE_END()
|