LCOV - code coverage report
Current view: top level - src/test - blockencodings_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 255 257 99.2 %
Date: 2020-09-26 01:30:44 Functions: 65 65 100.0 %

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

Generated by: LCOV version 1.15