blockencodings.h raw

   1  // Copyright (c) 2016-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  #ifndef BITCOIN_BLOCKENCODINGS_H
   6  #define BITCOIN_BLOCKENCODINGS_H
   7  
   8  #include <crypto/siphash.h>
   9  #include <primitives/block.h>
  10  
  11  #include <functional>
  12  
  13  class CTxMemPool;
  14  class BlockValidationState;
  15  namespace Consensus {
  16  struct Params;
  17  };
  18  
  19  // Transaction compression schemes for compact block relay can be introduced by writing
  20  // an actual formatter here.
  21  using TransactionCompression = DefaultFormatter;
  22  
  23  class DifferenceFormatter
  24  {
  25      uint64_t m_shift = 0;
  26  
  27  public:
  28      template<typename Stream, typename I>
  29      void Ser(Stream& s, I v)
  30      {
  31          if (v < m_shift || v >= std::numeric_limits<uint64_t>::max()) throw std::ios_base::failure("differential value overflow");
  32          WriteCompactSize(s, v - m_shift);
  33          m_shift = uint64_t(v) + 1;
  34      }
  35      template<typename Stream, typename I>
  36      void Unser(Stream& s, I& v)
  37      {
  38          uint64_t n = ReadCompactSize(s);
  39          m_shift += n;
  40          if (m_shift < n || m_shift >= std::numeric_limits<uint64_t>::max() || m_shift < std::numeric_limits<I>::min() || m_shift > std::numeric_limits<I>::max()) throw std::ios_base::failure("differential value overflow");
  41          v = I(m_shift++);
  42      }
  43  };
  44  
  45  class BlockTransactionsRequest {
  46  public:
  47      // A BlockTransactionsRequest message
  48      uint256 blockhash;
  49      std::vector<uint16_t> indexes;
  50  
  51      SERIALIZE_METHODS(BlockTransactionsRequest, obj)
  52      {
  53          READWRITE(obj.blockhash, Using<VectorFormatter<DifferenceFormatter>>(obj.indexes));
  54      }
  55  };
  56  
  57  class BlockTransactions {
  58  public:
  59      // A BlockTransactions message
  60      uint256 blockhash;
  61      std::vector<CTransactionRef> txn;
  62  
  63      BlockTransactions() = default;
  64      explicit BlockTransactions(const BlockTransactionsRequest& req) :
  65          blockhash(req.blockhash), txn(req.indexes.size()) {}
  66  
  67      SERIALIZE_METHODS(BlockTransactions, obj)
  68      {
  69          READWRITE(obj.blockhash, TX_WITH_WITNESS(Using<VectorFormatter<TransactionCompression>>(obj.txn)));
  70      }
  71  };
  72  
  73  // Dumb serialization/storage-helper for CBlockHeaderAndShortTxIDs and PartiallyDownloadedBlock
  74  struct PrefilledTransaction {
  75      // Used as an offset since last prefilled tx in CBlockHeaderAndShortTxIDs,
  76      // as a proper transaction-in-block-index in PartiallyDownloadedBlock
  77      uint16_t index;
  78      CTransactionRef tx;
  79  
  80      SERIALIZE_METHODS(PrefilledTransaction, obj) { READWRITE(COMPACTSIZE(obj.index), TX_WITH_WITNESS(Using<TransactionCompression>(obj.tx))); }
  81  };
  82  
  83  typedef enum ReadStatus_t
  84  {
  85      READ_STATUS_OK,
  86      READ_STATUS_INVALID, // Invalid object, peer is sending bogus crap
  87      READ_STATUS_FAILED, // Failed to process object
  88  } ReadStatus;
  89  
  90  class CBlockHeaderAndShortTxIDs {
  91      mutable std::optional<PresaltedSipHasher> m_hasher;
  92      uint64_t nonce;
  93  
  94      void FillShortTxIDSelector() const;
  95  
  96      friend class PartiallyDownloadedBlock;
  97  
  98  protected:
  99      std::vector<uint64_t> shorttxids;
 100      std::vector<PrefilledTransaction> prefilledtxn;
 101  
 102  public:
 103      static constexpr int SHORTTXIDS_LENGTH = 6;
 104  
 105      CBlockHeader header;
 106  
 107      /**
 108       * Dummy for deserialization
 109       */
 110      CBlockHeaderAndShortTxIDs() = default;
 111  
 112      /**
 113       * @param[in]  nonce  This should be randomly generated, and is used for the siphash secret key
 114       */
 115      CBlockHeaderAndShortTxIDs(const CBlock& block, uint64_t nonce);
 116  
 117      uint64_t GetShortID(const Wtxid& wtxid) const;
 118  
 119      size_t BlockTxCount() const { return shorttxids.size() + prefilledtxn.size(); }
 120  
 121      SERIALIZE_METHODS(CBlockHeaderAndShortTxIDs, obj)
 122      {
 123          READWRITE(obj.header, obj.nonce, Using<VectorFormatter<CustomUintFormatter<SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn);
 124          if (ser_action.ForRead()) {
 125              if (obj.BlockTxCount() > std::numeric_limits<uint16_t>::max()) {
 126                  throw std::ios_base::failure("indexes overflowed 16 bits");
 127              }
 128              obj.FillShortTxIDSelector();
 129          }
 130      }
 131  };
 132  
 133  class PartiallyDownloadedBlock {
 134  protected:
 135      std::vector<CTransactionRef> txn_available;
 136      size_t prefilled_count = 0, mempool_count = 0, extra_count = 0;
 137      const CTxMemPool* pool;
 138  public:
 139      CBlockHeader header;
 140  
 141      // Can be overridden for testing
 142      using IsBlockMutatedFn = std::function<bool(const CBlock&, bool)>;
 143      IsBlockMutatedFn m_check_block_mutated_mock{nullptr};
 144  
 145      explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
 146  
 147      // extra_txn is a list of extra transactions to look at, in <witness hash, reference> form
 148      ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<Wtxid, CTransactionRef>>& extra_txn);
 149      bool IsTxAvailable(size_t index) const;
 150      // segwit_active enforces witness mutation checks just before reporting a healthy status
 151      ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing, bool segwit_active);
 152  };
 153  
 154  #endif // BITCOIN_BLOCKENCODINGS_H
 155