зеркало из https://github.com/microsoft/CCF.git
Split JS Wrapper (#2306)
This commit is contained in:
Родитель
b06bc3cd56
Коммит
92f9fe2015
|
@ -1 +1 @@
|
|||
Happy daily trigger :))
|
||||
Happy daily trigger :)))
|
|
@ -64,8 +64,8 @@ if("sgx" IN_LIST COMPILE_TARGETS)
|
|||
# enclave version
|
||||
add_library(
|
||||
ccf.enclave STATIC
|
||||
${CCF_DIR}/src/enclave/main.cpp ${CCF_DIR}/src/enclave/thread_local.cpp
|
||||
${CCF_GENERATED_DIR}/ccf_t.cpp
|
||||
${CCF_DIR}/src/enclave/main.cpp ${CCF_DIR}/src/js/wrap.cpp
|
||||
${CCF_DIR}/src/enclave/thread_local.cpp ${CCF_GENERATED_DIR}/ccf_t.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
|
@ -86,6 +86,7 @@ if("sgx" IN_LIST COMPILE_TARGETS)
|
|||
ccf.enclave
|
||||
PUBLIC ${OE_TARGET_ENCLAVE_AND_STD}
|
||||
-lgcc
|
||||
quickjs.enclave
|
||||
ccfcrypto.enclave
|
||||
http_parser.enclave
|
||||
lua.enclave
|
||||
|
@ -111,8 +112,9 @@ endif()
|
|||
if("virtual" IN_LIST COMPILE_TARGETS)
|
||||
# virtual version
|
||||
add_library(
|
||||
ccf.virtual STATIC ${CCF_DIR}/src/enclave/main.cpp
|
||||
${CCF_DIR}/src/enclave/thread_local.cpp
|
||||
ccf.virtual STATIC
|
||||
${CCF_DIR}/src/enclave/main.cpp ${CCF_DIR}/src/js/wrap.cpp
|
||||
${CCF_DIR}/src/enclave/thread_local.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
|
@ -137,6 +139,7 @@ if("virtual" IN_LIST COMPILE_TARGETS)
|
|||
ccfcrypto.host
|
||||
http_parser.host
|
||||
lua.host
|
||||
quickjs.host
|
||||
aft.virtual
|
||||
sss.host
|
||||
openenclave::oehost
|
||||
|
@ -361,12 +364,12 @@ if(BUILD_TESTS)
|
|||
target_link_libraries(http_test PRIVATE http_parser.host)
|
||||
|
||||
add_unit_test(
|
||||
frontend_test
|
||||
frontend_test ${CMAKE_CURRENT_SOURCE_DIR}/src/js/wrap.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/node/rpc/test/frontend_test.cpp
|
||||
)
|
||||
target_link_libraries(
|
||||
frontend_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} lua.host http_parser.host
|
||||
sss.host
|
||||
sss.host quickjs.host
|
||||
)
|
||||
|
||||
add_unit_test(
|
||||
|
@ -375,12 +378,12 @@ if(BUILD_TESTS)
|
|||
)
|
||||
|
||||
add_unit_test(
|
||||
member_voting_test
|
||||
member_voting_test ${CMAKE_CURRENT_SOURCE_DIR}/src/js/wrap.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/node/rpc/test/member_voting_test.cpp
|
||||
)
|
||||
target_link_libraries(
|
||||
member_voting_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} lua.host
|
||||
http_parser.host sss.host
|
||||
http_parser.host sss.host quickjs.host
|
||||
)
|
||||
|
||||
set_tests_properties(
|
||||
|
@ -390,12 +393,12 @@ if(BUILD_TESTS)
|
|||
)
|
||||
|
||||
add_unit_test(
|
||||
proposal_id_test
|
||||
proposal_id_test ${CMAKE_CURRENT_SOURCE_DIR}/src/js/wrap.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/node/rpc/test/proposal_id_test.cpp
|
||||
)
|
||||
target_link_libraries(
|
||||
proposal_id_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} lua.host
|
||||
http_parser.host sss.host
|
||||
http_parser.host sss.host quickjs.host
|
||||
)
|
||||
|
||||
add_unit_test(
|
||||
|
|
|
@ -35,9 +35,16 @@ if("sgx" IN_LIST COMPILE_TARGETS)
|
|||
)
|
||||
target_link_libraries(quickjs.enclave PUBLIC ${OE_TARGET_LIBC})
|
||||
set_property(TARGET quickjs.enclave PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_include_directories(quickjs.enclave PUBLIC ${QUICKJS_INC})
|
||||
target_include_directories(
|
||||
quickjs.enclave PUBLIC $<BUILD_INTERFACE:${CCF_DIR}/3rdparty/quickjs>
|
||||
$<INSTALL_INTERFACE:include/3rdparty/quickjs>
|
||||
)
|
||||
|
||||
install(TARGETS quickjs.enclave DESTINATION lib)
|
||||
install(
|
||||
TARGETS quickjs.enclave
|
||||
EXPORT ccf
|
||||
DESTINATION lib
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(quickjs.host STATIC ${QUICKJS_SRC})
|
||||
|
@ -48,6 +55,13 @@ target_compile_options(
|
|||
)
|
||||
add_san(quickjs.host)
|
||||
set_property(TARGET quickjs.host PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_include_directories(quickjs.host PUBLIC ${QUICKJS_INC})
|
||||
target_include_directories(
|
||||
quickjs.host PUBLIC $<BUILD_INTERFACE:${CCF_DIR}/3rdparty/quickjs>
|
||||
$<INSTALL_INTERFACE:include/3rdparty/quickjs>
|
||||
)
|
||||
|
||||
install(TARGETS quickjs.host DESTINATION lib)
|
||||
install(
|
||||
TARGETS quickjs.host
|
||||
EXPORT ccf
|
||||
DESTINATION lib
|
||||
)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the Apache 2.0 License.
|
||||
|
||||
namespace js
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wc99-extensions"
|
||||
|
||||
static void js_free_arraybuffer_cstring(JSRuntime*, void* opaque, void* ptr)
|
||||
{
|
||||
JS_FreeCString((JSContext*)opaque, (char*)ptr);
|
||||
}
|
||||
|
||||
static JSValue js_str_to_buf(
|
||||
JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
|
||||
{
|
||||
if (argc != 1)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 1", argc);
|
||||
|
||||
if (!JS_IsString(argv[0]))
|
||||
return JS_ThrowTypeError(ctx, "Argument must be a string");
|
||||
|
||||
size_t str_size = 0;
|
||||
const char* str = JS_ToCStringLen(ctx, &str_size, argv[0]);
|
||||
|
||||
if (!str)
|
||||
{
|
||||
js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSValue buf = JS_NewArrayBuffer(
|
||||
ctx, (uint8_t*)str, str_size, js_free_arraybuffer_cstring, ctx, false);
|
||||
|
||||
if (JS_IsException(buf))
|
||||
js_dump_error(ctx);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static JSValue js_buf_to_str(
|
||||
JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
|
||||
{
|
||||
if (argc != 1)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 1", argc);
|
||||
|
||||
size_t buf_size;
|
||||
uint8_t* buf = JS_GetArrayBuffer(ctx, &buf_size, argv[0]);
|
||||
|
||||
if (!buf)
|
||||
return JS_ThrowTypeError(ctx, "Argument must be an ArrayBuffer");
|
||||
|
||||
JSValue str = JS_NewStringLen(ctx, (char*)buf, buf_size);
|
||||
|
||||
if (JS_IsException(str))
|
||||
js::js_dump_error(ctx);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static JSValue js_json_compatible_to_buf(
|
||||
JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
|
||||
{
|
||||
if (argc != 1)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 1", argc);
|
||||
|
||||
JSValue str = JS_JSONStringify(ctx, argv[0], JS_NULL, JS_NULL);
|
||||
|
||||
if (JS_IsException(str))
|
||||
{
|
||||
js::js_dump_error(ctx);
|
||||
return str;
|
||||
}
|
||||
|
||||
JSValue buf = js_str_to_buf(ctx, JS_NULL, 1, &str);
|
||||
JS_FreeValue(ctx, str);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static JSValue js_buf_to_json_compatible(
|
||||
JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
|
||||
{
|
||||
if (argc != 1)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 1", argc);
|
||||
|
||||
size_t buf_size;
|
||||
uint8_t* buf = JS_GetArrayBuffer(ctx, &buf_size, argv[0]);
|
||||
|
||||
if (!buf)
|
||||
return JS_ThrowTypeError(ctx, "Argument must be an ArrayBuffer");
|
||||
|
||||
std::vector<uint8_t> buf_null_terminated(buf_size + 1);
|
||||
buf_null_terminated[buf_size] = 0;
|
||||
buf_null_terminated.assign(buf, buf + buf_size);
|
||||
|
||||
JSValue obj =
|
||||
JS_ParseJSON(ctx, (char*)buf_null_terminated.data(), buf_size, "<json>");
|
||||
|
||||
if (JS_IsException(obj))
|
||||
js::js_dump_error(ctx);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the Apache 2.0 License.
|
||||
#include "crypto/entropy.h"
|
||||
#include "crypto/key_wrap.h"
|
||||
#include "crypto/rsa_key_pair.h"
|
||||
|
||||
#include <quickjs/quickjs.h>
|
||||
|
||||
namespace js
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wc99-extensions"
|
||||
|
||||
static JSValue js_generate_aes_key(
|
||||
JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
|
||||
{
|
||||
if (argc != 1)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 1", argc);
|
||||
|
||||
int32_t key_size;
|
||||
if (JS_ToInt32(ctx, &key_size, argv[0]) < 0)
|
||||
{
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
// Supported key sizes for AES.
|
||||
if (key_size != 128 && key_size != 192 && key_size != 256)
|
||||
{
|
||||
JS_ThrowRangeError(ctx, "invalid key size");
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> key = crypto::create_entropy()->random(key_size / 8);
|
||||
|
||||
return JS_NewArrayBufferCopy(ctx, key.data(), key.size());
|
||||
}
|
||||
|
||||
static JSValue js_generate_rsa_key_pair(
|
||||
JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
|
||||
{
|
||||
if (argc != 1 && argc != 2)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 1 or 2", argc);
|
||||
|
||||
uint32_t key_size = 0, key_exponent = 0;
|
||||
if (JS_ToUint32(ctx, &key_size, argv[0]) < 0)
|
||||
{
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
if (argc == 2 && JS_ToUint32(ctx, &key_exponent, argv[1]) < 0)
|
||||
{
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
std::shared_ptr<crypto::RSAKeyPair> k;
|
||||
if (argc == 1)
|
||||
{
|
||||
k = crypto::make_rsa_key_pair(key_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
k = crypto::make_rsa_key_pair(key_size, key_exponent);
|
||||
}
|
||||
|
||||
crypto::Pem prv = k->private_key_pem();
|
||||
crypto::Pem pub = k->public_key_pem();
|
||||
|
||||
auto r = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(
|
||||
ctx, r, "privateKey", JS_NewString(ctx, (char*)prv.data()));
|
||||
JS_SetPropertyStr(
|
||||
ctx, r, "publicKey", JS_NewString(ctx, (char*)pub.data()));
|
||||
return r;
|
||||
}
|
||||
|
||||
static JSValue js_wrap_key(
|
||||
JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
|
||||
{
|
||||
if (argc != 3)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 3", argc);
|
||||
|
||||
// API loosely modeled after
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/wrapKey.
|
||||
|
||||
size_t key_size;
|
||||
uint8_t* key = JS_GetArrayBuffer(ctx, &key_size, argv[0]);
|
||||
if (!key)
|
||||
{
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
size_t wrapping_key_size;
|
||||
uint8_t* wrapping_key = JS_GetArrayBuffer(ctx, &wrapping_key_size, argv[1]);
|
||||
if (!wrapping_key)
|
||||
{
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
void* auto_free_ptr = JS_GetContextOpaque(ctx);
|
||||
js::Context& auto_free = *(js::Context*)auto_free_ptr;
|
||||
|
||||
auto parameters = argv[2];
|
||||
JSValue wrap_algo_name_val =
|
||||
auto_free(JS_GetPropertyStr(ctx, parameters, "name"));
|
||||
|
||||
auto wrap_algo_name_cstr = auto_free(JS_ToCString(ctx, wrap_algo_name_val));
|
||||
|
||||
if (!wrap_algo_name_cstr)
|
||||
{
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto algo_name = std::string(wrap_algo_name_cstr);
|
||||
if (algo_name == "RSA-OAEP")
|
||||
{
|
||||
// key can in principle be arbitrary data (see note on maximum size
|
||||
// in rsa_key_pair.h). wrapping_key is a public RSA key.
|
||||
|
||||
auto label_val = auto_free(JS_GetPropertyStr(ctx, parameters, "label"));
|
||||
size_t label_buf_size = 0;
|
||||
uint8_t* label_buf = JS_GetArrayBuffer(ctx, &label_buf_size, label_val);
|
||||
|
||||
auto wrapped_key = crypto::ckm_rsa_pkcs_oaep_wrap(
|
||||
crypto::Pem(wrapping_key, wrapping_key_size),
|
||||
{key, key + key_size},
|
||||
{label_buf, label_buf + label_buf_size});
|
||||
|
||||
return JS_NewArrayBufferCopy(
|
||||
ctx, wrapped_key.data(), wrapped_key.size());
|
||||
}
|
||||
else if (algo_name == "AES-KWP")
|
||||
{
|
||||
std::vector<uint8_t> wrapped_key = crypto::ckm_aes_key_wrap_pad(
|
||||
{wrapping_key, wrapping_key + wrapping_key_size},
|
||||
{key, key + key_size});
|
||||
|
||||
return JS_NewArrayBufferCopy(
|
||||
ctx, wrapped_key.data(), wrapped_key.size());
|
||||
}
|
||||
else if (algo_name == "RSA-OAEP-AES-KWP")
|
||||
{
|
||||
auto aes_key_size_value =
|
||||
auto_free(JS_GetPropertyStr(ctx, parameters, "aesKeySize"));
|
||||
int32_t aes_key_size = 0;
|
||||
if (JS_ToInt32(ctx, &aes_key_size, aes_key_size_value) < 0)
|
||||
{
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
auto label_val = auto_free(JS_GetPropertyStr(ctx, parameters, "label"));
|
||||
size_t label_buf_size = 0;
|
||||
uint8_t* label_buf = JS_GetArrayBuffer(ctx, &label_buf_size, label_val);
|
||||
|
||||
auto wrapped_key = crypto::ckm_rsa_aes_key_wrap(
|
||||
aes_key_size,
|
||||
crypto::Pem(wrapping_key, wrapping_key_size),
|
||||
{key, key + key_size},
|
||||
{label_buf, label_buf + label_buf_size});
|
||||
|
||||
return JS_NewArrayBufferCopy(
|
||||
ctx, wrapped_key.data(), wrapped_key.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_ThrowRangeError(
|
||||
ctx,
|
||||
"unsupported key wrapping algorithm, supported: RSA-OAEP, AES-KWP, "
|
||||
"RSA-OAEP-AES-KWP");
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
JS_ThrowRangeError(ctx, "%s", ex.what());
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
JS_ThrowRangeError(ctx, "caught unknown exception");
|
||||
js::js_dump_error(ctx);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
}
|
|
@ -0,0 +1,625 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the Apache 2.0 License.
|
||||
|
||||
#include "js/wrap.h"
|
||||
|
||||
#include "ds/logger.h"
|
||||
#include "js/conv.cpp"
|
||||
#include "js/crypto.cpp"
|
||||
#include "kv/untyped_map.h"
|
||||
#include "node/rpc/call_types.h"
|
||||
#include "tls/base64.h"
|
||||
#include "tx_id.h"
|
||||
|
||||
#include <memory>
|
||||
#include <quickjs/quickjs-exports.h>
|
||||
#include <quickjs/quickjs.h>
|
||||
|
||||
namespace js
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wc99-extensions"
|
||||
|
||||
using KVMap = kv::untyped::Map;
|
||||
|
||||
JSClassID kv_class_id = 0;
|
||||
JSClassID kv_map_handle_class_id = 0;
|
||||
JSClassID body_class_id = 0;
|
||||
|
||||
JSClassDef kv_class_def = {};
|
||||
JSClassExoticMethods kv_exotic_methods = {};
|
||||
JSClassDef kv_map_handle_class_def = {};
|
||||
JSClassDef body_class_def = {};
|
||||
|
||||
static JSValue js_kv_map_has(
|
||||
JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
auto handle = static_cast<KVMap::Handle*>(
|
||||
JS_GetOpaque(this_val, kv_map_handle_class_id));
|
||||
|
||||
if (argc != 1)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 1", argc);
|
||||
|
||||
size_t key_size;
|
||||
uint8_t* key = JS_GetArrayBuffer(ctx, &key_size, argv[0]);
|
||||
|
||||
if (!key)
|
||||
return JS_ThrowTypeError(ctx, "Argument must be an ArrayBuffer");
|
||||
|
||||
auto has = handle->has({key, key + key_size});
|
||||
|
||||
return JS_NewBool(ctx, has);
|
||||
}
|
||||
|
||||
static JSValue js_kv_map_get(
|
||||
JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
auto handle = static_cast<KVMap::Handle*>(
|
||||
JS_GetOpaque(this_val, kv_map_handle_class_id));
|
||||
|
||||
if (argc != 1)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 1", argc);
|
||||
|
||||
size_t key_size;
|
||||
uint8_t* key = JS_GetArrayBuffer(ctx, &key_size, argv[0]);
|
||||
|
||||
if (!key)
|
||||
return JS_ThrowTypeError(ctx, "Argument must be an ArrayBuffer");
|
||||
|
||||
auto val = handle->get({key, key + key_size});
|
||||
|
||||
if (!val.has_value())
|
||||
return JS_UNDEFINED;
|
||||
|
||||
JSValue buf =
|
||||
JS_NewArrayBufferCopy(ctx, val.value().data(), val.value().size());
|
||||
|
||||
if (JS_IsException(buf))
|
||||
js_dump_error(ctx);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static JSValue js_kv_map_delete(
|
||||
JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
auto handle = static_cast<KVMap::Handle*>(
|
||||
JS_GetOpaque(this_val, kv_map_handle_class_id));
|
||||
|
||||
if (argc != 1)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 1", argc);
|
||||
|
||||
size_t key_size;
|
||||
uint8_t* key = JS_GetArrayBuffer(ctx, &key_size, argv[0]);
|
||||
|
||||
if (!key)
|
||||
return JS_ThrowTypeError(ctx, "Argument must be an ArrayBuffer");
|
||||
|
||||
auto val = handle->remove({key, key + key_size});
|
||||
|
||||
return JS_NewBool(ctx, val);
|
||||
}
|
||||
|
||||
static JSValue js_kv_map_delete_read_only(
|
||||
JSContext* ctx, JSValueConst, int, JSValueConst*)
|
||||
{
|
||||
return JS_ThrowTypeError(ctx, "Cannot call delete on read-only map");
|
||||
}
|
||||
|
||||
static JSValue js_kv_map_set(
|
||||
JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
auto handle = static_cast<KVMap::Handle*>(
|
||||
JS_GetOpaque(this_val, kv_map_handle_class_id));
|
||||
|
||||
if (argc != 2)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 2", argc);
|
||||
|
||||
size_t key_size;
|
||||
uint8_t* key = JS_GetArrayBuffer(ctx, &key_size, argv[0]);
|
||||
|
||||
size_t val_size;
|
||||
uint8_t* val = JS_GetArrayBuffer(ctx, &val_size, argv[1]);
|
||||
|
||||
if (!key || !val)
|
||||
return JS_ThrowTypeError(ctx, "Arguments must be ArrayBuffers");
|
||||
|
||||
handle->put({key, key + key_size}, {val, val + val_size});
|
||||
|
||||
return JS_DupValue(ctx, this_val);
|
||||
}
|
||||
|
||||
static JSValue js_kv_map_set_read_only(
|
||||
JSContext* ctx, JSValueConst, int, JSValueConst*)
|
||||
{
|
||||
return JS_ThrowTypeError(ctx, "Cannot call set on read-only map");
|
||||
}
|
||||
|
||||
static JSValue js_kv_map_foreach(
|
||||
JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
auto handle = static_cast<KVMap::Handle*>(
|
||||
JS_GetOpaque(this_val, kv_map_handle_class_id));
|
||||
|
||||
if (argc != 1)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected 1", argc);
|
||||
|
||||
JSValue func = argv[0];
|
||||
|
||||
if (!JS_IsFunction(ctx, func))
|
||||
return JS_ThrowTypeError(ctx, "Argument must be a function");
|
||||
|
||||
bool failed = false;
|
||||
handle->foreach(
|
||||
[ctx, this_val, func, &failed](const auto& k, const auto& v) {
|
||||
JSValue args[3];
|
||||
|
||||
// JS forEach expects (v, k, map) rather than (k, v)
|
||||
args[0] = JS_NewArrayBufferCopy(ctx, v.data(), v.size());
|
||||
args[1] = JS_NewArrayBufferCopy(ctx, k.data(), k.size());
|
||||
args[2] = JS_DupValue(ctx, this_val);
|
||||
|
||||
auto val = JS_Call(ctx, func, JS_UNDEFINED, 3, args);
|
||||
|
||||
JS_FreeValue(ctx, args[0]);
|
||||
JS_FreeValue(ctx, args[1]);
|
||||
JS_FreeValue(ctx, args[2]);
|
||||
|
||||
if (JS_IsException(val))
|
||||
{
|
||||
js_dump_error(ctx);
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_FreeValue(ctx, val);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (failed)
|
||||
{
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static int js_kv_lookup(
|
||||
JSContext* ctx,
|
||||
JSPropertyDescriptor* desc,
|
||||
JSValueConst this_val,
|
||||
JSAtom property)
|
||||
{
|
||||
const auto property_name_c = JS_AtomToCString(ctx, property);
|
||||
const std::string property_name(property_name_c);
|
||||
JS_FreeCString(ctx, property_name_c);
|
||||
LOG_TRACE_FMT("Looking for kv map '{}'", property_name);
|
||||
|
||||
const auto [security_domain, access_category] =
|
||||
kv::parse_map_name(property_name);
|
||||
|
||||
auto read_only = false;
|
||||
switch (access_category)
|
||||
{
|
||||
case kv::AccessCategory::INTERNAL:
|
||||
{
|
||||
if (security_domain == kv::SecurityDomain::PUBLIC)
|
||||
{
|
||||
read_only = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(fmt::format(
|
||||
"JS application cannot access private internal CCF table '{}'",
|
||||
property_name));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kv::AccessCategory::GOVERNANCE:
|
||||
{
|
||||
read_only = true;
|
||||
break;
|
||||
}
|
||||
case kv::AccessCategory::APPLICATION:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw std::logic_error(fmt::format(
|
||||
"Unhandled AccessCategory for table '{}'", property_name));
|
||||
}
|
||||
}
|
||||
|
||||
auto tx_ptr = static_cast<kv::Tx*>(JS_GetOpaque(this_val, kv_class_id));
|
||||
auto handle = tx_ptr->rw<KVMap>(property_name);
|
||||
|
||||
// This follows the interface of Map:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
|
||||
// Keys and values are ArrayBuffers. Keys are matched based on their
|
||||
// contents.
|
||||
auto view_val = JS_NewObjectClass(ctx, kv_map_handle_class_id);
|
||||
JS_SetOpaque(view_val, handle);
|
||||
|
||||
JS_SetPropertyStr(
|
||||
ctx, view_val, "has", JS_NewCFunction(ctx, js_kv_map_has, "has", 1));
|
||||
|
||||
JS_SetPropertyStr(
|
||||
ctx, view_val, "get", JS_NewCFunction(ctx, js_kv_map_get, "get", 1));
|
||||
|
||||
auto setter = js_kv_map_set;
|
||||
auto deleter = js_kv_map_delete;
|
||||
|
||||
if (read_only)
|
||||
{
|
||||
setter = js_kv_map_set_read_only;
|
||||
deleter = js_kv_map_delete_read_only;
|
||||
}
|
||||
|
||||
JS_SetPropertyStr(
|
||||
ctx, view_val, "set", JS_NewCFunction(ctx, setter, "set", 2));
|
||||
JS_SetPropertyStr(
|
||||
ctx, view_val, "delete", JS_NewCFunction(ctx, deleter, "delete", 1));
|
||||
|
||||
JS_SetPropertyStr(
|
||||
ctx,
|
||||
view_val,
|
||||
"forEach",
|
||||
JS_NewCFunction(ctx, js_kv_map_foreach, "forEach", 1));
|
||||
|
||||
desc->flags = 0;
|
||||
desc->value = view_val;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JSValue js_body_text(
|
||||
JSContext* ctx,
|
||||
JSValueConst this_val,
|
||||
int argc,
|
||||
[[maybe_unused]] JSValueConst* argv)
|
||||
{
|
||||
if (argc != 0)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected none", argc);
|
||||
|
||||
auto body = static_cast<const std::vector<uint8_t>*>(
|
||||
JS_GetOpaque(this_val, body_class_id));
|
||||
auto body_ = JS_NewStringLen(ctx, (const char*)body->data(), body->size());
|
||||
return body_;
|
||||
}
|
||||
|
||||
JSValue js_body_json(
|
||||
JSContext* ctx,
|
||||
JSValueConst this_val,
|
||||
int argc,
|
||||
[[maybe_unused]] JSValueConst* argv)
|
||||
{
|
||||
if (argc != 0)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected none", argc);
|
||||
|
||||
auto body = static_cast<const std::vector<uint8_t>*>(
|
||||
JS_GetOpaque(this_val, body_class_id));
|
||||
std::string body_str(body->begin(), body->end());
|
||||
auto body_ = JS_ParseJSON(ctx, body_str.c_str(), body->size(), "<body>");
|
||||
return body_;
|
||||
}
|
||||
|
||||
JSValue js_body_array_buffer(
|
||||
JSContext* ctx,
|
||||
JSValueConst this_val,
|
||||
int argc,
|
||||
[[maybe_unused]] JSValueConst* argv)
|
||||
{
|
||||
if (argc != 0)
|
||||
return JS_ThrowTypeError(
|
||||
ctx, "Passed %d arguments, but expected none", argc);
|
||||
|
||||
auto body = static_cast<const std::vector<uint8_t>*>(
|
||||
JS_GetOpaque(this_val, body_class_id));
|
||||
auto body_ = JS_NewArrayBufferCopy(ctx, body->data(), body->size());
|
||||
return body_;
|
||||
}
|
||||
|
||||
// Partially replicates https://developer.mozilla.org/en-US/docs/Web/API/Body
|
||||
// with a synchronous interface.
|
||||
static const JSCFunctionListEntry js_body_proto_funcs[] = {
|
||||
JS_CFUNC_DEF("text", 0, js_body_text),
|
||||
JS_CFUNC_DEF("json", 0, js_body_json),
|
||||
JS_CFUNC_DEF("arrayBuffer", 0, js_body_array_buffer),
|
||||
};
|
||||
|
||||
// Not thread-safe, must happen exactly once
|
||||
void register_class_ids()
|
||||
{
|
||||
JS_NewClassID(&kv_class_id);
|
||||
kv_exotic_methods.get_own_property = js_kv_lookup;
|
||||
kv_class_def.class_name = "KV Tables";
|
||||
kv_class_def.exotic = &kv_exotic_methods;
|
||||
|
||||
JS_NewClassID(&kv_map_handle_class_id);
|
||||
kv_map_handle_class_def.class_name = "KV Map Handle";
|
||||
|
||||
JS_NewClassID(&body_class_id);
|
||||
body_class_def.class_name = "Body";
|
||||
}
|
||||
|
||||
JSValue js_print(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
|
||||
{
|
||||
int i;
|
||||
const char* str;
|
||||
std::stringstream ss;
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
ss << ' ';
|
||||
if (!JS_IsError(ctx, argv[i]) && JS_IsObject(argv[i]))
|
||||
{
|
||||
JSValue rval = JS_JSONStringify(ctx, argv[i], JS_NULL, JS_NULL);
|
||||
str = JS_ToCString(ctx, rval);
|
||||
JS_FreeValue(ctx, rval);
|
||||
}
|
||||
else
|
||||
str = JS_ToCString(ctx, argv[i]);
|
||||
if (!str)
|
||||
return JS_EXCEPTION;
|
||||
ss << str;
|
||||
JS_FreeCString(ctx, str);
|
||||
}
|
||||
LOG_INFO << ss.str() << std::endl;
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void js_dump_error(JSContext* ctx)
|
||||
{
|
||||
JSValue exception_val = JS_GetException(ctx);
|
||||
|
||||
JSValue val;
|
||||
const char* stack;
|
||||
bool is_error;
|
||||
|
||||
is_error = JS_IsError(ctx, exception_val);
|
||||
if (!is_error)
|
||||
LOG_INFO_FMT("Throw: ");
|
||||
js_print(ctx, JS_NULL, 1, (JSValueConst*)&exception_val);
|
||||
if (is_error)
|
||||
{
|
||||
val = JS_GetPropertyStr(ctx, exception_val, "stack");
|
||||
if (!JS_IsUndefined(val))
|
||||
{
|
||||
stack = JS_ToCString(ctx, val);
|
||||
LOG_INFO_FMT("{}", stack);
|
||||
|
||||
JS_FreeCString(ctx, stack);
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
}
|
||||
|
||||
JS_FreeValue(ctx, exception_val);
|
||||
}
|
||||
|
||||
JSValue Context::function(const std::string& code, const std::string& path)
|
||||
{
|
||||
JSValue module = JS_Eval(
|
||||
ctx,
|
||||
code.c_str(),
|
||||
code.size(),
|
||||
path.c_str(),
|
||||
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
|
||||
|
||||
if (JS_IsException(module))
|
||||
{
|
||||
js_dump_error(ctx);
|
||||
throw std::runtime_error(fmt::format("Failed to compile {}", path));
|
||||
}
|
||||
|
||||
auto eval_val = JS_EvalFunction(ctx, module);
|
||||
if (JS_IsException(eval_val))
|
||||
{
|
||||
js_dump_error(ctx);
|
||||
JS_FreeValue(ctx, eval_val);
|
||||
throw std::runtime_error(fmt::format("Failed to execute {}", path));
|
||||
}
|
||||
JS_FreeValue(ctx, eval_val);
|
||||
|
||||
// Get exported function from module
|
||||
assert(JS_VALUE_GET_TAG(module) == JS_TAG_MODULE);
|
||||
auto module_def = (JSModuleDef*)JS_VALUE_GET_PTR(module);
|
||||
if (JS_GetModuleExportEntriesCount(module_def) != 1)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Endpoint module exports more than one function");
|
||||
}
|
||||
|
||||
auto export_func = JS_GetModuleExportEntry(ctx, module_def, 0);
|
||||
if (!JS_IsFunction(ctx, export_func))
|
||||
{
|
||||
JS_FreeValue(ctx, export_func);
|
||||
throw std::runtime_error(
|
||||
"Endpoint module exports something that is not a function");
|
||||
}
|
||||
|
||||
return export_func;
|
||||
}
|
||||
|
||||
void register_request_body_class(JSContext* ctx)
|
||||
{
|
||||
// Set prototype for request body class
|
||||
JSValue body_proto = JS_NewObject(ctx);
|
||||
size_t func_count =
|
||||
sizeof(js_body_proto_funcs) / sizeof(js_body_proto_funcs[0]);
|
||||
JS_SetPropertyFunctionList(
|
||||
ctx, body_proto, js_body_proto_funcs, func_count);
|
||||
JS_SetClassProto(ctx, body_class_id, body_proto);
|
||||
}
|
||||
|
||||
static JSValue create_console_obj(JSContext* ctx)
|
||||
{
|
||||
auto console = JS_NewObject(ctx);
|
||||
|
||||
JS_SetPropertyStr(
|
||||
ctx, console, "log", JS_NewCFunction(ctx, js_print, "log", 1));
|
||||
|
||||
return console;
|
||||
}
|
||||
|
||||
void populate_global_console(JSContext* ctx)
|
||||
{
|
||||
auto global_obj = JS_GetGlobalObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global_obj, "console", create_console_obj(ctx));
|
||||
JS_FreeValue(ctx, global_obj);
|
||||
}
|
||||
|
||||
JSValue create_ccf_obj(
|
||||
kv::Tx* tx,
|
||||
const std::optional<kv::TxID>& transaction_id,
|
||||
ccf::historical::TxReceiptPtr receipt,
|
||||
JSContext* ctx)
|
||||
{
|
||||
auto ccf = JS_NewObject(ctx);
|
||||
|
||||
JS_SetPropertyStr(
|
||||
ctx, ccf, "strToBuf", JS_NewCFunction(ctx, js_str_to_buf, "strToBuf", 1));
|
||||
JS_SetPropertyStr(
|
||||
ctx, ccf, "bufToStr", JS_NewCFunction(ctx, js_buf_to_str, "bufToStr", 1));
|
||||
JS_SetPropertyStr(
|
||||
ctx,
|
||||
ccf,
|
||||
"jsonCompatibleToBuf",
|
||||
JS_NewCFunction(
|
||||
ctx, js_json_compatible_to_buf, "jsonCompatibleToBuf", 1));
|
||||
JS_SetPropertyStr(
|
||||
ctx,
|
||||
ccf,
|
||||
"bufToJsonCompatible",
|
||||
JS_NewCFunction(
|
||||
ctx, js_buf_to_json_compatible, "bufToJsonCompatible", 1));
|
||||
JS_SetPropertyStr(
|
||||
ctx,
|
||||
ccf,
|
||||
"generateAesKey",
|
||||
JS_NewCFunction(ctx, js_generate_aes_key, "generateAesKey", 1));
|
||||
JS_SetPropertyStr(
|
||||
ctx,
|
||||
ccf,
|
||||
"generateRsaKeyPair",
|
||||
JS_NewCFunction(ctx, js_generate_rsa_key_pair, "generateRsaKeyPair", 1));
|
||||
JS_SetPropertyStr(
|
||||
ctx, ccf, "wrapKey", JS_NewCFunction(ctx, js_wrap_key, "wrapKey", 3));
|
||||
|
||||
if (tx != nullptr)
|
||||
{
|
||||
auto kv = JS_NewObjectClass(ctx, kv_class_id);
|
||||
JS_SetOpaque(kv, tx);
|
||||
JS_SetPropertyStr(ctx, ccf, "kv", kv);
|
||||
}
|
||||
|
||||
// Historical queries
|
||||
if (receipt)
|
||||
{
|
||||
auto state = JS_NewObject(ctx);
|
||||
|
||||
ccf::TxID tx_id;
|
||||
tx_id.seqno = static_cast<ccf::SeqNo>(transaction_id.value().version);
|
||||
tx_id.view = static_cast<ccf::View>(transaction_id.value().term);
|
||||
JS_SetPropertyStr(
|
||||
ctx, state, "transactionId", JS_NewString(ctx, tx_id.to_str().c_str()));
|
||||
|
||||
ccf::GetReceipt::Out receipt_out;
|
||||
receipt_out.from_receipt(receipt);
|
||||
auto js_receipt = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(
|
||||
ctx,
|
||||
js_receipt,
|
||||
"signature",
|
||||
JS_NewString(ctx, receipt_out.signature.c_str()));
|
||||
JS_SetPropertyStr(
|
||||
ctx, js_receipt, "root", JS_NewString(ctx, receipt_out.root.c_str()));
|
||||
JS_SetPropertyStr(
|
||||
ctx, js_receipt, "leaf", JS_NewString(ctx, receipt_out.leaf.c_str()));
|
||||
JS_SetPropertyStr(
|
||||
ctx,
|
||||
js_receipt,
|
||||
"nodeId",
|
||||
JS_NewString(ctx, receipt_out.node_id.value().c_str()));
|
||||
auto proof = JS_NewArray(ctx);
|
||||
uint32_t i = 0;
|
||||
for (auto& element : receipt_out.proof)
|
||||
{
|
||||
auto js_element = JS_NewObject(ctx);
|
||||
auto is_left = element.left.has_value();
|
||||
JS_SetPropertyStr(
|
||||
ctx,
|
||||
js_element,
|
||||
is_left ? "left" : "right",
|
||||
JS_NewString(
|
||||
ctx, (is_left ? element.left : element.right).value().c_str()));
|
||||
JS_DefinePropertyValueUint32(
|
||||
ctx, proof, i++, js_element, JS_PROP_C_W_E);
|
||||
}
|
||||
JS_SetPropertyStr(ctx, js_receipt, "proof", proof);
|
||||
JS_SetPropertyStr(ctx, state, "receipt", js_receipt);
|
||||
JS_SetPropertyStr(ctx, ccf, "historicalState", state);
|
||||
}
|
||||
|
||||
return ccf;
|
||||
}
|
||||
|
||||
void populate_global_ccf(
|
||||
kv::Tx* tx,
|
||||
const std::optional<kv::TxID>& transaction_id,
|
||||
ccf::historical::TxReceiptPtr receipt,
|
||||
JSContext* ctx)
|
||||
{
|
||||
auto global_obj = JS_GetGlobalObject(ctx);
|
||||
|
||||
JS_SetPropertyStr(
|
||||
ctx, global_obj, "ccf", create_ccf_obj(tx, transaction_id, receipt, ctx));
|
||||
|
||||
JS_FreeValue(ctx, global_obj);
|
||||
}
|
||||
|
||||
void Runtime::add_ccf_classdefs()
|
||||
{
|
||||
// Register class for KV
|
||||
{
|
||||
auto ret = JS_NewClass(rt, kv_class_id, &kv_class_def);
|
||||
if (ret != 0)
|
||||
{
|
||||
throw std::logic_error("Failed to register JS class definition for KV");
|
||||
}
|
||||
}
|
||||
|
||||
// Register class for KV map views
|
||||
{
|
||||
auto ret =
|
||||
JS_NewClass(rt, kv_map_handle_class_id, &kv_map_handle_class_def);
|
||||
if (ret != 0)
|
||||
{
|
||||
throw std::logic_error(
|
||||
"Failed to register JS class definition for KVMap");
|
||||
}
|
||||
}
|
||||
|
||||
// Register class for request body
|
||||
{
|
||||
auto ret = JS_NewClass(rt, body_class_id, &body_class_def);
|
||||
if (ret != 0)
|
||||
{
|
||||
throw std::logic_error(
|
||||
"Failed to register JS class definition for Body");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the Apache 2.0 License.
|
||||
#pragma once
|
||||
|
||||
#include "ds/logger.h"
|
||||
#include "historical_queries_interface.h"
|
||||
#include "kv/kv_types.h"
|
||||
#include "kv/tx.h"
|
||||
|
||||
#include <memory>
|
||||
#include <quickjs/quickjs-exports.h>
|
||||
#include <quickjs/quickjs.h>
|
||||
|
||||
namespace js
|
||||
{
|
||||
extern JSClassID kv_class_id;
|
||||
extern JSClassID kv_map_handle_class_id;
|
||||
extern JSClassID body_class_id;
|
||||
|
||||
extern JSClassDef kv_class_def;
|
||||
extern JSClassExoticMethods kv_exotic_methods;
|
||||
extern JSClassDef kv_map_handle_class_def;
|
||||
extern JSClassDef body_class_def;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wc99-extensions"
|
||||
|
||||
void register_class_ids();
|
||||
void register_request_body_class(JSContext* ctx);
|
||||
void populate_global_console(JSContext* ctx);
|
||||
void populate_global_ccf(
|
||||
kv::Tx* tx,
|
||||
const std::optional<kv::TxID>& transaction_id,
|
||||
ccf::historical::TxReceiptPtr receipt,
|
||||
JSContext* ctx);
|
||||
|
||||
JSValue js_print(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv);
|
||||
void js_dump_error(JSContext* ctx);
|
||||
|
||||
JSValue js_body_text(
|
||||
JSContext* ctx,
|
||||
JSValueConst this_val,
|
||||
int argc,
|
||||
[[maybe_unused]] JSValueConst* argv);
|
||||
|
||||
JSValue js_body_json(
|
||||
JSContext* ctx,
|
||||
JSValueConst this_val,
|
||||
int argc,
|
||||
[[maybe_unused]] JSValueConst* argv);
|
||||
|
||||
JSValue js_body_array_buffer(
|
||||
JSContext* ctx,
|
||||
JSValueConst this_val,
|
||||
int argc,
|
||||
[[maybe_unused]] JSValueConst* argv);
|
||||
|
||||
class Runtime
|
||||
{
|
||||
JSRuntime* rt;
|
||||
|
||||
public:
|
||||
inline Runtime(
|
||||
size_t max_stack_size = 1024 * 1024,
|
||||
size_t max_heap_size = 100 * 1024 * 1024)
|
||||
{
|
||||
rt = JS_NewRuntime();
|
||||
if (rt == nullptr)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialise QuickJS runtime");
|
||||
}
|
||||
JS_SetMaxStackSize(rt, max_stack_size);
|
||||
JS_SetMemoryLimit(rt, max_heap_size);
|
||||
}
|
||||
|
||||
inline ~Runtime()
|
||||
{
|
||||
JS_FreeRuntime(rt);
|
||||
}
|
||||
|
||||
inline operator JSRuntime*() const
|
||||
{
|
||||
return rt;
|
||||
}
|
||||
|
||||
void add_ccf_classdefs();
|
||||
};
|
||||
|
||||
class Context
|
||||
{
|
||||
JSContext* ctx;
|
||||
|
||||
public:
|
||||
inline Context(JSRuntime* rt)
|
||||
{
|
||||
ctx = JS_NewContext(rt);
|
||||
if (ctx == nullptr)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialise QuickJS context");
|
||||
}
|
||||
JS_SetContextOpaque(ctx, this);
|
||||
}
|
||||
|
||||
inline ~Context()
|
||||
{
|
||||
JS_FreeContext(ctx);
|
||||
}
|
||||
|
||||
inline operator JSContext*() const
|
||||
{
|
||||
return ctx;
|
||||
}
|
||||
|
||||
struct JSWrappedValue
|
||||
{
|
||||
inline JSWrappedValue(JSContext* ctx, JSValue&& val) :
|
||||
ctx(ctx),
|
||||
val(std::move(val))
|
||||
{}
|
||||
inline ~JSWrappedValue()
|
||||
{
|
||||
JS_FreeValue(ctx, val);
|
||||
}
|
||||
inline operator const JSValue&() const
|
||||
{
|
||||
return val;
|
||||
}
|
||||
JSContext* ctx;
|
||||
JSValue val;
|
||||
};
|
||||
|
||||
struct JSWrappedCString
|
||||
{
|
||||
inline JSWrappedCString(JSContext* ctx, const char* cstr) :
|
||||
ctx(ctx),
|
||||
cstr(cstr)
|
||||
{}
|
||||
inline ~JSWrappedCString()
|
||||
{
|
||||
JS_FreeCString(ctx, cstr);
|
||||
}
|
||||
inline operator const char*() const
|
||||
{
|
||||
return cstr;
|
||||
}
|
||||
inline operator std::string() const
|
||||
{
|
||||
return std::string(cstr);
|
||||
}
|
||||
inline operator std::string_view() const
|
||||
{
|
||||
return std::string_view(cstr);
|
||||
}
|
||||
JSContext* ctx;
|
||||
const char* cstr;
|
||||
};
|
||||
|
||||
inline JSWrappedValue operator()(JSValue&& val)
|
||||
{
|
||||
return JSWrappedValue(ctx, std::move(val));
|
||||
};
|
||||
|
||||
inline JSWrappedCString operator()(const char* cstr)
|
||||
{
|
||||
return JSWrappedCString(ctx, cstr);
|
||||
};
|
||||
|
||||
JSValue function(const std::string& code, const std::string& path);
|
||||
};
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
#include "node/ledger_secrets.h"
|
||||
#include "node/members.h"
|
||||
#include "node/node_info_network.h"
|
||||
#include "tls/base64.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <openenclave/advanced/mallinfo.h>
|
||||
|
|
|
@ -66,7 +66,7 @@ namespace ccf
|
|||
|
||||
constexpr int64_t VIEW_UNKNOWN = std::numeric_limits<int64_t>::min();
|
||||
|
||||
static TxStatus evaluate_tx_status(
|
||||
[[maybe_unused]] static TxStatus evaluate_tx_status(
|
||||
int64_t target_view,
|
||||
int64_t target_seqno,
|
||||
int64_t local_view,
|
||||
|
|
Загрузка…
Ссылка в новой задаче