diff --git a/.daily_canary b/.daily_canary index eedd09e179a8..93a0bfe6a735 100644 --- a/.daily_canary +++ b/.daily_canary @@ -3,4 +3,4 @@ ( V ) / . \ | +---=---' /--x-m- /--n-n---xXx--/--yY------>>>----<<<>>]]{{}}---||-/\---.. 2024__ -!"! \ No newline at end of file +! \ No newline at end of file diff --git a/include/ccf/js/core/context.h b/include/ccf/js/core/context.h index 28b1fbc556a5..a08c7d6b05d2 100644 --- a/include/ccf/js/core/context.h +++ b/include/ccf/js/core/context.h @@ -5,6 +5,7 @@ #include "ccf/js/core/runtime.h" #include "ccf/js/core/wrapped_value.h" #include "ccf/js/extensions/extension_interface.h" +#include "ccf/js/modules/module_loader_interface.h" #include "ccf/js/tx_access.h" #include "ccf/pal/locking.h" @@ -46,14 +47,16 @@ namespace ccf::js::core JSContext* ctx; Runtime rt; + js::extensions::Extensions extensions; + js::modules::ModuleLoaderPtr module_loader; + // The interpreter can cache loaded modules so they do not need to be loaded // from the KV for every execution, which is particularly useful when // re-using interpreters. A module can only be loaded once per interpreter, // and the entire interpreter should be thrown away if _any_ of its modules // needs to be refreshed. - std::map loaded_modules_cache; - - js::extensions::Extensions extensions; + std::map> + loaded_modules_cache; public: ccf::pal::Mutex lock; @@ -82,10 +85,13 @@ namespace ccf::js::core return ctx; } - std::optional get_module_from_cache( - const std::string& module_name); - void load_module_to_cache( - const std::string& module_name, const JSWrappedValue& module); + void set_module_loader(const modules::ModuleLoaderPtr& ml) + { + module_loader = ml; + } + + virtual std::optional get_module( + std::string_view module_name); // Construct RAII wrapper around raw QuickJS value JSWrappedValue wrap(JSValue&& val) const; diff --git a/include/ccf/js/modules.h b/include/ccf/js/modules.h deleted file mode 100644 index 4e0685d71615..000000000000 --- a/include/ccf/js/modules.h +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the Apache 2.0 License. -#pragma once - -#include "ccf/ds/logger.h" -#include "ccf/service/tables/modules.h" -#include "ccf/tx.h" -#include "ccf/version.h" - -#include - -namespace ccf::js -{ - static inline js::core::JSWrappedValue load_app_module( - JSContext* ctx, - const char* module_name, - kv::Tx* tx, - const std::string& modules_map = ccf::Tables::MODULES, - const std::string& modules_quickjs_bytecode_map = - ccf::Tables::MODULES_QUICKJS_BYTECODE, - const std::string& modules_quickjs_version_map = - ccf::Tables::MODULES_QUICKJS_VERSION) - { - js::core::Context& jsctx = *(js::core::Context*)JS_GetContextOpaque(ctx); - - std::string module_name_kv(module_name); - if (module_name_kv[0] != '/') - { - module_name_kv.insert(0, "/"); - } - // conforms to quickjs' default module filename normalizer - auto module_name_quickjs = module_name_kv.c_str() + 1; - - auto loaded_module = jsctx.get_module_from_cache(module_name_quickjs); - if (loaded_module.has_value()) - { - CCF_APP_TRACE("Using module from interpreter cache '{}'", module_name_kv); - return loaded_module.value(); - } - - const auto modules = tx->ro(modules_map); - - std::optional> bytecode; - const auto modules_quickjs_bytecode = - tx->ro(modules_quickjs_bytecode_map); - bytecode = modules_quickjs_bytecode->get(module_name_kv); - if (bytecode) - { - auto modules_quickjs_version = - tx->ro(modules_quickjs_version_map); - if (modules_quickjs_version->get() != std::string(ccf::quickjs_version)) - bytecode = std::nullopt; - } - - js::core::JSWrappedValue module_val; - - if (!bytecode) - { - CCF_APP_TRACE("Loading module '{}'", module_name_kv); - - auto module = modules->get(module_name_kv); - auto& js = module.value(); - - const char* buf = js.c_str(); - size_t buf_len = js.size(); - module_val = jsctx.eval( - buf, - buf_len, - module_name_quickjs, - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - if (module_val.is_exception()) - { - auto [reason, trace] = jsctx.error_message(); - - auto& rt = jsctx.runtime(); - if (rt.log_exception_details) - { - CCF_APP_FAIL("{}: {}", reason, trace.value_or("")); - } - - throw std::runtime_error(fmt::format( - "Failed to compile module '{}': {}", module_name, reason)); - } - } - else - { - CCF_APP_TRACE("Loading module from bytecode cache '{}'", module_name_kv); - - module_val = jsctx.read_object( - bytecode->data(), bytecode->size(), JS_READ_OBJ_BYTECODE); - if (module_val.is_exception()) - { - auto [reason, trace] = jsctx.error_message(); - - auto& rt = jsctx.runtime(); - if (rt.log_exception_details) - { - CCF_APP_FAIL("{}: {}", reason, trace.value_or("")); - } - - throw std::runtime_error(fmt::format( - "Failed to deserialize bytecode for module '{}': {}", - module_name, - reason)); - } - if (JS_ResolveModule(ctx, module_val.val) < 0) - { - auto [reason, trace] = jsctx.error_message(); - - auto& rt = jsctx.runtime(); - if (rt.log_exception_details) - { - CCF_APP_FAIL("{}: {}", reason, trace.value_or("")); - } - - throw std::runtime_error(fmt::format( - "Failed to resolve dependencies for module '{}': {}", - module_name, - reason)); - } - } - - CCF_APP_TRACE("Adding module to interpreter cache '{}'", module_name_kv); - jsctx.load_module_to_cache(module_name_quickjs, module_val); - - return module_val; - } - - static inline JSModuleDef* js_app_module_loader( - JSContext* ctx, const char* module_name, void* opaque) - { - auto tx = (kv::Tx*)opaque; - - try - { - auto module_val = load_app_module(ctx, module_name, tx); - return (JSModuleDef*)JS_VALUE_GET_PTR(module_val.val); - } - catch (const std::exception& exc) - { - JS_ThrowReferenceError(ctx, "%s", exc.what()); - js::core::Context& jsctx = *(js::core::Context*)JS_GetContextOpaque(ctx); - auto [reason, trace] = jsctx.error_message(); - - auto& rt = jsctx.runtime(); - if (rt.log_exception_details) - { - CCF_APP_FAIL( - "Failed to load module '{}': {} {}", - module_name, - reason, - trace.value_or("")); - } - return nullptr; - } - } -} diff --git a/include/ccf/js/modules/chained_module_loader.h b/include/ccf/js/modules/chained_module_loader.h new file mode 100644 index 000000000000..23faa0551a70 --- /dev/null +++ b/include/ccf/js/modules/chained_module_loader.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the Apache 2.0 License. +#pragma once + +#include "ccf/js/modules/module_loader_interface.h" + +namespace ccf::js::modules +{ + class ChainedModuleLoader : public ModuleLoaderInterface + { + protected: + ModuleLoaders sub_loaders; + + public: + ChainedModuleLoader(ModuleLoaders&& ml) : sub_loaders(std::move(ml)) {} + + virtual std::optional get_module( + std::string_view module_name, js::core::Context& ctx) override + { + for (auto& sub_loader : sub_loaders) + { + auto module_val = sub_loader->get_module(module_name, ctx); + if (module_val.has_value()) + { + return module_val; + } + } + + return std::nullopt; + } + }; +} diff --git a/include/ccf/js/modules/kv_bytecode_module_loader.h b/include/ccf/js/modules/kv_bytecode_module_loader.h new file mode 100644 index 000000000000..d64ca52aa340 --- /dev/null +++ b/include/ccf/js/modules/kv_bytecode_module_loader.h @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the Apache 2.0 License. +#pragma once + +#include "ccf/js/modules/module_loader_interface.h" +#include "ccf/service/tables/modules.h" +#include "ccf/tx.h" +#include "ccf/version.h" + +#include + +namespace ccf::js::modules +{ + class KvBytecodeModuleLoader : public ModuleLoaderInterface + { + protected: + ccf::ModulesQuickJsBytecode::ReadOnlyHandle* modules_bytecode_handle; + + bool version_ok; + + public: + KvBytecodeModuleLoader( + ccf::ModulesQuickJsBytecode::ReadOnlyHandle* mbh, + ccf::ModulesQuickJsVersion::ReadOnlyHandle* modules_version_handle) : + modules_bytecode_handle(mbh) + { + const auto version_in_kv = modules_version_handle->get(); + const auto version_in_binary = std::string(ccf::quickjs_version); + if (version_in_kv != version_in_binary) + { + CCF_APP_INFO( + "Ignoring bytecode table, which was written for QuickJS {} (this " + "node is running QuickJS {})", + version_in_kv, + version_in_binary); + version_ok = false; + } + else + { + version_ok = true; + } + } + + virtual std::optional get_module( + std::string_view module_name, js::core::Context& ctx) override + { + if (!version_ok) + { + return std::nullopt; + } + + std::string module_name_kv(module_name); + if (module_name_kv[0] != '/') + { + module_name_kv.insert(0, "/"); + } + + CCF_APP_TRACE("Looking for module '{}' bytecode in KV", module_name_kv); + + auto module_bytecode = modules_bytecode_handle->get(module_name_kv); + if (!module_bytecode.has_value()) + { + CCF_APP_TRACE("Module '{}' not found", module_name_kv); + return std::nullopt; + } + + auto module_val = ctx.read_object( + module_bytecode->data(), module_bytecode->size(), JS_READ_OBJ_BYTECODE); + + const bool failed_deser = module_val.is_exception(); + const bool failed_resolve = + !failed_deser && (JS_ResolveModule(ctx, module_val.val) < 0); + + if (failed_deser || failed_resolve) + { + auto [reason, trace] = ctx.error_message(); + + auto& rt = ctx.runtime(); + if (rt.log_exception_details) + { + CCF_APP_FAIL("{}: {}", reason, trace.value_or("")); + } + + if (failed_deser) + { + throw std::runtime_error(fmt::format( + "Failed to deserialize bytecode for module '{}': {}", + module_name, + reason)); + } + else + { + throw std::runtime_error(fmt::format( + "Failed to resolve dependencies for module '{}': {}", + module_name, + reason)); + } + } + + CCF_APP_TRACE( + "Module '{}' bytecode found in KV (table: {})", + module_name_kv, + modules_bytecode_handle->get_name_of_map()); + return module_val; + } + }; +} diff --git a/include/ccf/js/modules/kv_module_loader.h b/include/ccf/js/modules/kv_module_loader.h new file mode 100644 index 000000000000..fc76dcff7595 --- /dev/null +++ b/include/ccf/js/modules/kv_module_loader.h @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the Apache 2.0 License. +#pragma once + +#include "ccf/js/modules/module_loader_interface.h" +#include "ccf/service/tables/modules.h" +#include "ccf/tx.h" + +#include + +namespace ccf::js::modules +{ + class KvModuleLoader : public ModuleLoaderInterface + { + protected: + ccf::Modules::ReadOnlyHandle* modules_handle; + + public: + KvModuleLoader(ccf::Modules::ReadOnlyHandle* mh) : modules_handle(mh) {} + + virtual std::optional get_module( + std::string_view module_name, js::core::Context& ctx) override + { + std::string module_name_kv(module_name); + if (module_name_kv[0] != '/') + { + module_name_kv.insert(0, "/"); + } + + CCF_APP_TRACE("Looking for module '{}' in KV", module_name_kv); + + auto module_str = modules_handle->get(module_name_kv); + if (!module_str.has_value()) + { + CCF_APP_TRACE("Module '{}' not found", module_name_kv); + return std::nullopt; + } + + auto module_name_quickjs = module_name_kv.c_str() + 1; + const char* buf = module_str->c_str(); + size_t buf_len = module_str->size(); + auto parsed_module = ctx.eval( + buf, + buf_len, + module_name_quickjs, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + if (parsed_module.is_exception()) + { + auto [reason, trace] = ctx.error_message(); + + auto& rt = ctx.runtime(); + if (rt.log_exception_details) + { + CCF_APP_FAIL("{}: {}", reason, trace.value_or("")); + } + + throw std::runtime_error(fmt::format( + "Failed to compile module '{}': {}", module_name, reason)); + } + + CCF_APP_TRACE( + "Module '{}' found in KV (table: {})", + module_name_kv, + modules_handle->get_name_of_map()); + return parsed_module; + } + }; +} diff --git a/include/ccf/js/modules/module_loader_interface.h b/include/ccf/js/modules/module_loader_interface.h new file mode 100644 index 000000000000..a77765aed9fb --- /dev/null +++ b/include/ccf/js/modules/module_loader_interface.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the Apache 2.0 License. +#pragma once + +#include "ccf/js/core/wrapped_value.h" + +#include +#include +#include + +namespace ccf::js +{ + namespace core + { + class Context; + } + + namespace modules + { + class ModuleLoaderInterface + { + public: + virtual ~ModuleLoaderInterface() = default; + + virtual std::optional get_module( + std::string_view module_name, js::core::Context& ctx) = 0; + }; + + using ModuleLoaderPtr = std::shared_ptr; + using ModuleLoaders = std::vector; + } +} diff --git a/src/apps/js_generic/js_generic_base.cpp b/src/apps/js_generic/js_generic_base.cpp index dc0cf39da971..bfc3292d470c 100644 --- a/src/apps/js_generic/js_generic_base.cpp +++ b/src/apps/js_generic/js_generic_base.cpp @@ -18,7 +18,9 @@ #include "ccf/js/extensions/console.h" #include "ccf/js/extensions/math/random.h" #include "ccf/js/interpreter_cache_interface.h" -#include "ccf/js/modules.h" +#include "ccf/js/modules/chained_module_loader.h" +#include "ccf/js/modules/kv_bytecode_module_loader.h" +#include "ccf/js/modules/kv_module_loader.h" #include "ccf/js/named_auth_policies.h" #include "ccf/node/host_processes_interface.h" #include "ccf/node/rpc_context_impl.h" @@ -145,8 +147,17 @@ namespace ccfapp // Make the heap and stack limits safe while we init the runtime ctx.runtime().reset_runtime_options(); - JS_SetModuleLoaderFunc( - ctx.runtime(), nullptr, js::js_app_module_loader, &endpoint_ctx.tx); + ccf::js::modules::ModuleLoaders sub_loaders = { + std::make_shared( + endpoint_ctx.tx.ro( + ccf::Tables::MODULES_QUICKJS_BYTECODE), + endpoint_ctx.tx.ro( + ccf::Tables::MODULES_QUICKJS_VERSION)), + std::make_shared( + endpoint_ctx.tx.ro(ccf::Tables::MODULES))}; + auto module_loader = std::make_shared( + std::move(sub_loaders)); + ctx.set_module_loader(std::move(module_loader)); // Extensions with a dependency on this endpoint context (invocation), // which must be removed after execution. @@ -180,10 +191,14 @@ namespace ccfapp try { const auto& props = endpoint->properties; - auto module_val = - js::load_app_module(ctx, props.js_module.c_str(), &endpoint_ctx.tx); + auto module_val = ctx.get_module(props.js_module); + if (!module_val.has_value()) + { + throw std::runtime_error( + fmt::format("Unable to load module: {}", props.js_module)); + } export_func = ctx.get_exported_function( - module_val, props.js_function, props.js_module); + *module_val, props.js_function, props.js_module); } catch (const std::exception& exc) { @@ -209,6 +224,8 @@ namespace ccfapp ctx.remove_extension(extension); } + ctx.set_module_loader(nullptr); + const auto& rt = ctx.runtime(); if (val.is_exception()) diff --git a/src/js/core/context.cpp b/src/js/core/context.cpp index faf7f83bb018..280291930002 100644 --- a/src/js/core/context.cpp +++ b/src/js/core/context.cpp @@ -22,6 +22,43 @@ namespace ccf::js::core { + namespace + { + static inline JSModuleDef* load_module_via_context( + JSContext* ctx, const char* module_name, void* opaque) + { + auto context = (Context*)opaque; + + try + { + auto opt_module = context->get_module(module_name); + if (!opt_module.has_value()) + { + return nullptr; + } + return (JSModuleDef*)JS_VALUE_GET_PTR(opt_module->val); + } + catch (const std::exception& exc) + { + JS_ThrowReferenceError(ctx, "%s", exc.what()); + js::core::Context& jsctx = + *(js::core::Context*)JS_GetContextOpaque(ctx); + auto [reason, trace] = jsctx.error_message(); + + auto& rt = jsctx.runtime(); + if (rt.log_exception_details) + { + CCF_APP_FAIL( + "Failed to load module '{}': {} {}", + module_name, + reason, + trace.value_or("")); + } + return nullptr; + } + } + } + Context::Context(TxAccess acc) : access(acc) { ctx = JS_NewContext(rt); @@ -36,6 +73,8 @@ namespace ccf::js::core LOG_DEBUG_FMT("Extending JS context with plugin {}", plugin.name); plugin.extend(*this); } + + JS_SetModuleLoaderFunc(rt, nullptr, load_module_via_context, this); } Context::~Context() @@ -44,27 +83,36 @@ namespace ccf::js::core JS_FreeContext(ctx); } - std::optional Context::get_module_from_cache( - const std::string& module_name) + std::optional Context::get_module( + std::string_view module_name) { - auto module = loaded_modules_cache.find(module_name); - if (module == loaded_modules_cache.end()) + auto it = loaded_modules_cache.find(module_name); + if (it == loaded_modules_cache.end()) { - return std::nullopt; - } + LOG_TRACE_FMT("Module cache miss for '{}'", module_name); - return module->second; - } + // If not currently in cache, ask configured loader + if (module_loader == nullptr) + { + LOG_FAIL_FMT("Unable to load module: No module loader configured"); + return std::nullopt; + } - void Context::load_module_to_cache( - const std::string& module_name, const JSWrappedValue& module) - { - if (get_module_from_cache(module_name).has_value()) + auto module_val = module_loader->get_module(module_name, *this); + if (module_val.has_value()) + { + // If returned a new module, store it in cache + loaded_modules_cache.emplace_hint(it, module_name, *module_val); + } + + return module_val; + } + else { - throw std::logic_error(fmt::format( - "Module '{}' is already loaded in interpreter cache", module_name)); + LOG_TRACE_FMT("Module cache hit for '{}'", module_name); } - loaded_modules_cache[module_name] = module; + + return it->second; } JSWrappedValue Context::wrap(JSValue&& val) const diff --git a/src/js/extensions/ccf/converters.cpp b/src/js/extensions/ccf/converters.cpp index b8d058ca5781..41958eaf5aac 100644 --- a/src/js/extensions/ccf/converters.cpp +++ b/src/js/extensions/ccf/converters.cpp @@ -7,7 +7,6 @@ #include "ccf/js/extensions/ccf/converters.h" #include "ccf/js/core/context.h" -#include "ccf/js/modules.h" #include "ccf/version.h" #include "js/checks.h" #include "node/rpc/jwt_management.h" diff --git a/src/js/extensions/ccf/gov_effects.cpp b/src/js/extensions/ccf/gov_effects.cpp index 2f4e1d3ea3fe..6706dece4b09 100644 --- a/src/js/extensions/ccf/gov_effects.cpp +++ b/src/js/extensions/ccf/gov_effects.cpp @@ -7,7 +7,7 @@ #include "ccf/js/extensions/ccf/gov_effects.h" #include "ccf/js/core/context.h" -#include "ccf/js/modules.h" +#include "ccf/js/modules/kv_module_loader.h" #include "ccf/version.h" #include "node/rpc/jwt_management.h" @@ -45,22 +45,27 @@ namespace ccf::js::extensions js::core::Context ctx2(js::TxAccess::APP_RW); ctx2.runtime().set_runtime_options( tx_ptr, js::core::RuntimeLimitsPolicy::NO_LOWER_THAN_DEFAULTS); - JS_SetModuleLoaderFunc( - ctx2.runtime(), nullptr, js::js_app_module_loader, &tx); - auto modules = tx.ro(ccf::Tables::MODULES); auto quickjs_version = tx.wo(ccf::Tables::MODULES_QUICKJS_VERSION); + quickjs_version->put(ccf::quickjs_version); + auto quickjs_bytecode = tx.wo( ccf::Tables::MODULES_QUICKJS_BYTECODE); - - quickjs_version->put(ccf::quickjs_version); quickjs_bytecode->clear(); try { + auto modules = tx.ro(ccf::Tables::MODULES); + ctx2.set_module_loader( + std::make_shared(modules)); + modules->foreach([&](const auto& name, const auto& src) { - auto module_val = load_app_module(ctx2, name.c_str(), &tx); + auto module_val = ctx2.eval( + src.c_str(), + src.size(), + name.c_str(), + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); uint8_t* out_buf; size_t out_buf_len; diff --git a/src/js/registry.cpp b/src/js/registry.cpp index 2e7c6120e554..5c99dc12f69f 100644 --- a/src/js/registry.cpp +++ b/src/js/registry.cpp @@ -9,6 +9,7 @@ #include "ccf/ds/hash.h" #include "ccf/http_query.h" #include "ccf/json_handler.h" +#include "ccf/service/tables/modules.h" #include "ccf/version.h" #include @@ -32,7 +33,9 @@ #include "ccf/js/extensions/console.h" #include "ccf/js/extensions/math/random.h" #include "ccf/js/interpreter_cache_interface.h" -#include "ccf/js/modules.h" +#include "ccf/js/modules/chained_module_loader.h" +#include "ccf/js/modules/kv_bytecode_module_loader.h" +#include "ccf/js/modules/kv_module_loader.h" #include "ccf/node/rpc_context_impl.h" namespace ccf::js @@ -81,8 +84,18 @@ namespace ccf::js // Make the heap and stack limits safe while we init the runtime ctx.runtime().reset_runtime_options(); - JS_SetModuleLoaderFunc( - ctx.runtime(), nullptr, ccf::js::js_app_module_loader, &endpoint_ctx.tx); + ccf::js::modules::ModuleLoaders sub_loaders = { + std::make_shared( + endpoint_ctx.tx.ro( + modules_quickjs_bytecode_map), + endpoint_ctx.tx.ro( + modules_quickjs_version_map)), + std::make_shared( + endpoint_ctx.tx.ro(modules_map))}; + auto module_loader = + std::make_shared( + std::move(sub_loaders)); + ctx.set_module_loader(std::move(module_loader)); // Extensions with a dependency on this endpoint context (invocation), // which must be removed after execution. @@ -116,15 +129,9 @@ namespace ccf::js try { const auto& props = endpoint->properties; - auto module_val = ccf::js::load_app_module( - ctx, - props.js_module.c_str(), - &endpoint_ctx.tx, - modules_map, - modules_quickjs_bytecode_map, - modules_quickjs_version_map); + auto module_val = ctx.get_module(props.js_module); export_func = ctx.get_exported_function( - module_val, props.js_function, props.js_module); + *module_val, props.js_function, props.js_module); } catch (const std::exception& exc) { @@ -485,8 +492,6 @@ namespace ccf::js ccf::js::core::Context jsctx(ccf::js::TxAccess::APP_RW); jsctx.runtime().set_runtime_options( &ctx.tx, ccf::js::core::RuntimeLimitsPolicy::NO_LOWER_THAN_DEFAULTS); - JS_SetModuleLoaderFunc( - jsctx.runtime(), nullptr, ccf::js::js_app_module_loader, &ctx.tx); auto quickjs_version = ctx.tx.wo(modules_quickjs_version_map); @@ -495,15 +500,15 @@ namespace ccf::js quickjs_version->put(ccf::quickjs_version); quickjs_bytecode->clear(); + jsctx.set_module_loader( + std::make_shared(modules)); modules->foreach([&](const auto& name, const auto& src) { - auto module_val = ccf::js::load_app_module( - jsctx, + auto module_val = jsctx.eval( + src.c_str(), + src.size(), name.c_str(), - &ctx.tx, - modules_map, - modules_quickjs_bytecode_map, - modules_quickjs_version_map); + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); uint8_t* out_buf; size_t out_buf_len; diff --git a/src/node/rpc/jwt_management.h b/src/node/rpc/jwt_management.h index 012ea43a3f98..71b21c78452f 100644 --- a/src/node/rpc/jwt_management.h +++ b/src/node/rpc/jwt_management.h @@ -6,6 +6,7 @@ #include "ccf/ds/hex.h" #include "ccf/service/tables/jwt.h" #include "ccf/service/tables/proposals.h" +#include "ccf/tx.h" #ifdef SGX_ATTESTATION_VERIFICATION # include diff --git a/tests/js-modules/modules.py b/tests/js-modules/modules.py index 658b9c1cb2b4..bd3004f1863a 100644 --- a/tests/js-modules/modules.py +++ b/tests/js-modules/modules.py @@ -1380,7 +1380,7 @@ def test_js_exception_output(network, args): assert body["error"]["details"][0]["message"] == "Error: test error: 42" assert ( body["error"]["details"][0]["trace"] - == " at nested (endpoints/rpc.js:27)\n at throwError (endpoints/rpc.js:29)\n" + == " at nested (/endpoints/rpc.js:27)\n at throwError (/endpoints/rpc.js:29)\n" ) network.consortium.set_js_runtime_options(