Skip to content

src/runtime/binaryen/runtime_api/runtime_api.hpp

Namespaces

Name
sgns
sgns::runtime
sgns::runtime::binaryen

Classes

Name
class sgns::runtime::binaryen::RuntimeApi
base class for all runtime apis

Source code

#ifndef SUPERGENIUS_SRC_RUNTIME_BINARYEN_RUNTIME_API_HPP
#define SUPERGENIUS_SRC_RUNTIME_BINARYEN_RUNTIME_API_HPP

#include <utility>

#include <binaryen/wasm-binary.h>
#include <binaryen/wasm-interpreter.h>
#include <boost/optional.hpp>

#include "base/buffer.hpp"
#include "base/logger.hpp"
#include "extensions/extension_factory.hpp"
#include "runtime/binaryen/runtime_environment.hpp"
#include "runtime/binaryen/runtime_external_interface.hpp"
#include "runtime/binaryen/runtime_manager.hpp"
#include "runtime/binaryen/wasm_executor.hpp"
#include "runtime/wasm_memory.hpp"
#include "runtime/wasm_provider.hpp"
#include "runtime/wasm_result.hpp"
#include "scale/scale.hpp"

namespace sgns::runtime::binaryen {

  class RuntimeApi {
   public:
    enum class CallPersistency {
      PERSISTENT,  // the changes made by this call will be applied to the state
                   // trie storage
      EPHEMERAL    // the changes made by this call will vanish once it's
                   // completed
    };

    RuntimeApi(std::shared_ptr<WasmProvider> wasm_provider,
               std::shared_ptr<RuntimeManager> runtime_manager)
        : runtime_manager_(std::move(runtime_manager)),
          wasm_provider_(std::move(wasm_provider)) {
      BOOST_ASSERT(runtime_manager_);
      BOOST_ASSERT(wasm_provider_);
    }

   private:
    // as it has a deduced return type, must be defined before execute()
    auto createRuntimeEnvironment(
        CallPersistency persistency,
        const boost::optional<base::Hash256> &state_root_opt) {
      if (state_root_opt.has_value()) {
        switch (persistency) {
          case CallPersistency::PERSISTENT:
            return runtime_manager_
                ->createPersistentRuntimeEnvironmentAt(
                    wasm_provider_->getStateCode(), state_root_opt.value())
                .value();
          case CallPersistency::EPHEMERAL:
            return runtime_manager_
                ->createEphemeralRuntimeEnvironmentAt(
                    wasm_provider_->getStateCode(), state_root_opt.value())
                .value();
        }
      } else {
        switch (persistency) {
          case CallPersistency::PERSISTENT:
            return runtime_manager_
                ->createPersistentRuntimeEnvironment(
                    wasm_provider_->getStateCode())
                .value();
          case CallPersistency::EPHEMERAL:
            return runtime_manager_
                ->createEphemeralRuntimeEnvironment(
                    wasm_provider_->getStateCode())
                .value();
        }
      }
    }

   protected:
    template <typename R, typename... Args>
    outcome::result<R> executeAt(std::string_view name,
                                 const base::Hash256 &state_root,
                                 CallPersistency persistency,
                                 Args &&... args) {
      return executeMaybeAt<R>(
          name, state_root, persistency, std::forward<Args>(args)...);
    }

    template <typename R, typename... Args>
    outcome::result<R> execute(std::string_view name,
                               CallPersistency persistency,
                               Args &&... args) {
      return executeMaybeAt<R>(
          name, boost::none, persistency, std::forward<Args>(args)...);
    }

   private:
    template <typename R, typename... Args>
    outcome::result<R> executeMaybeAt(
        std::string_view name,
        const boost::optional<base::Hash256> &state_root,
        CallPersistency persistency,
        Args &&... args) {
      logger_->debug("Executing export function: {}", name);
      if (state_root.has_value()) {
        logger_->debug("Resetting state to: {}", state_root.value().toHex());
      }

      auto environment = createRuntimeEnvironment(persistency, state_root);
      auto &&[module, memory, opt_batch] = environment;

      runtime::WasmPointer ptr = 0u;
      runtime::WasmSize len = 0u;

      if constexpr (sizeof...(args) > 0) {
        OUTCOME_TRY((auto &&, buffer), scale::encode(std::forward<Args>(args)...));
        len = buffer.size();
        ptr = memory->allocate(len);
        memory->storeBuffer(ptr, base::Buffer(std::move(buffer)));
      }

      wasm::LiteralList ll{wasm::Literal(ptr), wasm::Literal(len)};

      wasm::Name wasm_name = std::string(name);

      OUTCOME_TRY((auto &&, res), executor_.call(*module, wasm_name, ll));
      memory->reset();
      if constexpr (!std::is_same_v<void, R>) {
        WasmResult r(res.geti64());
        auto buffer = memory->loadN(r.address, r.length);
        // TODO (yuraz) PRE-98: after check for memory overflow is done,
        //  refactor it
        return scale::decode<R>(std::move(buffer));
      }

      if(opt_batch) {
        BOOST_OUTCOME_TRYV2(auto &&, opt_batch.value()->writeBack());
      }
      return outcome::success();
    }
    std::shared_ptr<RuntimeManager> runtime_manager_;
    std::shared_ptr<WasmProvider> wasm_provider_;
    WasmExecutor executor_;
    base::Logger logger_ = base::createLogger("Runtime API");
  };
}  // namespace sgns::runtime::binaryen

#endif  // SUPERGENIUS_SRC_RUNTIME_BINARYEN_RUNTIME_API_HPP

Updated on 2026-03-04 at 13:10:45 -0800