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