Kagome
Polkadot Runtime Engine in C++17
offchain_extension.cpp
Go to the documentation of this file.
1 
7 
8 #include <stdexcept>
9 #include <thread>
10 
13 #include "runtime/memory.hpp"
15 #include "runtime/ptr_size.hpp"
16 #include "scale/scale.hpp"
17 
18 namespace kagome::host_api {
19 
21  using offchain::OffchainWorker;
22  using offchain::RequestId;
24  using offchain::Timestamp;
25 
27  const OffchainExtensionConfig &config,
28  std::shared_ptr<const runtime::MemoryProvider> memory_provider,
29  std::shared_ptr<offchain::OffchainStorage> offchain_storage,
30  std::shared_ptr<offchain::OffchainWorkerPool> ocw_pool)
31  : config_(config),
32  memory_provider_(std::move(memory_provider)),
33  offchain_storage_(std::move(offchain_storage)),
34  ocw_pool_(std::move(ocw_pool)),
35  log_(log::createLogger("OffchainExtension", "offchain_extension")) {
36  BOOST_ASSERT(memory_provider_);
37  BOOST_ASSERT(offchain_storage_);
38  BOOST_ASSERT(ocw_pool_);
39  }
40 
41  std::shared_ptr<offchain::OffchainWorker> OffchainExtension::getWorker() {
42  auto worker_opt = ocw_pool_->getWorker();
43  if (not worker_opt.has_value()) {
44  throw std::runtime_error(
45  "Method was called not in offchain worker context");
46  }
47  return worker_opt.value();
48  }
49 
51  auto worker = getWorker();
52  bool isValidator = worker->isValidator();
53  return isValidator ? 1 : 0;
54  }
55 
58  runtime::WasmSpan data_pos) {
59  auto worker = getWorker();
60  auto &memory = memory_provider_->getCurrentMemory()->get();
61 
62  auto [data_ptr, data_size] = runtime::PtrSize(data_pos);
63  auto data_buffer = memory.loadN(data_ptr, data_size);
64  auto xt_res = scale::decode<primitives::Extrinsic>(data_buffer);
65  if (xt_res.has_error()) {
66  throw std::runtime_error("Invalid encoded data for transaction arg");
67  }
68  auto &xt = xt_res.value();
69 
70  auto result = worker->submitTransaction(std::move(xt));
71 
72  return memory.storeBuffer(scale::encode(result).value());
73  }
74 
76  auto worker = getWorker();
77  auto &memory = memory_provider_->getCurrentMemory()->get();
78 
79  auto result = worker->networkState();
80 
81  return memory.storeBuffer(scale::encode(result).value());
82  }
83 
85  auto worker = getWorker();
86  auto result = worker->timestamp();
87  return result;
88  }
89 
91  runtime::WasmU64 deadline) {
92  auto worker = getWorker();
93  worker->sleepUntil(deadline);
94  }
95 
97  auto worker = getWorker();
98  auto &memory = memory_provider_->getCurrentMemory()->get();
99  auto result = worker->timestamp();
100  return memory.storeBuffer(scale::encode(result).value());
101  }
102 
105  auto worker = getWorker();
106  auto &memory = memory_provider_->getCurrentMemory()->get();
107 
108  StorageType storage_type = StorageType::Undefined;
109  if (kind == 1) {
110  storage_type = StorageType::Persistent;
111  } else if (kind == 2) {
112  storage_type = StorageType::Local;
113  } else if (kind == 0) {
114  // TODO(xDimon): Remove this if-branch when it will be fixed it substrate
115  // issue: https://github.com/soramitsu/kagome/issues/997
116  storage_type = StorageType::Persistent;
117  } else {
118  throw std::invalid_argument(
119  "Method was called with unknown kind of storage");
120  }
121 
122  auto [key_ptr, key_size] = runtime::PtrSize(key);
123  auto key_buffer = memory.loadN(key_ptr, key_size);
124  auto [value_ptr, value_size] = runtime::PtrSize(value);
125  auto value_buffer = memory.loadN(value_ptr, value_size);
126 
127  worker->localStorageSet(storage_type, key_buffer, value_buffer);
128  }
129 
132  auto worker = getWorker();
133  auto &memory = memory_provider_->getCurrentMemory()->get();
134 
135  StorageType storage_type = StorageType::Undefined;
136  if (kind == 1) {
137  storage_type = StorageType::Persistent;
138  } else if (kind == 2) {
139  storage_type = StorageType::Local;
140  } else if (kind == 0) {
141  // TODO(xDimon): Remove this if-branch when it will be fixed it substrate
142  // issue: https://github.com/soramitsu/kagome/issues/997
143  storage_type = StorageType::Persistent;
144  } else {
145  throw std::invalid_argument(
146  "Method was called with unknown kind of storage");
147  }
148 
149  auto [key_ptr, key_size] = runtime::PtrSize(key);
150  auto key_buffer = memory.loadN(key_ptr, key_size);
151 
152  worker->localStorageClear(storage_type, key_buffer);
153  }
154 
157  runtime::WasmI32 kind,
158  runtime::WasmSpan key,
159  runtime::WasmSpan expected,
160  runtime::WasmSpan value) {
161  auto worker = getWorker();
162  auto &memory = memory_provider_->getCurrentMemory()->get();
163 
164  StorageType storage_type = StorageType::Undefined;
165  if (kind == 1) {
166  storage_type = StorageType::Persistent;
167  } else if (kind == 2) {
168  storage_type = StorageType::Local;
169  } else if (kind == 0) {
170  // TODO(xDimon): Remove this if-branch when it will be fixed it substrate
171  // issue: https://github.com/soramitsu/kagome/issues/997
172  storage_type = StorageType::Persistent;
173  } else {
174  throw std::invalid_argument(
175  "Method was called with unknown kind of storage");
176  }
177 
178  auto [key_ptr, key_size] = runtime::PtrSize(key);
179  auto key_buffer = memory.loadN(key_ptr, key_size);
180  auto [expected_ptr, expected_size] = runtime::PtrSize(expected);
181  auto expected_encoded = memory.loadN(expected_ptr, expected_size);
182  auto expected_res =
183  scale::decode<std::optional<common::Buffer>>(expected_encoded);
184  if (expected_res.has_error()) {
185  throw std::runtime_error("Invalid encoded data for expected arg");
186  }
187  auto &expected_as_buffer{expected_res.value()};
188  std::optional<common::BufferView> expected_as_view;
189  if (expected_as_buffer) {
190  expected_as_view.emplace(expected_as_buffer.value());
191  }
192  auto [value_ptr, value_size] = runtime::PtrSize(value);
193  auto value_buffer = memory.loadN(value_ptr, value_size);
194 
195  auto result = worker->localStorageCompareAndSet(
196  storage_type, key_buffer, expected_as_view, value_buffer);
197 
198  return result;
199  }
200 
203  auto worker = getWorker();
204  auto &memory = memory_provider_->getCurrentMemory()->get();
205 
206  StorageType storage_type = StorageType::Undefined;
207  if (kind == 1) {
208  storage_type = StorageType::Persistent;
209  } else if (kind == 2) {
210  storage_type = StorageType::Local;
211  } else if (kind == 0) {
212  // TODO(xDimon): Remove this if-branch when it will be fixed it substrate
213  // issue: https://github.com/soramitsu/kagome/issues/997
214  storage_type = StorageType::Persistent;
215  } else {
216  throw std::invalid_argument(
217  "Method was called with unknown kind of storage");
218  }
219 
220  auto [key_ptr, key_size] = runtime::PtrSize(key);
221  auto key_buffer = memory.loadN(key_ptr, key_size);
222 
223  auto result = worker->localStorageGet(storage_type, key_buffer);
224 
225  auto option = result ? std::make_optional(result.value()) : std::nullopt;
226 
227  return memory.storeBuffer(scale::encode(option).value());
228  }
229 
232  runtime::WasmSpan method_pos,
233  runtime::WasmSpan uri_pos,
234  runtime::WasmSpan meta_pos) {
235  auto worker = getWorker();
236  auto &memory = memory_provider_->getCurrentMemory()->get();
237 
238  auto [method_ptr, method_size] = runtime::PtrSize(method_pos);
239  auto method_buffer = memory.loadN(method_ptr, method_size);
240 
241  auto [uri_ptr, uri_size] = runtime::PtrSize(uri_pos);
242  auto uri_buffer = memory.loadN(uri_ptr, uri_size);
243  auto uri = uri_buffer.toString();
244 
245  auto [meta_ptr, meta_size] = runtime::PtrSize(meta_pos);
246  [[maybe_unused]] // It is future-reserved field, is not used now
247  auto meta_buffer = memory.loadN(meta_ptr, meta_size);
248 
249  HttpMethod method = HttpMethod::Undefined;
250  if (method_buffer.toString() == "Get") {
251  method = HttpMethod::Get;
252  } else if (method_buffer.toString() == "Post") {
253  method = HttpMethod::Post;
254  } else {
255  SL_TRACE(
256  log_,
257  "ext_offchain_http_request_start_version_1( {}, {}, {} ) failed: "
258  "Reason: unknown method",
259  method_buffer.toString(),
260  uri,
261  meta_buffer.toString());
262  }
263 
264  auto result = worker->httpRequestStart(method, uri, meta_buffer);
265 
266  if (result.isSuccess()) {
268  log_, result.value(), method_buffer.toString(), uri, meta_buffer);
269 
270  } else {
271  SL_TRACE(log_,
272  "ext_offchain_http_request_start_version_1( {}, {}, {} ) failed "
273  "during execution",
274  method_buffer.toString(),
275  uri,
276  meta_buffer.toString());
277  }
278 
279  return memory.storeBuffer(scale::encode(result).value());
280  }
281 
284  runtime::WasmI32 request_id,
285  runtime::WasmSpan name_pos,
286  runtime::WasmSpan value_pos) {
287  auto worker = getWorker();
288 
289  auto &memory = memory_provider_->getCurrentMemory()->get();
290 
291  auto [name_ptr, name_size] = runtime::PtrSize(name_pos);
292  auto name_buffer = memory.loadN(name_ptr, name_size);
293  auto name = name_buffer.toString();
294 
295  auto [value_ptr, value_size] = runtime::PtrSize(value_pos);
296  auto value_buffer = memory.loadN(value_ptr, value_size);
297  auto value = value_buffer.toString();
298 
299  auto result = worker->httpRequestAddHeader(request_id, name, value);
300 
301  if (result.isSuccess()) {
302  SL_TRACE_FUNC_CALL(log_, "Success", name, value);
303 
304  } else {
305  SL_TRACE(
306  log_,
307  "ext_offchain_http_request_add_header_version_1( {}, {}, {} ) failed "
308  "during execution",
309  request_id,
310  name,
311  value);
312  }
313 
314  return memory.storeBuffer(scale::encode(result).value());
315  }
316 
319  runtime::WasmI32 request_id,
320  runtime::WasmSpan chunk_pos,
321  runtime::WasmSpan deadline_pos) {
322  auto worker = getWorker();
323 
324  auto &memory = memory_provider_->getCurrentMemory()->get();
325 
326  auto [chunk_ptr, chunk_size] = runtime::PtrSize(chunk_pos);
327  auto chunk_buffer = memory.loadN(chunk_ptr, chunk_size);
328 
329  auto [deadline_ptr, deadline_size] = runtime::PtrSize(deadline_pos);
330  auto deadline_buffer = memory.loadN(deadline_ptr, deadline_size);
331  auto deadline_res =
332  scale::decode<std::optional<Timestamp>>(deadline_buffer);
333  if (deadline_res.has_error()) {
334  throw std::runtime_error("Invalid encoded data for deadline arg");
335  }
336  auto &deadline = deadline_res.value();
337 
338  auto result =
339  worker->httpRequestWriteBody(request_id, chunk_buffer, deadline);
340 
341  return memory.storeBuffer(scale::encode(result).value());
342  }
343 
346  runtime::WasmSpan ids_pos, runtime::WasmSpan deadline_pos) {
347  auto worker = getWorker();
348 
349  auto &memory = memory_provider_->getCurrentMemory()->get();
350 
351  auto [ids_ptr, ids_size] = runtime::PtrSize(ids_pos);
352  auto ids_buffer = memory.loadN(ids_ptr, ids_size);
353  auto ids_res = scale::decode<std::vector<RequestId>>(ids_buffer);
354  if (ids_res.has_error()) {
355  throw std::runtime_error("Invalid encoded data for IDs arg");
356  }
357  auto &ids = ids_res.value();
358 
359  auto [deadline_ptr, deadline_size] = runtime::PtrSize(deadline_pos);
360  auto deadline_buffer = memory.loadN(deadline_ptr, deadline_size);
361  auto deadline_res =
362  scale::decode<std::optional<Timestamp>>(deadline_buffer);
363  if (deadline_res.has_error()) {
364  throw std::runtime_error("Invalid encoded data for deadline arg");
365  }
366  auto &deadline = deadline_res.value();
367 
368  auto result = worker->httpResponseWait(ids, deadline);
369 
370  return memory.storeBuffer(scale::encode(result).value());
371  }
372 
375  runtime::WasmI32 request_id) {
376  auto worker = getWorker();
377 
378  auto &memory = memory_provider_->getCurrentMemory()->get();
379 
380  auto result = worker->httpResponseHeaders(request_id);
381 
383  log_, fmt::format("<{} headers>", result.size()), request_id);
384 
385  return memory.storeBuffer(scale::encode(result).value());
386  }
387 
390  runtime::WasmI32 request_id,
391  runtime::WasmSpan buffer_pos,
392  runtime::WasmSpan deadline_pos) {
393  auto worker = getWorker();
394  auto &memory = memory_provider_->getCurrentMemory()->get();
395 
396  auto dst_buffer = runtime::PtrSize(buffer_pos);
397 
398  auto [deadline_ptr, deadline_size] = runtime::PtrSize(deadline_pos);
399  auto deadline_buffer = memory.loadN(deadline_ptr, deadline_size);
400  auto deadline_res =
401  scale::decode<std::optional<Timestamp>>(deadline_buffer);
402  if (deadline_res.has_error()) {
403  throw std::runtime_error("Invalid encoded data for deadline arg");
404  }
405  auto &deadline = deadline_res.value();
406 
407  common::Buffer buffer;
408  buffer.resize(dst_buffer.size);
409 
410  auto result = worker->httpResponseReadBody(request_id, buffer, deadline);
411 
412  if (result.isSuccess()) {
413  memory.storeBuffer(dst_buffer.ptr, buffer);
414  }
415 
416  return memory.storeBuffer(scale::encode(result).value());
417  }
418 
420  runtime::WasmSpan nodes_pos, runtime::WasmI32 authorized_only) {
421  auto worker = getWorker();
422  auto &memory = memory_provider_->getCurrentMemory()->get();
423 
424  auto [nodes_ptr, nodes_size] = runtime::PtrSize(nodes_pos);
425  auto nodes_buffer = memory.loadN(nodes_ptr, nodes_size);
426  auto nodes_res = scale::decode<std::vector<common::Buffer>>(nodes_buffer);
427  if (nodes_res.has_error()) {
428  throw std::runtime_error("Invalid encoded data for nodes arg");
429  }
430  const auto &nodes_as_buffers = nodes_res.value();
431 
432  std::vector<libp2p::peer::PeerId> nodes;
433  for (auto buff : nodes_as_buffers) {
434  auto peer_id_res = libp2p::peer::PeerId::fromBytes(buff);
435  if (peer_id_res.has_error()) {
436  throw std::runtime_error("Invalid encoded data for nodes arg");
437  }
438  auto &peer_id = peer_id_res.value();
439  nodes.emplace_back(std::move(peer_id));
440  }
441 
442  worker->setAuthorizedNodes(std::move(nodes), authorized_only == 1);
443  }
444 
447  if (not config_.is_indexing_enabled) {
448  return;
449  }
450 
451  auto &memory = memory_provider_->getCurrentMemory()->get();
452 
453  auto [key_ptr, key_size] = runtime::PtrSize(key);
454  auto key_buffer = memory.loadN(key_ptr, key_size);
455  auto [value_ptr, value_size] = runtime::PtrSize(value);
456  auto value_buffer = memory.loadN(value_ptr, value_size);
457 
458  auto result = offchain_storage_->set(key_buffer, std::move(value_buffer));
459  if (result.has_error()) {
460  SL_WARN(log_, "Can't set value in storage: {}", result.error().message());
461  }
462  }
463 
465  runtime::WasmSpan key) {
466  if (not config_.is_indexing_enabled) {
467  return;
468  }
469 
470  auto &memory = memory_provider_->getCurrentMemory()->get();
471 
472  auto [key_ptr, key_size] = runtime::PtrSize(key);
473  auto key_buffer = memory.loadN(key_ptr, key_size);
474 
475  auto result = offchain_storage_->clear(key_buffer);
476  if (result.has_error()) {
477  SL_WARN(
478  log_, "Can't clear value in storage: {}", result.error().message());
479  }
480  }
481 
482 } // namespace kagome::host_api
uint64_t WasmU64
Definition: types.hpp:47
Class represents arbitrary (including empty) byte buffer.
Definition: buffer.hpp:29
void ext_offchain_index_clear_version_1(runtime::WasmSpan key)
Remove a key and its associated value from the offchain database.
runtime::WasmSpan ext_offchain_local_storage_get_version_1(runtime::WasmI32 kind, runtime::WasmSpan key)
Gets a value from the local storage.
runtime::WasmSpan ext_offchain_http_response_read_body_version_1(runtime::WasmI32 request_id, runtime::WasmSpan buffer, runtime::WasmSpan deadline)
Reads a chunk of body response to the given buffer. Returns the number of bytes written or an error i...
std::shared_ptr< offchain::OffchainWorker > getWorker()
runtime::WasmU64 ext_offchain_timestamp_version_1()
Returns current timestamp.
SLBuffer & resize(size_t size)
Definition: buffer.hpp:66
runtime::WasmSpan ext_offchain_http_response_headers_version_1(runtime::WasmI32 request_id)
Read all HTTP response headers. Returns an array of key/value pairs. Response headers must be read be...
#define SL_TRACE_FUNC_CALL(logger, ret,...)
Definition: logger.hpp:142
STL namespace.
int32_t WasmI32
Definition: types.hpp:46
runtime::WasmPointer ext_offchain_random_seed_version_1()
Generates a random seed. This is a truly random non deterministic seed generated by the host environm...
runtime::WasmSpan ext_offchain_http_response_wait_version_1(runtime::WasmSpan ids, runtime::WasmSpan deadline)
Returns an array of request statuses (the length is the same as IDs). Note that if deadline is not pr...
const OffchainExtensionConfig & config_
runtime::WasmI32 ext_offchain_local_storage_compare_and_set_version_1(runtime::WasmI32 kind, runtime::WasmSpan key, runtime::WasmSpan expected, runtime::WasmSpan value)
Sets a new value in the local storage if the condition matches the current value. ...
runtime::WasmSpan ext_offchain_http_request_start_version_1(runtime::WasmSpan method, runtime::WasmSpan uri, runtime::WasmSpan meta)
Initiates a HTTP request given by the HTTP method and the URL. Returns the id of a newly started requ...
runtime::WasmSpan ext_offchain_network_state_version_1()
Returns the SCALE encoded, opaque information about the local node&#39;s network state.
OffchainExtension(const OffchainExtensionConfig &config, std::shared_ptr< const runtime::MemoryProvider > memory_provider, std::shared_ptr< offchain::OffchainStorage > offchain_storage, std::shared_ptr< offchain::OffchainWorkerPool > ocw_pool)
void ext_offchain_set_authorized_nodes_version_1(runtime::WasmSpan nodes, runtime::WasmI32 authorized_only)
Set the authorized nodes which are allowed to connect to the local node. This function is offered by ...
uint64_t WasmSpan
combination of pointer and size, where less significant part represents wasm pointer, and most significant represents size
Definition: types.hpp:31
int16_t RequestId
Definition: types.hpp:29
runtime::WasmI32 ext_offchain_is_validator_version_1()
Check whether the local node is a potential validator. Even if this function returns 1...
void ext_offchain_local_storage_set_version_1(runtime::WasmI32 kind, runtime::WasmSpan key, runtime::WasmSpan value)
Sets a value in the local storage. This storage is not part of the consensus, it&#39;s only accessible by...
runtime::WasmSpan ext_offchain_http_request_write_body_version_1(runtime::WasmI32 request_id, runtime::WasmSpan chunk, runtime::WasmSpan deadline)
Writes a chunk of the request body. Returns a non-zero value in case the deadline is reached or the c...
runtime::WasmSpan ext_offchain_http_request_add_header_version_1(runtime::WasmI32 request_id, runtime::WasmSpan name, runtime::WasmSpan value)
Append header to the request. Returns an error if the request identifier is invalid, http_response_wait has already been called on the specified request identifier, the deadline is reached or an I/O error has happened (e.g. the remote has closed the connection)
void ext_offchain_index_set_version_1(runtime::WasmSpan key, runtime::WasmSpan value)
Write a key value pair to the offchain database in a buffered fashion.
uint32_t WasmPointer
type of wasm memory is 32 bit integer
Definition: types.hpp:26
void ext_offchain_sleep_until_version_1(runtime::WasmU64 deadline)
Pause the execution until deadline is reached.
std::shared_ptr< const runtime::MemoryProvider > memory_provider_
uint64_t Timestamp
Timestamp is milliseconds since UNIX Epoch.
Definition: types.hpp:21
void ext_offchain_local_storage_clear_version_1(runtime::WasmI32 kind, runtime::WasmSpan key)
Remove a value from the local storage.
runtime::WasmSpan ext_offchain_submit_transaction_version_1(runtime::WasmSpan data)
Given a SCALE encoded extrinsic, this function submits the extrinsic to the Host&#39;s transaction pool...
std::shared_ptr< offchain::OffchainWorkerPool > ocw_pool_
Logger createLogger(const std::string &tag)
Definition: logger.cpp:112
std::shared_ptr< offchain::OffchainStorage > offchain_storage_