Kagome
Polkadot Runtime Engine in C++17
child_storage_extension.cpp
Go to the documentation of this file.
1 
7 
11 #include "runtime/ptr_size.hpp"
13 #include "scale/encode_append.hpp"
16 
17 #include <tuple>
18 #include <utility>
19 
23 
24 namespace kagome::host_api {
26  std::shared_ptr<runtime::TrieStorageProvider> storage_provider,
27  std::shared_ptr<const runtime::MemoryProvider> memory_provider)
28  : storage_provider_(std::move(storage_provider)),
29  memory_provider_(std::move(memory_provider)),
30  logger_{log::createLogger("ChildStorageExtension",
31  "child_storage_extension")} {
32  BOOST_ASSERT_MSG(storage_provider_ != nullptr, "storage batch is nullptr");
33  BOOST_ASSERT_MSG(memory_provider_ != nullptr, "memory provider is nullptr");
34  }
35 
36  outcome::result<Buffer> make_prefixed_child_storage_key(
37  const Buffer &child_storage_key) {
38  return Buffer{storage::kChildStorageDefaultPrefix}.put(child_storage_key);
39  }
40 
41  template <typename R, typename F, typename... Args>
43  const Buffer &child_storage_key, F func, Args &&...args) const {
44  OUTCOME_TRY(prefixed_child_key,
45  make_prefixed_child_storage_key(child_storage_key));
46  OUTCOME_TRY(child_batch,
47  storage_provider_->getChildBatchAt(prefixed_child_key));
48 
49  auto result = func(child_batch, std::forward<Args>(args)...);
50  if (not result) {
51  storage_provider_->clearChildBatches();
52  return result;
53  }
54 
55  OUTCOME_TRY(new_child_root, child_batch->commit());
56  OUTCOME_TRY(storage_provider_->getCurrentBatch()->put(
57  prefixed_child_key, Buffer{scale::encode(new_child_root).value()}));
58  SL_TRACE(logger_,
59  "Update child trie root: prefix is {}, new root is",
60  child_storage_key,
61  new_child_root);
62  storage_provider_->clearChildBatches();
63  if constexpr (!std::is_void_v<R>) {
64  return result;
65  } else {
66  return outcome::success();
67  }
68  }
69 
70  template <typename Arg>
71  auto loadBuffer(runtime::Memory &memory, Arg &&span) {
72  auto [span_ptr, span_size] = runtime::PtrSize(span);
73  return memory.loadN(span_ptr, span_size);
74  }
75 
76  template <typename... Args>
77  auto loadBuffer(runtime::Memory &memory, Args &&...spans) {
78  return std::make_tuple(loadBuffer(memory, std::forward<Args>(spans))...);
79  }
80 
82  runtime::WasmSpan child_storage_key,
84  runtime::WasmSpan value) {
85  auto &memory = memory_provider_->getCurrentMemory()->get();
86  auto [child_key_buffer, key_buffer, value_buffer] =
87  loadBuffer(memory, child_storage_key, key, value);
88 
90  logger_, child_key_buffer, key_buffer, value_buffer);
91 
92  auto result = executeOnChildStorage<void>(
93  child_key_buffer,
94  [](auto &child_batch, auto &key, auto &value) {
95  return child_batch->put(key, value);
96  },
97  key_buffer,
98  value_buffer);
99 
100  if (not result) {
101  logger_->error(
102  "ext_default_child_storage_set_version_1 failed with reason: {}",
103  result.error().message());
104  }
105  }
106 
109  runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const {
110  auto &memory = memory_provider_->getCurrentMemory()->get();
111  auto child_key_buffer = loadBuffer(memory, child_storage_key);
112  auto key_buffer = loadBuffer(memory, key);
113 
114  SL_TRACE_VOID_FUNC_CALL(logger_, child_key_buffer, key_buffer);
115 
116  return executeOnChildStorage<runtime::WasmSpan>(
117  child_key_buffer,
118  [&memory, &child_key_buffer, &key_buffer, this](
119  auto &child_batch, auto &key) {
120  constexpr auto error_message =
121  "ext_default_child_storage_get_version_1( {}, {} ) => "
122  "value was not "
123  "obtained. Reason: {}";
124 
125  auto result = child_batch->tryGet(key);
126  if (result) {
128  logger_, result.value(), child_key_buffer, key_buffer);
129  } else {
130  logger_->error(error_message,
131  child_key_buffer.toHex(),
132  key_buffer.toHex(),
133  result.error().message());
134  }
135  // okay to throw, we want to end this runtime call with error
136  return memory.storeBuffer(
137  scale::encode(result.value()).value());
138  },
139  key_buffer)
140  .value();
141  }
142 
144  runtime::WasmSpan child_storage_key, runtime::WasmSpan key) {
145  auto &memory = memory_provider_->getCurrentMemory()->get();
146  auto [child_key_buffer, key_buffer] =
147  loadBuffer(memory, child_storage_key, key);
148 
149  SL_TRACE_VOID_FUNC_CALL(logger_, child_key_buffer, key_buffer);
150 
151  auto result = executeOnChildStorage<void>(
152  child_key_buffer,
153  [](auto &child_batch, auto &key) { return child_batch->remove(key); },
154  key_buffer);
155 
156  if (not result) {
157  logger_->error(
158  "ext_default_child_storage_clear_version_1 failed, due to fail in "
159  "trie "
160  "db with reason: {}",
161  result.error().message());
162  }
163  }
164 
167  runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const {
168  static constexpr runtime::WasmSpan kErrorSpan = -1;
169  auto &memory = memory_provider_->getCurrentMemory()->get();
170  auto [child_key_buffer, key_buffer] =
171  loadBuffer(memory, child_storage_key, key);
172  auto prefixed_child_key =
173  make_prefixed_child_storage_key(child_key_buffer).value();
174 
175  SL_TRACE_VOID_FUNC_CALL(logger_, child_key_buffer, key_buffer);
176 
177  auto child_batch_outcome =
178  storage_provider_->getChildBatchAt(prefixed_child_key);
179  if (child_batch_outcome.has_error()) {
180  logger_->error(
181  "ext_default_child_storage_next_key_version_1 resulted with error: "
182  "{}",
183  child_batch_outcome.error().message());
184  storage_provider_->clearChildBatches();
185  return kErrorSpan;
186  }
187  auto child_batch = child_batch_outcome.value();
188  auto cursor = child_batch->trieCursor();
189  storage_provider_->clearChildBatches();
190 
191  auto seek_result = cursor->seekUpperBound(key_buffer);
192  if (seek_result.has_error()) {
193  logger_->error(
194  "ext_default_child_storage_next_key_version_1 resulted with error: "
195  "{}",
196  seek_result.error().message());
197  return kErrorSpan;
198  }
199  auto next_key_opt = cursor->key();
200  if (auto enc_res = scale::encode(next_key_opt); enc_res.has_value()) {
202  next_key_opt.has_value() ? next_key_opt.value()
203  : Buffer().put("no value"),
204  child_key_buffer,
205  key_buffer);
206  return memory.storeBuffer(enc_res.value());
207  } else { // NOLINT(readability-else-after-return)
208  logger_->error(
209  "ext_default_child_storage_next_key_version_1 result encoding "
210  "resulted with error: {}",
211  enc_res.error().message());
212  }
213  return kErrorSpan;
214  }
215 
218  runtime::WasmSpan child_storage_key) const {
219  outcome::result<storage::trie::RootHash> res{{}};
220  auto &memory = memory_provider_->getCurrentMemory()->get();
221  auto child_key_buffer = loadBuffer(memory, child_storage_key);
222  auto prefixed_child_key = make_prefixed_child_storage_key(child_key_buffer);
223 
224  if (auto child_batch =
225  storage_provider_->getChildBatchAt(prefixed_child_key.value());
226  child_batch.has_value() and child_batch.value() != nullptr) {
227  res = child_batch.value()->commit();
228  } else {
229  logger_->warn(
230  "ext_default_child_storage_root called in an ephemeral extension");
231  res = storage_provider_->forceCommit();
232  storage_provider_->clearChildBatches();
233  }
234  if (res.has_error()) {
235  logger_->error(
236  "ext_default_child_storage_root resulted with an error: {}",
237  res.error().message());
238  }
239  const auto &root = res.value();
240  SL_TRACE_FUNC_CALL(logger_, root, child_key_buffer);
241  return memory.storeBuffer(root);
242  }
243 
245  runtime::WasmSpan child_storage_key, runtime::WasmSpan prefix) {
246  auto &memory = memory_provider_->getCurrentMemory()->get();
247  auto [child_key_buffer, prefix_buffer] =
248  loadBuffer(memory, child_storage_key, prefix);
249 
250  SL_TRACE_VOID_FUNC_CALL(logger_, child_key_buffer, prefix);
251 
252  auto result = executeOnChildStorage<std::tuple<bool, uint32_t>>(
253  child_key_buffer,
254  [](auto &child_batch, auto &prefix) {
255  return child_batch->clearPrefix(prefix, std::nullopt);
256  },
257  prefix_buffer);
258 
259  if (not result) {
260  logger_->error(
261  "ext_default_child_storage_clear_prefix_version_1 failed with "
262  "reason: {}",
263  result.error().message());
264  }
265  }
266 
269  runtime::WasmSpan child_storage_key,
270  runtime::WasmSpan key,
271  runtime::WasmSpan value_out,
272  runtime::WasmOffset offset) const {
273  auto &memory = memory_provider_->getCurrentMemory()->get();
274  auto [child_key_buffer, key_buffer] =
275  loadBuffer(memory, child_storage_key, key);
276  auto [value_ptr, value_size] = runtime::PtrSize(value_out);
277 
278  auto value = executeOnChildStorage<std::optional<common::Buffer>>(
279  child_key_buffer,
280  [](auto &child_batch, auto &key) {
282  child_batch->tryGet(key),
283  [](auto &v) -> common::Buffer { return v.get(); });
284  },
285  key_buffer);
286  std::optional<uint32_t> res{std::nullopt};
287  if (auto data_opt_res = value; data_opt_res.has_value()) {
288  auto &data_opt = data_opt_res.value();
289  if (data_opt.has_value()) {
290  common::BufferView data = data_opt.value();
291  data = data.subspan(std::min<size_t>(offset, data.size()));
292  auto written = std::min<size_t>(data.size(), value_size);
293  memory.storeBuffer(value_ptr, data.subspan(0, written));
294  res = data.size();
295 
297  data,
298  child_key_buffer,
299  key,
300  common::Buffer{data.subspan(0, written)});
301  } else {
303  std::string_view{"none"},
304  child_key_buffer,
305  key,
306  value_out,
307  offset);
308  }
309  } else {
310  SL_ERROR(logger_,
311  "Error in ext_storage_read_version_1: {}",
312  data_opt_res.error().message());
313  throw std::runtime_error{data_opt_res.error().message()};
314  }
315 
316  return memory.storeBuffer(scale::encode(res).value());
317  }
318 
320  runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const {
321  auto &memory = memory_provider_->getCurrentMemory()->get();
322  auto [child_key_buffer, key_buffer] =
323  loadBuffer(memory, child_storage_key, key);
324 
325  SL_TRACE_VOID_FUNC_CALL(logger_, child_key_buffer, key_buffer);
326 
327  auto res = executeOnChildStorage<bool>(
328  child_key_buffer,
329  [](auto &child_batch, auto &key) { return child_batch->contains(key); },
330  key_buffer);
331 
332  if (not res) {
333  logger_->error(
334  "ext_default_child_storage_exists_version_1 failed with "
335  "reason: {}",
336  res.error().message());
337  }
338 
339  return (res.has_value() and res.value()) ? 1 : 0;
340  }
341 
343  runtime::WasmSpan child_storage_key) {
344  auto &memory = memory_provider_->getCurrentMemory()->get();
345  auto child_key_buffer = loadBuffer(memory, child_storage_key);
346 
347  SL_TRACE_VOID_FUNC_CALL(logger_, child_key_buffer);
348 
349  auto result = executeOnChildStorage<std::tuple<bool, uint32_t>>(
350  child_key_buffer, [](auto &child_batch) {
351  return child_batch->clearPrefix({}, std::nullopt);
352  });
353 
354  if (not result) {
355  logger_->error(
356  "ext_default_child_storage_storage_kill_version_1 failed with "
357  "reason: {}",
358  result.error().message());
359  }
360  }
361 
362 } // namespace kagome::host_api
runtime::WasmSpan ext_default_child_storage_get_version_1(runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const
Class represents arbitrary (including empty) byte buffer.
Definition: buffer.hpp:29
runtime::WasmSpan ext_default_child_storage_next_key_version_1(runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const
outcome::result< Buffer > make_prefixed_child_storage_key(const Buffer &child_storage_key)
runtime::WasmSpan ext_default_child_storage_read_version_1(runtime::WasmSpan child_storage_key, runtime::WasmSpan key, runtime::WasmSpan value_out, runtime::WasmOffset offset) const
#define SL_TRACE_FUNC_CALL(logger, ret,...)
Definition: logger.hpp:142
STL namespace.
uint32_t WasmOffset
Offset type is uint32_t because we are working in 32 bit address space.
Definition: types.hpp:44
virtual common::Buffer loadN(WasmPointer addr, WasmSize n) const =0
void ext_default_child_storage_storage_kill_version_1(runtime::WasmSpan child_storage_key)
auto loadBuffer(runtime::Memory &memory, Arg &&span)
const common::Buffer kChildStorageDefaultPrefix
SLBuffer< std::numeric_limits< size_t >::max()> Buffer
Definition: buffer.hpp:244
void ext_default_child_storage_set_version_1(runtime::WasmSpan child_storage_key, runtime::WasmSpan key, runtime::WasmSpan value)
SLBuffer & put(std::string_view view)
Put a string into byte buffer.
Definition: buffer.hpp:117
std::shared_ptr< const runtime::MemoryProvider > memory_provider_
runtime::WasmSpan ext_default_child_storage_root_version_1(runtime::WasmSpan child_storage_key) const
uint64_t WasmSpan
combination of pointer and size, where less significant part represents wasm pointer, and most significant represents size
Definition: types.hpp:31
ChildStorageExtension(std::shared_ptr< runtime::TrieStorageProvider > storage_provider, std::shared_ptr< const runtime::MemoryProvider > memory_provider)
uint32_t ext_default_child_storage_exists_version_1(runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const
void ext_default_child_storage_clear_prefix_version_1(runtime::WasmSpan child_storage_key, runtime::WasmSpan prefix)
std::reference_wrapper< const Buffer > BufferConstRef
Definition: buffer.hpp:249
outcome::result< R > executeOnChildStorage(const common::Buffer &child_storage_key, F func, Args &&...args) const
TrieError
TrieDbError enum provides error codes for TrieDb methods.
Definition: trie_error.hpp:15
Logger createLogger(const std::string &tag)
Definition: logger.cpp:112
#define SL_TRACE_VOID_FUNC_CALL(logger,...)
Definition: logger.hpp:146
void ext_default_child_storage_clear_version_1(runtime::WasmSpan child_storage_key, runtime::WasmSpan key)
std::shared_ptr< runtime::TrieStorageProvider > storage_provider_
outcome::result< std::optional< R > > map_result_optional(outcome::result< std::optional< T >> const &res_opt, F const &f)