Kagome
Polkadot Runtime Engine in C++17
executor.hpp
Go to the documentation of this file.
1 
6 #ifndef KAGOME_CORE_RUNTIME_COMMON_EXECUTOR_HPP
7 #define KAGOME_CORE_RUNTIME_COMMON_EXECUTOR_HPP
8 
10 
11 #include <optional>
12 
13 #include "common/buffer.hpp"
14 #include "host_api/host_api.hpp"
15 #include "log/logger.hpp"
16 #include "log/profiling_logger.hpp"
17 #include "outcome/outcome.hpp"
18 #include "primitives/version.hpp"
27 #include "scale/scale.hpp"
30 
31 #ifdef __has_builtin
32 #if __has_builtin(__builtin_expect)
33 #define likely(x) __builtin_expect((x), 1)
34 #endif
35 #endif
36 #ifndef likely
37 #define likely(x) (x)
38 #endif
39 
40 namespace kagome::runtime {
41 
47  class Executor : public RawExecutor {
48  public:
50 
51  Executor(std::shared_ptr<RuntimeEnvironmentFactory> env_factory,
52  std::shared_ptr<RuntimePropertiesCache> cache)
53  : env_factory_(std::move(env_factory)),
54  cache_(std::move(cache)),
55  logger_{log::createLogger("Executor", "runtime")} {
56  BOOST_ASSERT(env_factory_ != nullptr);
57  }
58 
65  template <typename Result, typename... Args>
66  outcome::result<PersistentResult<Result>> persistentCallAt(
67  primitives::BlockInfo const &block_info,
68  storage::trie::RootHash const &storage_state,
69  std::string_view name,
70  Args &&...args) {
71  OUTCOME_TRY(
72  env,
73  env_factory_->start(block_info, storage_state)->persistent().make());
74  auto res = callInternal<Result>(*env, name, std::forward<Args>(args)...);
75  if (res) {
76  OUTCOME_TRY(new_state_root, commitState(*env));
77  if constexpr (std::is_void_v<Result>) {
78  return PersistentResult<Result>{new_state_root};
79  } else {
80  return PersistentResult<Result>{std::move(res.value()),
81  new_state_root};
82  }
83  }
84  return res.error();
85  }
86 
92  template <typename Result, typename... Args>
93  outcome::result<PersistentResult<Result>> persistentCallAtGenesis(
94  std::string_view name, Args &&...args) {
95  OUTCOME_TRY(env_template, env_factory_->start());
96  OUTCOME_TRY(env, env_template->persistent().make());
97  auto res = callInternal<Result>(*env, name, std::forward<Args>(args)...);
98  if (res) {
99  OUTCOME_TRY(new_state_root, commitState(*env));
100  if constexpr (std::is_void_v<Result>) {
101  return PersistentResult<Result>{new_state_root};
102  } else {
103  return PersistentResult<Result>{std::move(res.value()),
104  new_state_root};
105  }
106  }
107  return res.error();
108  }
109 
115  template <typename Result, typename... Args>
116  outcome::result<PersistentResult<Result>> persistentCallAt(
117  primitives::BlockHash const &block_hash,
118  std::string_view name,
119  Args &&...args) {
120  OUTCOME_TRY(env_template, env_factory_->start(block_hash));
121  OUTCOME_TRY(env, env_template->persistent().make());
122  auto res = callInternal<Result>(*env, name, std::forward<Args>(args)...);
123  if (res) {
124  OUTCOME_TRY(new_state_root, commitState(*env));
125  if constexpr (std::is_void_v<Result>) {
126  return PersistentResult<Result>{new_state_root};
127  } else {
128  return PersistentResult<Result>{std::move(res.value()),
129  new_state_root};
130  }
131  }
132  return res.error();
133  }
134 
141  template <typename Result, typename... Args>
142  outcome::result<Result> callAt(primitives::BlockInfo const &block_info,
143  storage::trie::RootHash const &storage_state,
144  std::string_view name,
145  Args &&...args) {
146  OUTCOME_TRY(env, env_factory_->start(block_info, storage_state)->make());
147  return callMediateInternal<Result>(
148  *env, name, std::forward<Args>(args)...);
149  }
150 
156  template <typename Result, typename... Args>
157  outcome::result<Result> callAt(primitives::BlockHash const &block_hash,
158  std::string_view name,
159  Args &&...args) {
160  OUTCOME_TRY(env_template, env_factory_->start(block_hash));
161  OUTCOME_TRY(env, env_template->make());
162  return callMediateInternal<Result>(
163  *env, name, std::forward<Args>(args)...);
164  }
165 
171  template <typename Result, typename... Args>
172  outcome::result<Result> callAtGenesis(std::string_view name,
173  Args &&...args) {
174  OUTCOME_TRY(env_template, env_factory_->start());
175  OUTCOME_TRY(env, env_template->make());
176  return callMediateInternal<Result>(
177  *env, name, std::forward<Args>(args)...);
178  }
179 
180  outcome::result<common::Buffer> callAtRaw(
181  const primitives::BlockHash &block_hash,
182  std::string_view name,
183  const common::Buffer &encoded_args) override {
184  OUTCOME_TRY(env_template, env_factory_->start(block_hash));
185  OUTCOME_TRY(env, env_template->make());
186 
187  auto &memory = env->memory_provider->getCurrentMemory()->get();
188 
189  KAGOME_PROFILE_START(call_execution)
190 
191  auto result_span =
192  env->module_instance->callExportFunction(name, encoded_args);
193 
194  KAGOME_PROFILE_END(call_execution)
195  OUTCOME_TRY(span, result_span);
196 
197  OUTCOME_TRY(env->module_instance->resetEnvironment());
198  auto result = memory.loadN(span.ptr, span.size);
199 
200  return result;
201  }
202 
203  private:
211  template <typename Result, typename... Args>
212  inline outcome::result<Result> callMediateInternal(RuntimeEnvironment &env,
213  std::string_view name,
214  Args &&...args) {
215  if constexpr (std::is_same_v<Result, primitives::Version>) {
216  if (likely(name == "Core_version")) {
217  return cache_->getVersion(env.module_instance->getCodeHash(), [&] {
218  return callInternal<Result>(env, name, std::forward<Args>(args)...);
219  });
220  }
221  }
222 
223  if constexpr (std::is_same_v<Result, primitives::OpaqueMetadata>) {
224  if (likely(name == "Metadata_metadata")) {
225  return cache_->getMetadata(env.module_instance->getCodeHash(), [&] {
226  return callInternal<Result>(env, name, std::forward<Args>(args)...);
227  });
228  }
229  }
230 
231  return callInternal<Result>(env, name, std::forward<Args>(args)...);
232  }
233 
241  template <typename Result, typename... Args>
242  outcome::result<Result> callInternal(RuntimeEnvironment &env,
243  std::string_view name,
244  Args &&...args) {
245  auto &memory = env.memory_provider->getCurrentMemory()->get();
246 
247  Buffer encoded_args{};
248  if constexpr (sizeof...(args) > 0) {
249  OUTCOME_TRY(res, scale::encode(std::forward<Args>(args)...));
250  encoded_args.put(std::move(res));
251  }
252 
253  KAGOME_PROFILE_START(call_execution)
254 
255  auto result_span =
256  env.module_instance->callExportFunction(name, encoded_args);
257 
258  KAGOME_PROFILE_END(call_execution)
259  OUTCOME_TRY(span, result_span);
260 
261  OUTCOME_TRY(env.module_instance->resetEnvironment());
262 
263  if constexpr (std::is_void_v<Result>) {
264  return outcome::success();
265  } else {
266  auto result = memory.loadN(span.ptr, span.size);
267  Result t{};
268  scale::ScaleDecoderStream s(result);
269  try {
270  s >> t;
271  // Check whether the whole byte buffer was consumed
272  if (s.hasMore(1)) {
273  SL_ERROR(logger_,
274  "Runtime API call result size exceeds the size of the "
275  "type to initialize {}",
276  typeid(Result).name());
277  return outcome::failure(std::errc::illegal_byte_sequence);
278  }
279  return outcome::success(std::move(t));
280  } catch (std::system_error &e) {
281  return outcome::failure(e.code());
282  }
283  }
284  }
285 
286  outcome::result<storage::trie::RootHash> commitState(
287  const RuntimeEnvironment &env) {
288  KAGOME_PROFILE_START(state_commit)
289  BOOST_ASSERT_MSG(
290  env.storage_provider->tryGetPersistentBatch(),
291  "Current batch should always be persistent for a persistent call");
292  auto persistent_batch =
293  env.storage_provider->tryGetPersistentBatch().value();
294  OUTCOME_TRY(new_state_root, persistent_batch->commit());
295  SL_DEBUG(logger_,
296  "Runtime call committed new state with hash {}",
297  new_state_root.toHex());
298  KAGOME_PROFILE_END(state_commit)
299  return std::move(new_state_root);
300  }
301 
302  std::shared_ptr<RuntimeEnvironmentFactory> env_factory_;
303  std::shared_ptr<RuntimePropertiesCache> cache_;
305  };
306 
307 } // namespace kagome::runtime
308 
309 #undef likely
310 
311 #endif // KAGOME_CORE_RUNTIME_COMMON_EXECUTOR_HPP
#define KAGOME_PROFILE_END(scope)
Class represents arbitrary (including empty) byte buffer.
Definition: buffer.hpp:29
outcome::result< Result > callAt(primitives::BlockHash const &block_hash, std::string_view name, Args &&...args)
Definition: executor.hpp:157
const std::shared_ptr< const MemoryProvider > memory_provider
outcome::result< std::unique_ptr< PersistentTrieBatch > > persistent_batch(const std::unique_ptr< TrieStorageImpl > &trie, const RootHash &hash)
STL namespace.
#define likely(x)
Definition: executor.hpp:37
Executor(std::shared_ptr< RuntimeEnvironmentFactory > env_factory, std::shared_ptr< RuntimePropertiesCache > cache)
Definition: executor.hpp:51
outcome::result< Result > callAt(primitives::BlockInfo const &block_info, storage::trie::RootHash const &storage_state, std::string_view name, Args &&...args)
Definition: executor.hpp:142
outcome::result< Result > callAtGenesis(std::string_view name, Args &&...args)
Definition: executor.hpp:172
outcome::result< Result > callInternal(RuntimeEnvironment &env, std::string_view name, Args &&...args)
Definition: executor.hpp:242
const std::shared_ptr< TrieStorageProvider > storage_provider
SLBuffer< std::numeric_limits< size_t >::max()> Buffer
Definition: buffer.hpp:244
std::shared_ptr< soralog::Logger > Logger
Definition: logger.hpp:23
std::shared_ptr< RuntimeEnvironmentFactory > env_factory_
Definition: executor.hpp:302
outcome::result< Result > callMediateInternal(RuntimeEnvironment &env, std::string_view name, Args &&...args)
Definition: executor.hpp:212
outcome::result< PersistentResult< Result > > persistentCallAt(primitives::BlockHash const &block_hash, std::string_view name, Args &&...args)
Definition: executor.hpp:116
outcome::result< PersistentResult< Result > > persistentCallAtGenesis(std::string_view name, Args &&...args)
Definition: executor.hpp:93
outcome::result< common::Buffer > callAtRaw(const primitives::BlockHash &block_hash, std::string_view name, const common::Buffer &encoded_args) override
Definition: executor.hpp:180
std::shared_ptr< RuntimePropertiesCache > cache_
Definition: executor.hpp:303
#define KAGOME_PROFILE_START(scope)
Logger createLogger(const std::string &tag)
Definition: logger.cpp:112
outcome::result< PersistentResult< Result > > persistentCallAt(primitives::BlockInfo const &block_info, storage::trie::RootHash const &storage_state, std::string_view name, Args &&...args)
Definition: executor.hpp:66
const std::shared_ptr< ModuleInstance > module_instance
outcome::result< storage::trie::RootHash > commitState(const RuntimeEnvironment &env)
Definition: executor.hpp:286