Kagome
Polkadot Runtime Engine in C++17
state_protocol_observer_impl.cpp
Go to the documentation of this file.
1 
7 #include <boost/algorithm/string/predicate.hpp>
8 #include <boost/bind/storage.hpp>
9 #include <libp2p/outcome/outcome.hpp>
10 
12 #include "common/buffer.hpp"
16 #include "storage/trie/types.hpp"
17 
18 constexpr unsigned MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
19 
22  e) {
24  switch (e) {
25  case E::INVALID_CHILD_ROOTHASH:
26  return "Expected child root hash prefix.";
27  case E::NOTFOUND_CHILD_ROOTHASH:
28  return "Child storage root hash not found.";
29  }
30  return "unknown error";
31 }
32 
33 namespace kagome::network {
34 
36  std::shared_ptr<blockchain::BlockHeaderRepository> blocks_headers,
37  std::shared_ptr<storage::trie::TrieStorage> storage)
38  : blocks_headers_(std::move(blocks_headers)),
39  storage_{std::move(storage)},
40  log_(log::createLogger("StateProtocolObserver", "network")) {
41  BOOST_ASSERT(blocks_headers_);
42  BOOST_ASSERT(storage_);
43  }
44 
45  outcome::result<std::pair<KeyValueStateEntry, size_t>>
47  const common::Buffer &key,
48  size_t limit) const {
49  OUTCOME_TRY(batch, storage_->getEphemeralBatchAt(hash));
50 
51  auto cursor = batch->trieCursor();
52 
53  KeyValueStateEntry entry;
54  entry.state_root = hash;
55 
56  auto res = key.empty() ? cursor->next() : cursor->seekUpperBound(key);
57  size_t size = 0;
58  if (res.has_value()) {
59  while (cursor->key().has_value() && size < limit) {
60  if (auto value_res = batch->tryGet(cursor->key().value());
61  value_res.has_value()) {
62  const auto &value_opt = value_res.value();
63  if (value_opt.has_value()) {
64  entry.entries.emplace_back(
65  StateEntry{cursor->key().value(), value_opt.value()});
66  size += entry.entries.back().key.size()
67  + entry.entries.back().value.size();
68  }
69  }
70  res = cursor->next();
71  }
72  entry.complete = not cursor->key().has_value();
73  } else {
74  return res.as_failure();
75  }
76 
77  return {entry, size};
78  }
79 
80  outcome::result<network::StateResponse>
82  OUTCOME_TRY(header, blocks_headers_->getBlockHeader(request.hash));
83  OUTCOME_TRY(batch, storage_->getEphemeralBatchAt(header.state_root));
84 
85  auto cursor = batch->trieCursor();
86  // if key is not empty continue iteration from place where left
87  auto res = (request.start.empty() || request.start[0].empty()
88  ? cursor->next()
89  : cursor->seekUpperBound(request.start[0]));
90  unsigned size = 0;
91  KeyValueStateEntry entry;
92  // main state storage hash is marked with zeros in response
93  entry.state_root =
94  storage::trie::RootHash::fromSpan(std::vector<uint8_t>(32, 0)).value();
95 
96  StateResponse response;
97  response.entries.emplace_back(std::move(entry));
98 
99  // First key would contain main state storage key (child state storage hash)
100  // Second key is child state storage key
101  if (request.start.size() == 2) {
102  const auto &parent_key = request.start[0];
103  const auto &child_prefix = storage::kChildStorageDefaultPrefix;
104  if (!boost::starts_with(parent_key, child_prefix)) {
106  }
107  if (auto value_res = batch->tryGet(parent_key);
108  value_res.has_value() && value_res.value().has_value()) {
109  OUTCOME_TRY(
110  hash,
111  storage::trie::RootHash::fromSpan(value_res.value().value().get()));
112  OUTCOME_TRY(
113  entry_res,
114  this->getEntry(hash, request.start[1], MAX_RESPONSE_BYTES - size));
115  response.entries.emplace_back(std::move(entry_res.first));
116  size += entry_res.second;
117  } else {
119  }
120  }
121 
122  if (!res.has_value()) {
123  return res.as_failure();
124  }
125 
126  const auto &child_prefix = storage::kChildStorageDefaultPrefix;
127  while (cursor->key().has_value() && size < MAX_RESPONSE_BYTES) {
128  if (auto value_res = batch->tryGet(cursor->key().value());
129  value_res.has_value()) {
130  const auto &value = value_res.value();
131  auto &entry = response.entries.front();
132  entry.entries.emplace_back(
133  StateEntry{cursor->key().value(), value.value()});
134  size +=
135  entry.entries.back().key.size() + entry.entries.back().value.size();
136  // if key is child state storage hash iterate child storage keys
137  if (boost::starts_with(cursor->key().value(), child_prefix)) {
138  OUTCOME_TRY(hash,
140  value_res.value().value().get()));
141  OUTCOME_TRY(entry_res,
142  this->getEntry(
143  hash, common::Buffer(), MAX_RESPONSE_BYTES - size));
144  response.entries.emplace_back(std::move(entry_res.first));
145  size += entry_res.second;
146  // not complete means response bytes limit exceeded
147  // finish response formation
148  if (not entry_res.first.complete) {
149  break;
150  }
151  }
152  }
153  res = cursor->next();
154  }
155  response.entries.front().complete = not cursor->key().has_value();
156 
157  return response;
158  }
159 } // namespace kagome::network
std::vector< common::Buffer > start
primitives::BlockHash hash
Block header hash.
Class represents arbitrary (including empty) byte buffer.
Definition: buffer.hpp:29
A key value state.
STL namespace.
StateProtocolObserverImpl(std::shared_ptr< blockchain::BlockHeaderRepository > blocks_headers, std::shared_ptr< storage::trie::TrieStorage > storage)
std::vector< StateEntry > entries
A collection of keys-values.
A key-value pair.
const common::Buffer kChildStorageDefaultPrefix
storage::trie::RootHash state_root
std::shared_ptr< storage::trie::TrieStorage > storage_
static outcome::result< Blob< size_ > > fromSpan(const gsl::span< const uint8_t > &span)
Definition: blob.hpp:208
outcome::result< std::pair< KeyValueStateEntry, size_t > > getEntry(const storage::trie::RootHash &hash, const common::Buffer &key, size_t limit) const
outcome::result< StateResponse > onStateRequest(const StateRequest &request) const override
std::shared_ptr< blockchain::BlockHeaderRepository > blocks_headers_
constexpr unsigned MAX_RESPONSE_BYTES
Logger createLogger(const std::string &tag)
Definition: logger.cpp:112
std::vector< KeyValueStateEntry > entries
OUTCOME_CPP_DEFINE_CATEGORY(kagome::network, StateProtocolObserverImpl::Error, e)
bool complete
Set to true when there are no more keys to return.