blockencodings.h raw

   1  // Copyright (c) 2016-2022 The Limenka 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 LIMENKA_BLOCKENCODINGS_H
   6  #define LIMENKA_BLOCKENCODINGS_H
   7  
   8  #include <primitives/block.h>
   9  
  10  #include <functional>
  11  
  12  class CTxMemPool;
  13  class BlockValidationState;
  14  namespace Consensus {
  15  struct Params;
  16  };
  17  
  18  // Transaction compression schemes for compact block relay can be introduced by writing
  19  // an actual formatter here.
  20  using TransactionCompression = DefaultFormatter;
  21  
  22  class DifferenceFormatter
  23  {
  24      uint64_t m_shift = 0;
  25  
  26  public:
  27      template<typename Stream, typename I>
  28      void Ser(Stream& s, I v)
  29      {
  30          if (v < m_shift || v >= std::numeric_limits<uint64_t>::max()) throw std::ios_base::failure("differential value overflow");
  31          WriteCompactSize(s, v - m_shift);
  32          m_shift = uint64_t(v) + 1;
  33      }
  34      template<typename Stream, typename I>
  35      void Unser(Stream& s, I& v)
  36      {
  37          uint64_t n = ReadCompactSize(s);
  38          m_shift += n;
  39          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");
  40          v = I(m_shift++);
  41      }
  42  };
  43  
  44  class BlockTransactionsRequest {
  45  public:
  46      // A BlockTransactionsRequest message
  47      uint256 blockhash;
  48      std::vector<uint16_t> indexes;
  49  
  50      SERIALIZE_METHODS(BlockTransactionsRequest, obj)
  51      {
  52          READWRITE(obj.blockhash, Using<VectorFormatter<DifferenceFormatter>>(obj.indexes));
  53      }
  54  };
  55  
  56  class BlockTransactions {
  57  public:
  58      // A BlockTransactions message
  59      uint256 blockhash;
  60      std::vector<CTransactionRef> txn;
  61  
  62      BlockTransactions() = default;
  63      explicit BlockTransactions(const BlockTransactionsRequest& req) :
  64          blockhash(req.blockhash), txn(req.indexes.size()) {}
  65  
  66      SERIALIZE_METHODS(BlockTransactions, obj)
  67      {
  68          READWRITE(obj.blockhash, TX_WITH_WITNESS(Using<VectorFormatter<TransactionCompression>>(obj.txn)));
  69      }
  70  };
  71  
  72  // Dumb serialization/storage-helper for CBlockHeaderAndShortTxIDs and PartiallyDownloadedBlock
  73  struct PrefilledTransaction {
  74      // Used as an offset since last prefilled tx in CBlockHeaderAndShortTxIDs,
  75      // as a proper transaction-in-block-index in PartiallyDownloadedBlock
  76      uint16_t index;
  77      CTransactionRef tx;
  78  
  79      SERIALIZE_METHODS(PrefilledTransaction, obj) { READWRITE(COMPACTSIZE(obj.index), TX_WITH_WITNESS(Using<TransactionCompression>(obj.tx))); }
  80  };
  81  
  82  typedef enum ReadStatus_t
  83  {
  84      READ_STATUS_OK,
  85      READ_STATUS_INVALID, // Invalid object, peer is sending bogus crap
  86      READ_STATUS_FAILED, // Failed to process object
  87  } ReadStatus;
  88  
  89  class CBlockHeaderAndShortTxIDs {
  90  private:
  91      mutable uint64_t shorttxidk0, shorttxidk1;
  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, const 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 orphan/conflicted/etc transactions to look at
 148      ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<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 // LIMENKA_BLOCKENCODINGS_H
 155