Kagome
Polkadot Runtime Engine in C++17
polkadot_codec.cpp
Go to the documentation of this file.
1 
7 
9 #include "scale/scale.hpp"
10 #include "scale/scale_decoder_stream.hpp"
12 
15  switch (e) {
16  case E::SUCCESS:
17  return "success";
18  case E::TOO_MANY_NIBBLES:
19  return "number of nibbles in key is >= 2**16";
20  case E::UNKNOWN_NODE_TYPE:
21  return "unknown polkadot node type";
22  case E::INPUT_TOO_SMALL:
23  return "not enough bytes in the input to decode a node";
24  case E::NO_NODE_VALUE:
25  return "no value in leaf node";
26  }
27 
28  return "unknown";
29 }
30 
31 namespace kagome::storage::trie {
32 
33  inline common::Buffer ushortToBytes(uint16_t b) {
34  common::Buffer out(2, 0);
35  out[1] = (b >> 8u) & 0xffu;
36  out[0] = b & 0xffu;
37  return out;
38  }
39 
41  const common::BufferView &buf) const {
42  // if a buffer size is less than the size of a would-be hash, just return
43  // this buffer to save space
44  if (static_cast<size_t>(buf.size()) < common::Hash256::size()) {
45  return common::Buffer{buf};
46  }
47 
48  return Buffer{hash256(buf)};
49  }
50 
52  const auto size = static_cast<size_t>(buf.size());
53  BOOST_ASSERT(size <= common::Hash256::size());
54  return size == common::Hash256::size();
55  }
56 
58  common::Hash256 out;
59 
60  BOOST_VERIFY(crypto::blake2b(out.data(),
62  nullptr,
63  0,
64  buf.data(),
65  buf.size())
66  == EXIT_SUCCESS);
67  return out;
68  }
69 
70  outcome::result<common::Buffer> PolkadotCodec::encodeNodeAndStoreChildren(
71  const Node &node, const StoreChildren &store_children) const {
72  switch (static_cast<TrieNode::Type>(node.getType())) {
74  return encodeLeaf(dynamic_cast<const LeafNode &>(node));
75 
78  return encodeBranch(dynamic_cast<const BranchNode &>(node),
79  store_children);
80 
82  return encodeLeaf(dynamic_cast<const LeafNode &>(node));
83 
85  return encodeBranch(dynamic_cast<const BranchNode &>(node),
86  store_children);
87 
89  return std::errc::invalid_argument;
90 
92  return std::errc::invalid_argument;
93 
95  // special node is not handled right now
96  return std::errc::invalid_argument;
97 
98  default:
99  return std::errc::invalid_argument;
100  }
101  }
102 
103  outcome::result<common::Buffer> PolkadotCodec::encodeHeader(
104  const TrieNode &node) const {
105  if (node.key_nibbles.size() > 0xffffu) {
107  }
108 
109  uint8_t head;
110  uint8_t partial_length_mask; // max partial key length
111 
112  // set bits of type
113  switch (node.getTrieType()) {
115  head = 0b01'000000;
116  partial_length_mask = 0b00'111111; // 63
117  break;
119  head = 0b10'000000;
120  partial_length_mask = 0b00'111111; // 63
121  break;
123  head = 0b11'000000;
124  partial_length_mask = 0b00'111111; // 63
125  break;
127  head = 0b001'00000;
128  partial_length_mask = 0b000'11111; // 31
129  break;
131  head = 0b0001'0000;
132  partial_length_mask = 0b0000'1111; // 15
133  break;
135  head = 0b00000000;
136  partial_length_mask = 0;
137  break;
139  head = 0b00010000;
140  partial_length_mask = 0;
141  break;
142  default:
144  }
145 
146  // set bits of partial key length
147  if (node.key_nibbles.size() < partial_length_mask) {
148  head |= uint8_t(node.key_nibbles.size());
149  return Buffer{head}; // header contains 1 byte
150  }
151  // if partial key length is greater than max partial key length, then the
152  // rest of the length is stored in consequent bytes
153  head += partial_length_mask;
154 
155  size_t l = node.key_nibbles.size() - partial_length_mask;
156  Buffer out(1u + // 1 byte head
157  l / 0xffu + // number of 255 in l
158  1u, // for last byte
159  0xffu // fill array with 255
160  );
161 
162  // first byte is head
163  out[0] = head;
164  // last byte after while(l>255) l-=255;
165  out[out.size() - 1] = l % 0xffu;
166 
167  return out;
168  }
169 
170  outcome::result<common::Buffer> PolkadotCodec::encodeBranch(
171  const BranchNode &node, const StoreChildren &store_children) const {
172  // node header
173  OUTCOME_TRY(encoding, encodeHeader(node));
174 
175  // key
176  encoding += node.key_nibbles.toByteBuffer();
177 
178  // children bitmap
179  encoding += ushortToBytes(node.childrenBitmap());
180 
182  // scale encoded value
183  OUTCOME_TRY(encNodeValue, scale::encode(node.value.value()));
184  encoding += Buffer(std::move(encNodeValue));
185  }
186 
187  // encode each child
188  for (auto &child : node.children) {
189  if (child) {
190  if (auto dummy = std::dynamic_pointer_cast<DummyNode>(child);
191  dummy != nullptr) {
192  auto merkle_value = dummy->db_key;
193  OUTCOME_TRY(scale_enc, scale::encode(std::move(merkle_value)));
194  encoding.put(scale_enc);
195  } else {
196  OUTCOME_TRY(enc, encodeNodeAndStoreChildren(*child, store_children));
197  auto merkle = merkleValue(enc);
198  if (isMerkleHash(merkle)) {
199  OUTCOME_TRY(store_children(merkle, std::move(enc)));
200  }
201  OUTCOME_TRY(scale_enc, scale::encode(merkle));
202  encoding.put(scale_enc);
203  }
204  }
205  }
206 
207  return outcome::success(std::move(encoding));
208  }
209 
210  outcome::result<common::Buffer> PolkadotCodec::encodeLeaf(
211  const LeafNode &node) const {
212  OUTCOME_TRY(encoding, encodeHeader(node));
213 
214  // key
215  encoding += node.key_nibbles.toByteBuffer();
216 
217  if (!node.value) return Error::NO_NODE_VALUE;
218  // scale encoded value
219  OUTCOME_TRY(encNodeValue, scale::encode(node.value.value()));
220  encoding += Buffer(std::move(encNodeValue));
221 
222  return outcome::success(std::move(encoding));
223  }
224 
225  outcome::result<std::shared_ptr<Node>> PolkadotCodec::decodeNode(
226  gsl::span<const uint8_t> encoded_data) const {
227  BufferStream stream{encoded_data};
228  // decode the header with the node type and the partial key length
229  OUTCOME_TRY(header, decodeHeader(stream));
230  auto [type, pk_length] = header;
231  // decode the partial key
232  OUTCOME_TRY(partial_key, decodePartialKey(pk_length, stream));
233  // decode the node sub-value (see Definition 28 of the polkadot
234  // specification)
235  switch (type) {
236  case TrieNode::Type::Leaf: {
237  OUTCOME_TRY(value, scale::decode<Buffer>(stream.leftBytes()));
238  return std::make_shared<LeafNode>(partial_key, value);
239  }
240 
243  return decodeBranch(type, partial_key, stream);
244 
246  OUTCOME_TRY(value, scale::decode<Buffer>(stream.leftBytes()));
247  return std::make_shared<LeafContainingHashesNode>(partial_key, value);
248  }
249 
251  return decodeBranch(type, partial_key, stream);
252 
255 
258 
259  default:
261  }
262  }
263 
264  outcome::result<std::pair<TrieNode::Type, size_t>>
266  TrieNode::Type type;
267  if (not stream.hasMore(1)) {
268  return Error::INPUT_TOO_SMALL;
269  }
270  auto first = stream.next();
271 
272  uint8_t partial_key_length_mask = 0;
273  if (first & 0b1100'0000) {
274  if ((first >> 6 & 0b11) == 0b01) {
275  type = TrieNode::Type::Leaf;
276  } else if ((first >> 6 & 0b11) == 0b10) {
277  type = TrieNode::Type::BranchEmptyValue;
278  } else if ((first >> 6 & 0b11) == 0b11) {
279  type = TrieNode::Type::BranchWithValue;
280  } else {
281  BOOST_UNREACHABLE_RETURN();
282  }
283  partial_key_length_mask = 0b00'111111;
284  } else if (first & 0b0010'0000) {
285  type = TrieNode::Type::LeafContainingHashes;
286  partial_key_length_mask = 0b000'11111;
287  } else if (first & 0b0001'0000) {
288  if (first & 0b0000'1111) {
290  partial_key_length_mask = 0b0000'1111;
291  } else {
292  type = TrieNode::Type::ReservedForCompactEncoding;
293  }
294  } else if (first == 0) {
295  type = TrieNode::Type::Empty;
296  } else {
297  BOOST_UNREACHABLE_RETURN();
298  }
299 
300  // decode partial key length, which is stored in the last bits and,
301  // if it's greater than max partial key length, in the following bytes
302  size_t pk_length = first & partial_key_length_mask;
303 
304  if (pk_length == partial_key_length_mask) {
305  uint8_t read_length = 0;
306  do {
307  if (not stream.hasMore(1)) {
308  return Error::INPUT_TOO_SMALL;
309  }
310  read_length = stream.next();
311  pk_length += read_length;
312  } while (read_length == 0xFF);
313  }
314  return std::make_pair(type, pk_length);
315  }
316 
317  outcome::result<KeyNibbles> PolkadotCodec::decodePartialKey(
318  size_t nibbles_num, BufferStream &stream) const {
319  // length in bytes is length in nibbles over two round up
320  auto byte_length = nibbles_num / 2 + nibbles_num % 2;
321  Buffer partial_key;
322  partial_key.reserve(byte_length);
323  while (byte_length-- != 0) {
324  if (not stream.hasMore(1)) {
325  return Error::INPUT_TOO_SMALL;
326  }
327  partial_key.putUint8(stream.next());
328  }
329  // array of nibbles is much more convenient than array of bytes, though it
330  // wastes some memory
331  auto partial_key_nibbles = KeyNibbles::fromByteBuffer(partial_key);
332  if (nibbles_num % 2 == 1) {
333  partial_key_nibbles = partial_key_nibbles.subbuffer(1);
334  }
335  return partial_key_nibbles;
336  }
337 
338  outcome::result<std::shared_ptr<Node>> PolkadotCodec::decodeBranch(
339  TrieNode::Type type,
340  const KeyNibbles &partial_key,
341  BufferStream &stream) const {
342  constexpr uint8_t kChildrenBitmapSize = 2;
343 
344  if (not stream.hasMore(kChildrenBitmapSize)) {
345  return Error::INPUT_TOO_SMALL;
346  }
347  auto node = std::make_shared<BranchNode>(partial_key);
348 
349  uint16_t children_bitmap = stream.next();
350  children_bitmap += stream.next() << 8u;
351 
352  scale::ScaleDecoderStream ss(stream.leftBytes());
353 
354  // decode the branch value if needed
355  common::Buffer value;
356  if (type == TrieNode::Type::BranchWithValue) {
357  try {
358  ss >> value;
359  } catch (std::system_error &e) {
360  return outcome::failure(e.code());
361  }
362  node->value = value;
363  }
364 
365  uint8_t i = 0;
366  while (children_bitmap != 0) {
367  // if there is a child
368  if ((children_bitmap & (1u << i)) != 0) {
369  // unset bit for this child
370  children_bitmap &= ~(1u << i);
371  // read the hash of the child and make a dummy node from it for this
372  // child in the processed branch
373  common::Buffer child_hash;
374  try {
375  ss >> child_hash;
376  } catch (std::system_error &e) {
377  return outcome::failure(e.code());
378  }
379  node->children.at(i) = std::make_shared<DummyNode>(child_hash);
380  }
381  i++;
382  }
383  return node;
384  }
385 
386 } // namespace kagome::storage::trie
Class represents arbitrary (including empty) byte buffer.
Definition: buffer.hpp:29
common::Buffer merkleValue(const BufferView &buf) const override
Get the merkle value of a node.
uint16_t childrenBitmap() const
Definition: trie_node.cpp:15
OUTCOME_CPP_DEFINE_CATEGORY(kagome::storage::trie, PolkadotCodec::Error, e)
std::array< std::shared_ptr< OpaqueTrieNode >, kMaxChildren > children
Definition: trie_node.hpp:141
outcome::result< Buffer > encodeHeader(const TrieNode &node) const
outcome::result< Buffer > encodeLeaf(const LeafNode &node) const
int blake2b(void *out, size_t outlen, const void *key, size_t keylen, const void *in, size_t inlen)
Definition: blake2b.cpp:190
outcome::result< Buffer > encodeBranch(const BranchNode &node, const StoreChildren &store_children) const
outcome::result< std::pair< TrieNode::Type, size_t > > decodeHeader(BufferStream &stream) const
outcome::result< KeyNibbles > decodePartialKey(size_t nibbles_num, BufferStream &stream) const
virtual int getType() const =0
bool isMerkleHash(const common::BufferView &buf) const override
is this a hash of value, or value itself
SLBuffer & reserve(size_t size)
Definition: buffer.hpp:61
common::Buffer ushortToBytes(uint16_t b)
common::Hash256 hash256(const BufferView &buf) const override
Get the hash of a node.
outcome::result< std::shared_ptr< Node > > decodeBranch(TrieNode::Type type, const KeyNibbles &partial_key, BufferStream &stream) const
gsl::span< const uint8_t > leftBytes() const
Type getTrieType() const noexcept
Definition: trie_node.hpp:109
SLBuffer & putUint8(uint8_t n)
Put a 8-bit {.
Definition: buffer.hpp:79
cannot decode a node, not enough bytes on input
static constexpr size_t size()
Definition: blob.hpp:146
std::optional< common::Buffer > value
Definition: trie_node.hpp:120
bool hasMore(index_type num_bytes) const
outcome::result< Buffer > encodeNodeAndStoreChildren(const Node &node, const StoreChildren &store_children) const override
Encode node to byte representation and store children.
outcome::result< std::shared_ptr< Node > > decodeNode(gsl::span< const uint8_t > encoded_data) const override
Decode node from bytes.
static KeyNibbles fromByteBuffer(const common::BufferView &key)
Definition: trie_node.hpp:35
std::function< outcome::result< void >(common::BufferView, common::Buffer &&)> StoreChildren
Definition: codec.hpp:21