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