Bitcoin ABC  0.24.7
P2P Digital Currency
asmap.cpp
Go to the documentation of this file.
1 // Copyright (c) 2019 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 <crypto/common.h>
6 
7 #include <cassert>
8 #include <map>
9 #include <vector>
10 
11 namespace {
12 
13 constexpr uint32_t INVALID = 0xFFFFFFFF;
14 
15 uint32_t DecodeBits(std::vector<bool>::const_iterator &bitpos,
16  const std::vector<bool>::const_iterator &endpos,
17  uint8_t minval, const std::vector<uint8_t> &bit_sizes) {
18  uint32_t val = minval;
19  bool bit;
20  for (std::vector<uint8_t>::const_iterator bit_sizes_it = bit_sizes.begin();
21  bit_sizes_it != bit_sizes.end(); ++bit_sizes_it) {
22  if (bit_sizes_it + 1 != bit_sizes.end()) {
23  if (bitpos == endpos) {
24  break;
25  }
26  bit = *bitpos;
27  bitpos++;
28  } else {
29  bit = 0;
30  }
31  if (bit) {
32  val += (1 << *bit_sizes_it);
33  } else {
34  for (int b = 0; b < *bit_sizes_it; b++) {
35  if (bitpos == endpos) {
36  // Reached EOF in mantissa
37  return INVALID;
38  }
39  bit = *bitpos;
40  bitpos++;
41  val += bit << (*bit_sizes_it - 1 - b);
42  }
43  return val;
44  }
45  }
46  // Reached EOF in exponent
47  return INVALID;
48 }
49 
50 enum class Instruction : uint32_t {
51  RETURN = 0,
52  JUMP = 1,
53  MATCH = 2,
54  DEFAULT = 3,
55 };
56 
57 const std::vector<uint8_t> TYPE_BIT_SIZES{0, 0, 1};
58 Instruction DecodeType(std::vector<bool>::const_iterator &bitpos,
59  const std::vector<bool>::const_iterator &endpos) {
60  return Instruction(DecodeBits(bitpos, endpos, 0, TYPE_BIT_SIZES));
61 }
62 
63 const std::vector<uint8_t> ASN_BIT_SIZES{15, 16, 17, 18, 19,
64  20, 21, 22, 23, 24};
65 uint32_t DecodeASN(std::vector<bool>::const_iterator &bitpos,
66  const std::vector<bool>::const_iterator &endpos) {
67  return DecodeBits(bitpos, endpos, 1, ASN_BIT_SIZES);
68 }
69 
70 const std::vector<uint8_t> MATCH_BIT_SIZES{1, 2, 3, 4, 5, 6, 7, 8};
71 uint32_t DecodeMatch(std::vector<bool>::const_iterator &bitpos,
72  const std::vector<bool>::const_iterator &endpos) {
73  return DecodeBits(bitpos, endpos, 2, MATCH_BIT_SIZES);
74 }
75 
76 const std::vector<uint8_t> JUMP_BIT_SIZES{5, 6, 7, 8, 9, 10, 11, 12, 13,
77  14, 15, 16, 17, 18, 19, 20, 21, 22,
78  23, 24, 25, 26, 27, 28, 29, 30};
79 uint32_t DecodeJump(std::vector<bool>::const_iterator &bitpos,
80  const std::vector<bool>::const_iterator &endpos) {
81  return DecodeBits(bitpos, endpos, 17, JUMP_BIT_SIZES);
82 }
83 
84 } // namespace
85 
86 uint32_t Interpret(const std::vector<bool> &asmap,
87  const std::vector<bool> &ip) {
88  std::vector<bool>::const_iterator pos = asmap.begin();
89  const std::vector<bool>::const_iterator endpos = asmap.end();
90  uint8_t bits = ip.size();
91  uint32_t default_asn = 0;
92  uint32_t jump, match, matchlen;
93  Instruction opcode;
94  while (pos != endpos) {
95  opcode = DecodeType(pos, endpos);
96  if (opcode == Instruction::RETURN) {
97  default_asn = DecodeASN(pos, endpos);
98  if (default_asn == INVALID) {
99  // ASN straddles EOF
100  break;
101  }
102  return default_asn;
103  } else if (opcode == Instruction::JUMP) {
104  jump = DecodeJump(pos, endpos);
105  if (jump == INVALID) {
106  // Jump offset straddles EOF
107  break;
108  }
109  if (bits == 0) {
110  // No input bits left
111  break;
112  }
113  if (pos + jump < pos) {
114  // overflow
115  break;
116  }
117  if (pos + jump >= endpos) {
118  // Jumping past EOF
119  break;
120  }
121  if (ip[ip.size() - bits]) {
122  pos += jump;
123  }
124  bits--;
125  } else if (opcode == Instruction::MATCH) {
126  match = DecodeMatch(pos, endpos);
127  if (match == INVALID) {
128  // Match bits straddle EOF
129  break;
130  }
131  matchlen = CountBits(match) - 1;
132  if (bits < matchlen) {
133  // Not enough input bits
134  break;
135  }
136  for (uint32_t bit = 0; bit < matchlen; bit++) {
137  if ((ip[ip.size() - bits]) !=
138  ((match >> (matchlen - 1 - bit)) & 1)) {
139  return default_asn;
140  }
141  bits--;
142  }
143  } else if (opcode == Instruction::DEFAULT) {
144  default_asn = DecodeASN(pos, endpos);
145  if (default_asn == INVALID) {
146  // ASN straddles EOF
147  break;
148  }
149  } else {
150  // Instruction straddles EOF
151  break;
152  }
153  }
154 
155  // Reached EOF without RETURN, or aborted (see any of the breaks above) -
156  // should have been caught by SanityCheckASMap below
157  assert(false);
158 
159  // 0 is not a valid ASN
160  return 0;
161 }
162 
163 bool SanityCheckASMap(const std::vector<bool> &asmap, int bits) {
164  const std::vector<bool>::const_iterator begin = asmap.begin(),
165  endpos = asmap.end();
166  std::vector<bool>::const_iterator pos = begin;
167  // All future positions we may jump to (bit offset in asmap -> bits to
168  // consume left)
169  std::vector<std::pair<uint32_t, int>> jumps;
170  jumps.reserve(bits);
171  Instruction prevopcode = Instruction::JUMP;
172  bool had_incomplete_match = false;
173  while (pos != endpos) {
174  uint32_t offset = pos - begin;
175  if (!jumps.empty() && offset >= jumps.back().first) {
176  // There was a jump into the middle of the previous instruction
177  return false;
178  }
179  Instruction opcode = DecodeType(pos, endpos);
180  if (opcode == Instruction::RETURN) {
181  if (prevopcode == Instruction::DEFAULT) {
182  // There should not be any RETURN immediately after a DEFAULT
183  // (could be combined into just RETURN)
184  return false;
185  }
186  uint32_t asn = DecodeASN(pos, endpos);
187  if (asn == INVALID) {
188  // ASN straddles EOF
189  return false;
190  }
191  if (jumps.empty()) {
192  // Nothing to execute anymore
193  if (endpos - pos > 7) {
194  // Excessive padding
195  return false;
196  }
197  while (pos != endpos) {
198  if (*pos) {
199  // Nonzero padding bit
200  return false;
201  }
202  ++pos;
203  }
204  // Sanely reached EOF
205  return true;
206  } else {
207  // Continue by pretending we jumped to the next instruction
208  offset = pos - begin;
209  if (offset != jumps.back().first) {
210  // Unreachable code
211  return false;
212  }
213  // Restore the number of bits we would have had left after this
214  // jump
215  bits = jumps.back().second;
216  jumps.pop_back();
217  prevopcode = Instruction::JUMP;
218  }
219  } else if (opcode == Instruction::JUMP) {
220  uint32_t jump = DecodeJump(pos, endpos);
221  if (jump == INVALID) {
222  // Jump offset straddles EOF
223  return false;
224  }
225  if (pos + jump < pos) {
226  // overflow
227  return false;
228  }
229  if (pos + jump > endpos) {
230  // Jump out of range
231  return false;
232  }
233  if (bits == 0) {
234  // Consuming bits past the end of the input
235  return false;
236  }
237  --bits;
238  uint32_t jump_offset = pos - begin + jump;
239  if (!jumps.empty() && jump_offset >= jumps.back().first) {
240  // Intersecting jumps
241  return false;
242  }
243  jumps.emplace_back(jump_offset, bits);
244  prevopcode = Instruction::JUMP;
245  } else if (opcode == Instruction::MATCH) {
246  uint32_t match = DecodeMatch(pos, endpos);
247  if (match == INVALID) {
248  // Match bits straddle EOF
249  return false;
250  }
251  int matchlen = CountBits(match) - 1;
252  if (prevopcode != Instruction::MATCH) {
253  had_incomplete_match = false;
254  }
255  if (matchlen < 8 && had_incomplete_match) {
256  // Within a sequence of matches only at most one should be
257  // incomplete
258  return false;
259  }
260  had_incomplete_match = (matchlen < 8);
261  if (bits < matchlen) {
262  // Consuming bits past the end of the input
263  return false;
264  }
265  bits -= matchlen;
266  prevopcode = Instruction::MATCH;
267  } else if (opcode == Instruction::DEFAULT) {
268  if (prevopcode == Instruction::DEFAULT) {
269  // There should not be two successive DEFAULTs (they could be
270  // combined into one)
271  return false;
272  }
273  uint32_t asn = DecodeASN(pos, endpos);
274  if (asn == INVALID) {
275  // ASN straddles EOF
276  return false;
277  }
278  prevopcode = Instruction::DEFAULT;
279  } else {
280  // Instruction straddles EOF
281  return false;
282  }
283  }
284  // Reached EOF without RETURN instruction
285  return false;
286 }
SanityCheckASMap
bool SanityCheckASMap(const std::vector< bool > &asmap, int bits)
Definition: asmap.cpp:163
Interpret
uint32_t Interpret(const std::vector< bool > &asmap, const std::vector< bool > &ip)
Definition: asmap.cpp:86
CountBits
static uint64_t CountBits(uint64_t x)
Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set.
Definition: common.h:82
BlockFilterType::INVALID
@ INVALID
common.h
VarIntMode::DEFAULT
@ DEFAULT