LCOV - code coverage report
Current view: top level - src/test - blockencodings_tests.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 97.9 % 332 325
Test Date: 2026-08-07 06:33:33 Functions: 100.0 % 23 23
Branches: 49.7 % 1522 756

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2011-present 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                 :             : #include <test/util/random.h>
      11                 :             : #include <test/util/txmempool.h>
      12                 :             : 
      13                 :             : #include <test/util/common.h>
      14                 :             : #include <test/util/setup_common.h>
      15                 :             : 
      16                 :             : #include <boost/test/unit_test.hpp>
      17                 :             : 
      18                 :             : const std::vector<std::pair<Wtxid, CTransactionRef>> empty_extra_txn;
      19                 :             : 
      20                 :             : BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup)
      21                 :             : 
      22                 :           7 : static CMutableTransaction BuildTransactionTestCase() {
      23                 :           7 :     CMutableTransaction tx;
      24         [ +  - ]:           7 :     tx.vin.resize(1);
      25                 :           7 :     tx.vin[0].scriptSig.resize(10);
      26         [ +  - ]:           7 :     tx.vout.resize(1);
      27                 :           7 :     tx.vout[0].nValue = 42;
      28                 :           7 :     return tx;
      29                 :           0 : }
      30                 :             : 
      31                 :           4 : static CBlock BuildBlockTestCase(FastRandomContext& ctx) {
      32                 :           4 :     CBlock block;
      33         [ +  - ]:           4 :     CMutableTransaction tx = BuildTransactionTestCase();
      34                 :             : 
      35         [ +  - ]:           4 :     block.vtx.resize(3);
      36   [ +  -  -  + ]:           8 :     block.vtx[0] = MakeTransactionRef(tx);
      37                 :           4 :     block.nVersion = 42;
      38                 :           4 :     block.hashPrevBlock = ctx.rand256();
      39                 :           4 :     block.nBits = 0x207fffff;
      40                 :             : 
      41         [ +  - ]:           4 :     tx.vin[0].prevout.hash = Txid::FromUint256(ctx.rand256());
      42                 :           4 :     tx.vin[0].prevout.n = 0;
      43   [ +  -  -  + ]:           8 :     block.vtx[1] = MakeTransactionRef(tx);
      44                 :             : 
      45         [ +  - ]:           4 :     tx.vin.resize(10);
      46   [ -  +  +  + ]:          44 :     for (size_t i = 0; i < tx.vin.size(); i++) {
      47                 :          40 :         tx.vin[i].prevout.hash = Txid::FromUint256(ctx.rand256());
      48                 :          40 :         tx.vin[i].prevout.n = 0;
      49                 :             :     }
      50   [ +  -  -  + ]:           8 :     block.vtx[2] = MakeTransactionRef(tx);
      51                 :             : 
      52                 :           4 :     bool mutated;
      53         [ +  - ]:           4 :     block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
      54         [ -  + ]:           4 :     assert(!mutated);
      55   [ +  -  +  -  :           6 :     while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
             +  -  +  + ]
      56                 :           4 :     return block;
      57                 :           4 : }
      58                 :             : 
      59                 :             : // Number of shared use_counts we expect for a tx we haven't touched
      60                 :             : // (block + mempool entry + our copy from the GetSharedTx call)
      61                 :             : constexpr long SHARED_TX_OFFSET{3};
      62                 :             : 
      63   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
      64                 :             : {
      65         [ -  + ]:           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
      66                 :           1 :     TestMemPoolEntryHelper entry;
      67                 :           1 :     auto rand_ctx(FastRandomContext(uint256{42}));
      68         [ +  - ]:           1 :     CBlock block(BuildBlockTestCase(rand_ctx));
      69                 :             : 
      70   [ +  -  +  - ]:           1 :     LOCK2(cs_main, pool.cs);
      71   [ +  -  +  - ]:           1 :     TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
      72   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
             +  -  +  - ]
      73                 :             : 
      74                 :             :     // Do a simple ShortTxIDs RT
      75                 :           1 :     {
      76         [ +  - ]:           1 :         CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
      77                 :             : 
      78                 :           1 :         DataStream stream{};
      79         [ +  - ]:           1 :         stream << shortIDs;
      80                 :             : 
      81                 :           1 :         CBlockHeaderAndShortTxIDs shortIDs2;
      82         [ +  - ]:           1 :         stream >> shortIDs2;
      83                 :             : 
      84                 :           1 :         PartiallyDownloadedBlock partialBlock(&pool);
      85   [ +  -  +  -  :           2 :         BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
             +  -  +  - ]
      86   [ +  -  +  -  :           2 :         BOOST_CHECK( partialBlock.IsTxAvailable(0));
             +  -  +  - ]
      87   [ +  -  +  -  :           2 :         BOOST_CHECK(!partialBlock.IsTxAvailable(1));
             +  -  +  - ]
      88   [ +  -  +  -  :           2 :         BOOST_CHECK( partialBlock.IsTxAvailable(2));
             +  -  +  - ]
      89                 :             : 
      90   [ +  -  +  -  :           2 :         BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
             +  -  +  - ]
      91                 :             : 
      92         [ +  - ]:           1 :         size_t poolSize = pool.size();
      93         [ +  - ]:           1 :         pool.removeRecursive(*block.vtx[2], MemPoolRemovalReason::REPLACED);
      94   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);
                   +  - ]
      95                 :             : 
      96                 :           1 :         CBlock block2;
      97                 :           1 :         {
      98         [ +  - ]:           1 :             PartiallyDownloadedBlock tmp = partialBlock;
      99   [ +  -  +  -  :           2 :             BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_INVALID); // No transactions
             +  -  +  - ]
     100         [ +  - ]:           1 :             partialBlock = tmp;
     101                 :           0 :         }
     102                 :             : 
     103                 :             :         // Wrong transaction
     104                 :           1 :         {
     105         [ +  - ]:           1 :             PartiallyDownloadedBlock tmp = partialBlock;
     106   [ +  -  +  +  :           2 :             partialBlock.FillBlock(block2, {block.vtx[2]}, /*segwit_active=*/true); // Current implementation doesn't check txn here, but don't require that
          +  -  -  -  -  
                      - ]
     107         [ +  - ]:           1 :             partialBlock = tmp;
     108                 :           0 :         }
     109                 :           1 :         bool mutated;
     110   [ +  -  +  -  :           2 :         BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
                   +  - ]
     111                 :             : 
     112                 :           1 :         CBlock block3;
     113   [ +  -  +  -  :           4 :         BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}, /*segwit_active=*/true) == READ_STATUS_OK);
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     114   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
          +  -  +  -  +  
                -  +  - ]
     115   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
          +  -  +  -  +  
                      - ]
     116   [ +  -  +  - ]:           2 :         BOOST_CHECK(!mutated);
     117         [ +  - ]:           4 :     }
     118   [ +  -  +  -  :           6 : }
          +  -  +  -  +  
                      - ]
     119                 :             : 
     120                 :           2 : class TestHeaderAndShortIDs {
     121                 :             :     // Utility to encode custom CBlockHeaderAndShortTxIDs
     122                 :             : public:
     123                 :             :     CBlockHeader header;
     124                 :             :     uint64_t nonce;
     125                 :             :     std::vector<uint64_t> shorttxids;
     126                 :             :     std::vector<PrefilledTransaction> prefilledtxn;
     127                 :             : 
     128         [ +  - ]:           2 :     explicit TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs& orig) {
     129                 :           2 :         DataStream stream{};
     130         [ +  - ]:           2 :         stream << orig;
     131         [ +  - ]:           4 :         stream >> *this;
     132                 :           2 :     }
     133                 :           2 :     explicit TestHeaderAndShortIDs(const CBlock& block, FastRandomContext& ctx) :
     134         [ +  - ]:           4 :         TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs{block, ctx.rand64()}) {}
     135                 :             : 
     136                 :           3 :     uint64_t GetShortID(const Wtxid& txhash) const {
     137                 :           3 :         DataStream stream{};
     138         [ +  - ]:           3 :         stream << *this;
     139                 :           3 :         CBlockHeaderAndShortTxIDs base;
     140         [ +  - ]:           3 :         stream >> base;
     141         [ +  - ]:           3 :         return base.GetShortID(txhash);
     142                 :           3 :     }
     143                 :             : 
     144                 :          14 :     SERIALIZE_METHODS(TestHeaderAndShortIDs, obj) { READWRITE(obj.header, obj.nonce, Using<VectorFormatter<CustomUintFormatter<CBlockHeaderAndShortTxIDs::SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn); }
     145                 :             : };
     146                 :             : 
     147                 :           0 : struct TestPartiallyDownloadedBlock : PartiallyDownloadedBlock {
     148                 :           3 :     using PartiallyDownloadedBlock::PartiallyDownloadedBlock;
     149                 :             : 
     150                 :           3 :     size_t GetMempoolCount() const { return mempool_count; }
     151                 :           3 :     size_t GetExtraCount() const { return extra_count; }
     152                 :             : };
     153                 :             : 
     154   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     155                 :             : {
     156         [ -  + ]:           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     157                 :           1 :     TestMemPoolEntryHelper entry;
     158                 :           1 :     auto rand_ctx(FastRandomContext(uint256{42}));
     159         [ +  - ]:           1 :     CBlock block(BuildBlockTestCase(rand_ctx));
     160                 :             : 
     161   [ +  -  +  - ]:           1 :     LOCK2(cs_main, pool.cs);
     162   [ +  -  +  - ]:           1 :     TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
     163   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
             +  -  +  - ]
     164                 :             : 
     165         [ +  - ]:           1 :     Txid txhash;
     166                 :             : 
     167                 :             :     // Test with pre-forwarding tx 1, but not coinbase
     168                 :           1 :     {
     169         [ +  - ]:           1 :         TestHeaderAndShortIDs shortIDs(block, rand_ctx);
     170         [ +  - ]:           1 :         shortIDs.prefilledtxn.resize(1);
     171         [ -  + ]:           1 :         shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
     172         [ +  - ]:           1 :         shortIDs.shorttxids.resize(2);
     173   [ +  -  +  - ]:           1 :         shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetWitnessHash());
     174   [ +  -  +  - ]:           1 :         shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetWitnessHash());
     175                 :             : 
     176                 :           1 :         DataStream stream{};
     177         [ +  - ]:           1 :         stream << shortIDs;
     178                 :             : 
     179                 :           1 :         CBlockHeaderAndShortTxIDs shortIDs2;
     180         [ +  - ]:           1 :         stream >> shortIDs2;
     181                 :             : 
     182                 :           1 :         PartiallyDownloadedBlock partialBlock(&pool);
     183   [ +  -  +  -  :           2 :         BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
             +  -  +  - ]
     184   [ +  -  +  -  :           2 :         BOOST_CHECK(!partialBlock.IsTxAvailable(0));
             +  -  +  - ]
     185   [ +  -  +  -  :           2 :         BOOST_CHECK( partialBlock.IsTxAvailable(1));
             +  -  +  - ]
     186   [ +  -  +  -  :           2 :         BOOST_CHECK( partialBlock.IsTxAvailable(2));
             +  -  +  - ]
     187                 :             : 
     188   [ +  -  +  -  :           2 :         BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock
             +  -  +  - ]
     189                 :             : 
     190                 :           1 :         CBlock block2;
     191                 :           1 :         {
     192         [ +  - ]:           1 :             PartiallyDownloadedBlock tmp = partialBlock;
     193   [ +  -  +  -  :           2 :             BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_INVALID); // No transactions
             +  -  +  - ]
     194         [ +  - ]:           1 :             partialBlock = tmp;
     195                 :           0 :         }
     196                 :             : 
     197                 :             :         // Wrong transaction
     198                 :           1 :         {
     199         [ +  - ]:           1 :             PartiallyDownloadedBlock tmp = partialBlock;
     200   [ +  -  +  +  :           2 :             partialBlock.FillBlock(block2, {block.vtx[1]}, /*segwit_active=*/true); // Current implementation doesn't check txn here, but don't require that
          +  -  -  -  -  
                      - ]
     201         [ +  - ]:           1 :             partialBlock = tmp;
     202                 :           0 :         }
     203   [ +  -  +  -  :           2 :         BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
             +  -  +  - ]
     204                 :           1 :         bool mutated;
     205   [ +  -  +  -  :           2 :         BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
                   +  - ]
     206                 :             : 
     207                 :           1 :         CBlock block3;
     208         [ +  - ]:           1 :         PartiallyDownloadedBlock partialBlockCopy = partialBlock;
     209   [ +  -  +  -  :           4 :         BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}, /*segwit_active=*/true) == READ_STATUS_OK);
          +  -  +  -  +  
          +  +  -  -  -  
                   -  - ]
     210   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
          +  -  +  -  +  
                -  +  - ]
     211   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
          +  -  +  -  +  
                      - ]
     212   [ +  -  +  -  :           2 :         BOOST_CHECK(!mutated);
                   +  - ]
     213                 :             : 
     214   [ +  -  +  -  :           2 :         BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 3); // +2 because of partialBlock and block2 and block3
             +  -  +  - ]
     215                 :             : 
     216                 :           1 :         txhash = block.vtx[2]->GetHash();
     217                 :           1 :         block.vtx.clear();
     218                 :           1 :         block2.vtx.clear();
     219                 :           1 :         block3.vtx.clear();
     220   [ +  -  +  -  :           3 :         BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
             +  -  +  - ]
     221                 :           3 :     }
     222   [ +  -  +  -  :           3 :     BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
          +  -  +  -  +  
                      - ]
     223   [ +  -  +  -  :           8 : }
          +  -  +  -  +  
                -  +  - ]
     224                 :             : 
     225   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     226                 :             : {
     227         [ -  + ]:           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     228                 :           1 :     TestMemPoolEntryHelper entry;
     229                 :           1 :     auto rand_ctx(FastRandomContext(uint256{42}));
     230         [ +  - ]:           1 :     CBlock block(BuildBlockTestCase(rand_ctx));
     231                 :             : 
     232   [ +  -  +  - ]:           1 :     LOCK2(cs_main, pool.cs);
     233   [ +  -  +  - ]:           1 :     TryAddToMempool(pool, entry.FromTx(block.vtx[1]));
     234   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
             +  -  +  - ]
     235                 :             : 
     236         [ +  - ]:           1 :     Txid txhash;
     237                 :             : 
     238                 :             :     // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
     239                 :           1 :     {
     240         [ +  - ]:           1 :         TestHeaderAndShortIDs shortIDs(block, rand_ctx);
     241         [ +  - ]:           1 :         shortIDs.prefilledtxn.resize(2);
     242         [ -  + ]:           1 :         shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
     243         [ -  + ]:           1 :         shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
     244         [ +  - ]:           1 :         shortIDs.shorttxids.resize(1);
     245   [ +  -  +  - ]:           1 :         shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetWitnessHash());
     246                 :             : 
     247                 :           1 :         DataStream stream{};
     248         [ +  - ]:           1 :         stream << shortIDs;
     249                 :             : 
     250                 :           1 :         CBlockHeaderAndShortTxIDs shortIDs2;
     251         [ +  - ]:           1 :         stream >> shortIDs2;
     252                 :             : 
     253                 :           1 :         PartiallyDownloadedBlock partialBlock(&pool);
     254   [ +  -  +  -  :           2 :         BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
             +  -  +  - ]
     255   [ +  -  +  -  :           2 :         BOOST_CHECK( partialBlock.IsTxAvailable(0));
             +  -  +  - ]
     256   [ +  -  +  -  :           2 :         BOOST_CHECK( partialBlock.IsTxAvailable(1));
             +  -  +  - ]
     257   [ +  -  +  -  :           2 :         BOOST_CHECK( partialBlock.IsTxAvailable(2));
             +  -  +  - ]
     258                 :             : 
     259   [ +  -  +  -  :           2 :         BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
             +  -  +  - ]
     260                 :             : 
     261                 :           1 :         CBlock block2;
     262         [ +  - ]:           1 :         PartiallyDownloadedBlock partialBlockCopy = partialBlock;
     263   [ +  -  +  -  :           2 :         BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_OK);
             +  -  +  - ]
     264   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
          +  -  +  -  +  
                -  +  - ]
     265                 :           1 :         bool mutated;
     266   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
          +  -  +  -  +  
                      - ]
     267   [ +  -  +  - ]:           2 :         BOOST_CHECK(!mutated);
     268                 :             : 
     269                 :           1 :         txhash = block.vtx[1]->GetHash();
     270                 :           1 :         block.vtx.clear();
     271                 :           1 :         block2.vtx.clear();
     272   [ +  -  +  -  :           3 :         BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
             +  -  +  - ]
     273                 :           3 :     }
     274   [ +  -  +  -  :           3 :     BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
          +  -  +  -  +  
                      - ]
     275   [ +  -  +  -  :           6 : }
                   +  - ]
     276                 :             : 
     277   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     278                 :             : {
     279         [ -  + ]:           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     280                 :           1 :     CMutableTransaction coinbase = BuildTransactionTestCase();
     281                 :             : 
     282                 :           1 :     CBlock block;
     283                 :           1 :     auto rand_ctx(FastRandomContext(uint256{42}));
     284         [ +  - ]:           1 :     block.vtx.resize(1);
     285   [ +  -  -  + ]:           2 :     block.vtx[0] = MakeTransactionRef(std::move(coinbase));
     286                 :           1 :     block.nVersion = 42;
     287                 :           1 :     block.hashPrevBlock = rand_ctx.rand256();
     288                 :           1 :     block.nBits = 0x207fffff;
     289                 :             : 
     290                 :           1 :     bool mutated;
     291         [ +  - ]:           1 :     block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
     292         [ +  - ]:           1 :     assert(!mutated);
     293   [ +  -  +  -  :           1 :     while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
             +  -  -  + ]
     294                 :             : 
     295                 :             :     // Test simple header round-trip with only coinbase
     296                 :           1 :     {
     297         [ +  - ]:           1 :         CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
     298                 :             : 
     299                 :           1 :         DataStream stream{};
     300         [ +  - ]:           1 :         stream << shortIDs;
     301                 :             : 
     302                 :           1 :         CBlockHeaderAndShortTxIDs shortIDs2;
     303         [ +  - ]:           1 :         stream >> shortIDs2;
     304                 :             : 
     305                 :           1 :         PartiallyDownloadedBlock partialBlock(&pool);
     306   [ +  -  +  -  :           2 :         BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
             +  -  +  - ]
     307   [ +  -  +  -  :           2 :         BOOST_CHECK(partialBlock.IsTxAvailable(0));
                   +  - ]
     308                 :             : 
     309                 :           1 :         CBlock block2;
     310                 :           1 :         std::vector<CTransactionRef> vtx_missing;
     311   [ +  -  +  -  :           2 :         BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing, /*segwit_active=*/true) == READ_STATUS_OK);
             +  -  +  - ]
     312   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
          +  -  +  -  +  
                -  +  - ]
     313   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
          +  -  +  -  +  
                      - ]
     314   [ +  -  +  - ]:           2 :         BOOST_CHECK(!mutated);
     315                 :           4 :     }
     316                 :           2 : }
     317                 :             : 
     318   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(ReceiveWithExtraTransactions) {
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     319         [ -  + ]:           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     320                 :           1 :     TestMemPoolEntryHelper entry;
     321                 :           1 :     auto rand_ctx(FastRandomContext(uint256{42}));
     322                 :             : 
     323         [ +  - ]:           1 :     CMutableTransaction mtx = BuildTransactionTestCase();
     324         [ +  - ]:           1 :     mtx.vin[0].prevout.hash = Txid::FromUint256(rand_ctx.rand256());
     325                 :           1 :     mtx.vin[0].prevout.n = 0;
     326         [ +  - ]:           1 :     const CTransactionRef non_block_tx = MakeTransactionRef(std::move(mtx));
     327                 :             : 
     328         [ +  - ]:           1 :     CBlock block(BuildBlockTestCase(rand_ctx));
     329                 :             :     // Leave one transaction missing so scanning doesn't stop before the collision.
     330         [ +  - ]:           1 :     mtx = BuildTransactionTestCase();
     331         [ +  - ]:           1 :     mtx.vin[0].prevout.hash = Txid::FromUint256(rand_ctx.rand256());
     332   [ +  -  +  -  :           2 :     block.vtx.push_back(MakeTransactionRef(std::move(mtx)));
                   -  + ]
     333         [ +  - ]:           1 :     block.hashMerkleRoot = BlockMerkleRoot(block);
     334   [ +  -  +  -  :           2 :     while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
             +  -  +  + ]
     335                 :             : 
     336                 :           1 :     std::vector<std::pair<Wtxid, CTransactionRef>> extra_txn;
     337         [ +  - ]:           1 :     extra_txn.resize(10);
     338                 :             : 
     339   [ +  -  +  - ]:           1 :     LOCK2(cs_main, pool.cs);
     340   [ +  -  +  - ]:           1 :     TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
     341   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
             +  -  +  - ]
     342                 :             :     // Ensure the non_block_tx is actually not in the block
     343         [ +  + ]:           5 :     for (const auto &block_tx : block.vtx) {
     344   [ +  -  +  - ]:           4 :         BOOST_CHECK_NE(block_tx->GetHash(), non_block_tx->GetHash());
     345                 :             :     }
     346                 :             :     // Ensure block.vtx[1] is not in pool
     347   [ +  -  +  -  :           1 :     BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()), nullptr);
             +  -  -  + ]
     348                 :             : 
     349                 :           1 :     {
     350         [ +  - ]:           1 :         const CBlockHeaderAndShortTxIDs cmpctblock{block, rand_ctx.rand64()};
     351                 :           1 :         PartiallyDownloadedBlock partial_block(&pool);
     352                 :           1 :         PartiallyDownloadedBlock partial_block_with_extra(&pool);
     353                 :             : 
     354   [ +  -  +  -  :           2 :         BOOST_CHECK(partial_block.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
             +  -  +  - ]
     355   [ +  -  +  -  :           2 :         BOOST_CHECK( partial_block.IsTxAvailable(0));
             +  -  +  - ]
     356   [ +  -  +  -  :           2 :         BOOST_CHECK(!partial_block.IsTxAvailable(1));
             +  -  +  - ]
     357   [ +  -  +  -  :           2 :         BOOST_CHECK( partial_block.IsTxAvailable(2));
                   +  - ]
     358                 :             : 
     359                 :             :         // Add an unrelated tx to extra_txn:
     360         [ -  + ]:           1 :         extra_txn[0] = {non_block_tx->GetWitnessHash(), non_block_tx};
     361                 :             :         // and a tx from the block that's not in the mempool:
     362         [ -  + ]:           1 :         extra_txn[1] = {block.vtx[1]->GetWitnessHash(), block.vtx[1]};
     363                 :             : 
     364   [ +  -  +  -  :           2 :         BOOST_CHECK(partial_block_with_extra.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
             +  -  +  - ]
     365   [ +  -  +  -  :           2 :         BOOST_CHECK(partial_block_with_extra.IsTxAvailable(0));
             +  -  +  - ]
     366                 :             :         // This transaction is now available via extra_txn:
     367   [ +  -  +  -  :           2 :         BOOST_CHECK(partial_block_with_extra.IsTxAvailable(1));
             +  -  +  - ]
     368   [ +  -  +  -  :           2 :         BOOST_CHECK(partial_block_with_extra.IsTxAvailable(2));
                   +  - ]
     369                 :             : 
     370                 :             :         // Simulate a mempool collision after finding an unrelated extra transaction.
     371         [ -  + ]:           1 :         extra_txn[2] = {block.vtx[2]->GetWitnessHash(), non_block_tx};
     372                 :           1 :         TestPartiallyDownloadedBlock partial_block_with_extra_collision{&pool};
     373   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(partial_block_with_extra_collision.InitData(cmpctblock, extra_txn), READ_STATUS_OK);
                   +  - ]
     374   [ +  -  +  -  :           2 :         BOOST_CHECK(partial_block_with_extra_collision.IsTxAvailable(1));
             +  -  +  - ]
     375   [ +  -  +  -  :           2 :         BOOST_CHECK(!partial_block_with_extra_collision.IsTxAvailable(2));
             +  -  +  - ]
     376   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(partial_block_with_extra_collision.GetMempoolCount(), 1U);
     377   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(partial_block_with_extra_collision.GetExtraCount(), 1U);
     378                 :             : 
     379                 :             :         // Now also collide the extra-sourced slot: both counters decrement exactly once.
     380         [ -  + ]:           1 :         extra_txn[3] = {block.vtx[1]->GetWitnessHash(), non_block_tx};
     381                 :           1 :         TestPartiallyDownloadedBlock partial_block_with_extra_source_collision{&pool};
     382   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(partial_block_with_extra_source_collision.InitData(cmpctblock, extra_txn), READ_STATUS_OK);
                   +  - ]
     383   [ +  -  +  -  :           2 :         BOOST_CHECK(!partial_block_with_extra_source_collision.IsTxAvailable(1));
             +  -  +  - ]
     384   [ +  -  +  -  :           2 :         BOOST_CHECK(!partial_block_with_extra_source_collision.IsTxAvailable(2));
             +  -  +  - ]
     385   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(partial_block_with_extra_source_collision.GetMempoolCount(), 0U);
     386   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(partial_block_with_extra_source_collision.GetExtraCount(), 0U);
     387                 :             : 
     388                 :             :         // Collided slots are terminal: not even the genuine transactions refill them.
     389         [ -  + ]:           1 :         extra_txn[4] = {block.vtx[2]->GetWitnessHash(), block.vtx[2]};
     390         [ -  + ]:           1 :         extra_txn[5] = {block.vtx[1]->GetWitnessHash(), block.vtx[1]};
     391                 :           1 :         TestPartiallyDownloadedBlock partial_block_no_refill{&pool};
     392   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(partial_block_no_refill.InitData(cmpctblock, extra_txn), READ_STATUS_OK);
                   +  - ]
     393   [ +  -  +  -  :           2 :         BOOST_CHECK(!partial_block_no_refill.IsTxAvailable(1));
             +  -  +  - ]
     394   [ +  -  +  -  :           2 :         BOOST_CHECK(!partial_block_no_refill.IsTxAvailable(2));
             +  -  +  - ]
     395   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(partial_block_no_refill.GetMempoolCount(), 0U);
     396   [ +  -  +  - ]:           1 :         BOOST_CHECK_EQUAL(partial_block_no_refill.GetExtraCount(), 0U);
     397         [ +  - ]:           6 :     }
     398   [ +  -  +  - ]:           4 : }
     399                 :             : 
     400   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     401                 :           1 :     BlockTransactionsRequest req1;
     402                 :           1 :     req1.blockhash = m_rng.rand256();
     403         [ +  - ]:           1 :     req1.indexes.resize(4);
     404         [ +  - ]:           1 :     req1.indexes[0] = 0;
     405                 :           1 :     req1.indexes[1] = 1;
     406                 :           1 :     req1.indexes[2] = 3;
     407                 :           1 :     req1.indexes[3] = 4;
     408                 :             : 
     409                 :           1 :     DataStream stream{};
     410         [ +  - ]:           1 :     stream << req1;
     411                 :             : 
     412                 :           1 :     BlockTransactionsRequest req2;
     413         [ +  - ]:           1 :     stream >> req2;
     414                 :             : 
     415   [ +  -  +  -  :           1 :     BOOST_CHECK_EQUAL(req1.blockhash.ToString(), req2.blockhash.ToString());
             +  -  +  - ]
     416   [ +  -  -  +  :           1 :     BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
             -  +  +  - ]
     417   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
     418   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
     419   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
     420   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
     421                 :           1 : }
     422                 :             : 
     423   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationMaxTest) {
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     424                 :             :     // Check that the highest legal index is decoded correctly
     425                 :           1 :     BlockTransactionsRequest req0;
     426                 :           1 :     req0.blockhash = m_rng.rand256();
     427         [ +  - ]:           1 :     req0.indexes.resize(1);
     428         [ +  - ]:           1 :     req0.indexes[0] = 0xffff;
     429                 :           1 :     DataStream stream{};
     430         [ +  - ]:           1 :     stream << req0;
     431                 :             : 
     432                 :           1 :     BlockTransactionsRequest req1;
     433         [ +  - ]:           1 :     stream >> req1;
     434   [ +  -  -  +  :           1 :     BOOST_CHECK_EQUAL(req0.indexes.size(), req1.indexes.size());
             -  +  +  - ]
     435   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(req0.indexes[0], req1.indexes[0]);
     436                 :           1 : }
     437                 :             : 
     438   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     439                 :             :     // Any set of index deltas that starts with N values that sum to (0x10000 - N)
     440                 :             :     // causes the edge-case overflow that was originally not checked for. Such
     441                 :             :     // a request cannot be created by serializing a real BlockTransactionsRequest
     442                 :             :     // due to the overflow, so here we'll serialize from raw deltas.
     443                 :           1 :     BlockTransactionsRequest req0;
     444                 :           1 :     req0.blockhash = m_rng.rand256();
     445         [ +  - ]:           1 :     req0.indexes.resize(3);
     446         [ +  - ]:           1 :     req0.indexes[0] = 0x7000;
     447                 :           1 :     req0.indexes[1] = 0x10000 - 0x7000 - 2;
     448                 :           1 :     req0.indexes[2] = 0;
     449                 :           1 :     DataStream stream{};
     450         [ +  - ]:           1 :     stream << req0.blockhash;
     451   [ -  +  +  - ]:           1 :     WriteCompactSize(stream, req0.indexes.size());
     452         [ +  - ]:           1 :     WriteCompactSize(stream, req0.indexes[0]);
     453         [ +  - ]:           1 :     WriteCompactSize(stream, req0.indexes[1]);
     454         [ +  - ]:           1 :     WriteCompactSize(stream, req0.indexes[2]);
     455                 :             : 
     456                 :           1 :     BlockTransactionsRequest req1;
     457                 :           1 :     try {
     458         [ -  + ]:           1 :         stream >> req1;
     459                 :             :         // before patch: deserialize above succeeds and this check fails, demonstrating the overflow
     460   [ #  #  #  #  :           0 :         BOOST_CHECK(req1.indexes[1] < req1.indexes[2]);
                   #  # ]
     461                 :             :         // this shouldn't be reachable before or after patch
     462   [ -  -  -  - ]:           1 :         BOOST_CHECK(0);
     463         [ -  + ]:           1 :     } catch(std::ios_base::failure &) {
     464                 :             :         // deserialize should fail
     465   [ +  -  +  - ]:           2 :         BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
     466                 :           1 :     }
     467                 :           1 : }
     468                 :             : 
     469                 :             : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1