Created
October 6, 2023 17:42
-
-
Save sleepdefic1t/98bf4482a21a22ab19ecf8d813f7440c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define NAPI_EXPERIMENTAL | |
#include <assert.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <node_api.h> | |
#include <uv.h> | |
#include "evmc/evmc.h" | |
#include "evmc/loader.h" | |
#define GET_AND_THROW_LAST_ERROR(env) \ | |
do { \ | |
const napi_extended_error_info* error_info = NULL; \ | |
napi_get_last_error_info((env), &error_info); \ | |
const char* message = \ | |
error_info->error_message != NULL ? error_info->error_message : "Unknown error"; \ | |
bool is_pending; \ | |
napi_is_exception_pending((env), &is_pending); \ | |
if (!is_pending) { \ | |
napi_throw_error((env), NULL, message); \ | |
} \ | |
} while (0) | |
#define NAPI_CALL_RETURN_NULL(env, call) \ | |
do { \ | |
if ((call) != napi_ok) { \ | |
GET_AND_THROW_LAST_ERROR((env)); \ | |
return NULL; \ | |
} \ | |
} while (0) | |
#define NAPI_CALL(env, call) \ | |
do { \ | |
if ((call) != napi_ok) { \ | |
GET_AND_THROW_LAST_ERROR((env)); \ | |
} \ | |
} while (0) | |
struct evmc_js_context { | |
/** The Host interface. */ | |
const struct evmc_host_interface* host; | |
/** The EVMC instance. */ | |
struct evmc_vm* instance; | |
/** Reference to evm object */ | |
napi_ref object; | |
/** callbacks **/ | |
napi_threadsafe_function account_exists_fn; | |
napi_threadsafe_function get_storage_fn; | |
napi_threadsafe_function set_storage_fn; | |
napi_threadsafe_function get_balance_fn; | |
napi_threadsafe_function get_code_size_fn; | |
napi_threadsafe_function get_code_hash_fn; | |
napi_threadsafe_function copy_code_fn; | |
napi_threadsafe_function selfdestruct_fn; | |
napi_threadsafe_function call_fn; | |
napi_threadsafe_function get_tx_context_fn; | |
napi_threadsafe_function get_block_hash_fn; | |
napi_threadsafe_function emit_log_fn; | |
napi_threadsafe_function completer; | |
napi_threadsafe_function access_account_fn; | |
napi_threadsafe_function access_storage_fn; | |
/** if freed */ | |
bool released; | |
}; | |
#define EVMC_BYTES32_LEN 32 | |
#define EVMC_ADDRESS_LEN 20 | |
/** Creates a Uint8Array from a raw data buffer | |
* | |
* Parameters: | |
* - env: The NAPI environment | |
* - data: Pointer to the data buffer | |
* - length: Length of the data buffer | |
* - out: Output parameter that will hold the resulting Uint8Array | |
* | |
* Returns: | |
* - napi_status: The status code of the operation | |
*/ | |
napi_status create_uint8array_from_data( | |
napi_env env, uint8_t* data, size_t length, napi_value* out | |
) { | |
if (data == NULL || length == 0 || out == NULL || | |
(data[0] == '\0' && length > 0)) { | |
napi_throw_error(env, NULL, "Input value is invalid"); | |
return napi_invalid_arg; | |
} | |
void* array_buffer_data = NULL; | |
napi_value array_buffer; | |
NAPI_CALL(env, napi_create_arraybuffer(env, length, &array_buffer_data, &array_buffer)); | |
if (array_buffer_data == NULL) { | |
napi_throw_error(env, NULL, "ArrayBufferData is null"); | |
return napi_generic_failure; | |
} | |
memcpy(array_buffer_data, data, length); | |
return napi_create_typedarray(env, napi_uint8_array, length, array_buffer, 0, out); | |
} | |
/* Validates input is a Uint8Array, gets pointer to data and length. | |
* | |
* Parameters: | |
* - env: The NAPI environment | |
* - value: The input value to validate | |
* - data: Output parameter to store pointer to the Uint8Array's data buffer | |
* - length: Output parameter to store the length of the data buffer | |
* | |
* Returns: | |
* - napi_status: The status code of the operation | |
*/ | |
napi_status get_uint8array_data(napi_env env, napi_value value, uint8_t** data, size_t* length) { | |
// Check if value is a TypedArray | |
bool is_typedarray; | |
NAPI_CALL(env, napi_is_typedarray(env, value, &is_typedarray)); | |
if (!is_typedarray) { | |
napi_throw_error(env, NULL, "Input value is not a TypedArray"); | |
return napi_invalid_arg; | |
} | |
// Check if value is a Uint8Array | |
napi_typedarray_type type; | |
NAPI_CALL( | |
env, | |
napi_get_typedarray_info(env, value, &type, length, (void**) data, &value, NULL) | |
); | |
if (type != napi_uint8_array) { | |
napi_throw_error(env, NULL, "Input value is not a Uint8Array"); | |
return napi_invalid_arg; | |
} | |
// Get data length | |
size_t len; | |
NAPI_CALL(env, napi_get_arraybuffer_info(env, value, (void**) data, &len)); | |
*length = (size_t) len; | |
return napi_ok; | |
} | |
void create_uint8array_from_evmc_bytes32(napi_env env, const evmc_bytes32* bytes, napi_value* out) { | |
NAPI_CALL( | |
env, | |
create_uint8array_from_data(env, (uint8_t*) &bytes->bytes, EVMC_BYTES32_LEN, out) | |
); | |
} | |
void create_uint8array_from_evmc_address( | |
napi_env env, const evmc_address* address, napi_value* out | |
) { | |
NAPI_CALL( | |
env, | |
create_uint8array_from_data(env, (uint8_t*) address->bytes, EVMC_ADDRESS_LEN, out) | |
); | |
} | |
void get_evmc_bytes32_from_uint8array(napi_env env, napi_value in, evmc_bytes32* out) { | |
uint8_t* data; | |
size_t length; | |
NAPI_CALL(env, get_uint8array_data(env, in, &data, &length)); | |
memcpy(out->bytes, data, EVMC_BYTES32_LEN); | |
} | |
void get_evmc_address_from_uint8array(napi_env env, napi_value in, evmc_address* out) { | |
uint8_t* data; | |
size_t length; | |
NAPI_CALL(env, get_uint8array_data(env, in, &data, &length)); | |
memcpy(out->bytes, data, EVMC_ADDRESS_LEN); | |
} | |
void create_bigint_from_evmc_bytes32(napi_env env, const evmc_bytes32* bytes, napi_value* out) { | |
uint64_t temp[4] = {0}; | |
temp[3] = __builtin_bswap64(*(uint64_t*) bytes->bytes); | |
temp[2] = __builtin_bswap64(*(uint64_t*) (bytes->bytes + 8)); | |
temp[1] = __builtin_bswap64(*(uint64_t*) (bytes->bytes + 16)); | |
temp[0] = __builtin_bswap64(*(uint64_t*) (bytes->bytes + 24)); | |
NAPI_CALL(env, napi_create_bigint_words(env, 0, 4, temp, out)); | |
} | |
void get_evmc_bytes32_from_bigint(napi_env env, napi_value in, evmc_bytes32* out) { | |
uint64_t temp[4] = {0}; | |
size_t result_word_count = 4; | |
int sign_bit = 0; | |
// generate status | |
NAPI_CALL(env, napi_get_value_bigint_words(env, in, &sign_bit, &result_word_count, temp)); | |
// generate evmc32 | |
*((uint64_t*) out->bytes) = result_word_count > 3 ? __builtin_bswap64(temp[3]) : 0; | |
*((uint64_t*) (out->bytes + 8)) = result_word_count > 2 ? __builtin_bswap64(temp[2]) : 0; | |
*((uint64_t*) (out->bytes + 16)) = result_word_count > 1 ? __builtin_bswap64(temp[1]) : 0; | |
*((uint64_t*) (out->bytes + 24)) = __builtin_bswap64(temp[0]); | |
} | |
typedef void (*converter_fn)(napi_env env, napi_value value, void* data); | |
struct js_call { | |
uv_sem_t sem; | |
converter_fn converter; | |
}; | |
// TODO: Get rid of these assertions | |
// Throwing requires 'napi_env'. | |
// Callers to this function don't have thread-safe access to 'napi_env'. | |
// The assersions are back for now. | |
void js_call_and_wait(napi_threadsafe_function fn, struct js_call* calldata) { | |
napi_status status; | |
status = napi_acquire_threadsafe_function(fn); | |
assert(status == napi_ok); | |
int uv_status; | |
uv_status = uv_sem_init(&calldata->sem, 0); | |
assert(uv_status == 0); | |
status = napi_call_threadsafe_function(fn, calldata, napi_tsfn_blocking); | |
assert(status == napi_ok); | |
uv_sem_wait(&calldata->sem); | |
uv_sem_destroy(&calldata->sem); | |
status = napi_release_threadsafe_function(fn, napi_tsfn_release); | |
assert(status == napi_ok); | |
} | |
napi_value js_return_or_await_success(napi_env env, napi_callback_info info) { | |
napi_value argv[1]; | |
struct js_call* data; | |
size_t argc = 1; | |
NAPI_CALL_RETURN_NULL(env, napi_get_cb_info(env, info, &argc, argv, NULL, (void**) &data)); | |
if (data->converter != NULL) { | |
data->converter(env, argv[0], data); | |
} | |
uv_sem_post(&data->sem); | |
return NULL; | |
} | |
void js_return_or_await( | |
napi_env env, napi_value result, struct js_call* data, converter_fn converter | |
) { | |
bool is_promise = false; | |
NAPI_CALL(env, napi_is_promise(env, result, &is_promise)); | |
if (!is_promise) { | |
if (converter != NULL) { | |
converter(env, result, data); | |
} | |
uv_sem_post(&data->sem); | |
} else { | |
data->converter = converter; | |
napi_value then_callback; | |
NAPI_CALL(env, napi_get_named_property(env, result, "then", &then_callback)); | |
napi_value success_callback; | |
NAPI_CALL( | |
env, | |
napi_create_function(env, NULL, 0, js_return_or_await_success, data, &success_callback) | |
); | |
napi_value args[1]; | |
args[0] = success_callback; | |
NAPI_CALL(env, napi_call_function(env, result, then_callback, 1, args, NULL)); | |
} | |
} | |
struct js_set_storage_call { | |
struct js_call; | |
const evmc_address* address; | |
const evmc_bytes32* key; | |
const evmc_bytes32* value; | |
enum evmc_storage_status result; | |
}; | |
void set_storage_js_converter(napi_env env, napi_value result, struct js_set_storage_call* data) { | |
int64_t enumVal; | |
NAPI_CALL(env, napi_get_value_int64(env, result, &enumVal)); | |
data->result = enumVal; | |
} | |
void set_storage_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_set_storage_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[3]; | |
create_uint8array_from_evmc_address(env, data->address, &values[0]); | |
create_uint8array_from_evmc_bytes32(env, data->key, &values[1]); | |
create_uint8array_from_evmc_bytes32(env, data->value, &values[2]); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 3, values, &result)); | |
js_return_or_await( | |
env, | |
result, | |
(struct js_call*) data, | |
(converter_fn) set_storage_js_converter | |
); | |
} | |
enum evmc_storage_status set_storage( | |
struct evmc_js_context* context, | |
const evmc_address* address, | |
const evmc_bytes32* key, | |
const evmc_bytes32* value | |
) { | |
struct js_set_storage_call callinfo; | |
callinfo.address = address; | |
callinfo.key = key; | |
callinfo.value = value; | |
js_call_and_wait(context->set_storage_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_storage_call { | |
struct js_call; | |
const evmc_address* address; | |
const evmc_bytes32* key; | |
evmc_bytes32 result; | |
}; | |
void get_storage_js_converter(napi_env env, napi_value result, struct js_storage_call* data) { | |
get_evmc_bytes32_from_uint8array(env, result, &data->result); | |
} | |
void get_storage_js( | |
napi_env env, napi_value js_callback, struct evmc_js_context* ctx, struct js_storage_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[2]; | |
create_uint8array_from_evmc_address(env, data->address, &values[0]); | |
create_uint8array_from_evmc_bytes32(env, data->key, &values[1]); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 2, values, &result)); | |
js_return_or_await( | |
env, | |
result, | |
(struct js_call*) data, | |
(converter_fn) get_storage_js_converter | |
); | |
} | |
evmc_bytes32 get_storage( | |
struct evmc_js_context* context, const evmc_address* address, const evmc_bytes32* key | |
) { | |
struct js_storage_call callinfo; | |
callinfo.address = address; | |
callinfo.key = key; | |
js_call_and_wait(context->get_storage_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_account_exists_call { | |
struct js_call; | |
const evmc_address* address; | |
bool result; | |
}; | |
void account_exists_js_converter( | |
napi_env env, napi_value result, struct js_account_exists_call* data | |
) { | |
NAPI_CALL(env, napi_get_value_bool(env, result, &data->result)); | |
} | |
void account_exists_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_account_exists_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[1]; | |
create_uint8array_from_evmc_address(env, data->address, &values[0]); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 1, values, &result)); | |
js_return_or_await( | |
env, | |
result, | |
(struct js_call*) data, | |
(converter_fn) account_exists_js_converter | |
); | |
} | |
bool account_exists(struct evmc_js_context* context, const evmc_address* address) { | |
struct js_account_exists_call callinfo; | |
callinfo.address = address; | |
js_call_and_wait(context->account_exists_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_get_balance_call { | |
struct js_call; | |
const evmc_address* address; | |
evmc_bytes32 result; | |
}; | |
void get_balance_js_converter(napi_env env, napi_value result, struct js_get_balance_call* data) { | |
get_evmc_bytes32_from_bigint(env, result, &data->result); | |
} | |
void get_balance_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_get_balance_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[1]; | |
create_uint8array_from_evmc_address(env, data->address, &values[0]); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 1, values, &result)); | |
js_return_or_await( | |
env, | |
result, | |
(struct js_call*) data, | |
(converter_fn) get_balance_js_converter | |
); | |
} | |
evmc_bytes32 get_balance(struct evmc_js_context* context, const evmc_address* address) { | |
struct js_get_balance_call callinfo; | |
callinfo.address = address; | |
js_call_and_wait(context->get_balance_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_get_code_size_call { | |
struct js_call; | |
const evmc_address* address; | |
size_t result; | |
}; | |
void get_code_size_js_converter( | |
napi_env env, napi_value result, struct js_get_code_size_call* data | |
) { | |
bool lossless; | |
NAPI_CALL(env, napi_get_value_bigint_uint64(env, result, (uint64_t*) &data->result, &lossless)); | |
} | |
void get_code_size_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_get_code_size_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[1]; | |
create_uint8array_from_evmc_address(env, data->address, &values[0]); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 1, values, &result)); | |
js_return_or_await( | |
env, | |
result, | |
(struct js_call*) data, | |
(converter_fn) get_code_size_js_converter | |
); | |
} | |
size_t get_code_size(struct evmc_js_context* context, const evmc_address* address) { | |
struct js_get_code_size_call callinfo; | |
callinfo.address = address; | |
js_call_and_wait(context->get_code_size_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_get_code_hash_call { | |
struct js_call; | |
const evmc_address* address; | |
evmc_bytes32 result; | |
}; | |
void get_code_hash_js_converter( | |
napi_env env, napi_value result, struct js_get_code_hash_call* data | |
) { | |
get_evmc_bytes32_from_uint8array(env, result, &data->result); | |
} | |
void get_code_hash_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_get_code_hash_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[1]; | |
create_uint8array_from_evmc_address(env, data->address, &values[0]); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 1, values, &result)); | |
js_return_or_await( | |
env, | |
result, | |
(struct js_call*) data, | |
(converter_fn) get_code_hash_js_converter | |
); | |
} | |
evmc_bytes32 get_code_hash(struct evmc_js_context* context, const evmc_address* address) { | |
struct js_get_code_hash_call callinfo; | |
callinfo.address = address; | |
js_call_and_wait(context->get_code_hash_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_copy_code_call { | |
struct js_call; | |
const evmc_address* address; | |
size_t code_offset; | |
uint8_t* buffer_data; | |
size_t buffer_size; | |
size_t result; | |
}; | |
void copy_code_js_converter(napi_env env, napi_value result, struct js_copy_code_call* data) { | |
char* node_buffer; | |
size_t node_buffer_length; | |
NAPI_CALL(env, napi_get_buffer_info(env, result, (void**) &node_buffer, &node_buffer_length)); | |
size_t bytes_written = | |
(node_buffer_length < data->buffer_size) ? node_buffer_length : data->buffer_size; | |
memcpy(data->buffer_data, (void**) node_buffer, bytes_written); | |
data->result = bytes_written; | |
} | |
void copy_code_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_copy_code_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[3]; | |
create_uint8array_from_evmc_address(env, data->address, &values[0]); | |
NAPI_CALL(env, napi_create_int64(env, data->code_offset, &values[1])); | |
NAPI_CALL(env, napi_create_int64(env, data->buffer_size, &values[2])); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 3, values, &result)); | |
js_return_or_await(env, result, (struct js_call*) data, (converter_fn) copy_code_js_converter); | |
} | |
size_t copy_code( | |
struct evmc_js_context* context, | |
const evmc_address* address, | |
size_t code_offset, | |
uint8_t* buffer_data, | |
size_t buffer_size | |
) { | |
struct js_copy_code_call callinfo; | |
callinfo.address = address; | |
callinfo.code_offset = code_offset; | |
callinfo.buffer_data = buffer_data; | |
callinfo.buffer_size = buffer_size; | |
js_call_and_wait(context->copy_code_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_selfdestruct_call { | |
struct js_call; | |
const evmc_address* address; | |
const evmc_address* beneficiary; | |
}; | |
void selfdestruct_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_selfdestruct_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[2]; | |
create_uint8array_from_evmc_address(env, data->address, &values[0]); | |
create_uint8array_from_evmc_address(env, data->beneficiary, &values[1]); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 2, values, &result)); | |
js_return_or_await(env, result, (struct js_call*) data, NULL); | |
} | |
void selfdestruct( | |
struct evmc_js_context* context, const evmc_address* address, const evmc_address* beneficiary | |
) { | |
struct js_selfdestruct_call callinfo; | |
callinfo.address = address; | |
callinfo.beneficiary = beneficiary; | |
js_call_and_wait(context->selfdestruct_fn, (struct js_call*) &callinfo); | |
} | |
struct js_call_call { | |
struct js_call; | |
const struct evmc_message* msg; | |
struct evmc_result* result; | |
}; | |
void call_free_result(const struct evmc_result* result) { | |
if (result->output_size > 0) { | |
free((void*) result->output_data); | |
} | |
} | |
void call_js_converter(napi_env env, napi_value result, struct js_call_call* data) { | |
napi_value node_status_code; | |
NAPI_CALL(env, napi_get_named_property(env, result, "statusCode", &node_status_code)); | |
int64_t int_status_code; | |
NAPI_CALL(env, napi_get_value_int64(env, node_status_code, &int_status_code)); | |
data->result->status_code = (enum evmc_status_code) int_status_code; | |
napi_value node_gas_left; | |
NAPI_CALL(env, napi_get_named_property(env, result, "gasLeft", &node_gas_left)); | |
bool gasLeftLossless = true; | |
NAPI_CALL( | |
env, | |
napi_get_value_bigint_int64(env, node_gas_left, &data->result->gas_left, &gasLeftLossless) | |
); | |
napi_value node_output_data; | |
NAPI_CALL(env, napi_get_named_property(env, result, "outputData", &node_output_data)); | |
uint8_t* outputData; | |
size_t outputData_size; | |
NAPI_CALL( | |
env, | |
napi_get_buffer_info(env, node_output_data, (void**) &outputData, &outputData_size) | |
); | |
data->result->output_size = outputData_size; | |
if (outputData_size > 0) { | |
data->result->output_data = (uint8_t*) malloc(outputData_size); | |
memcpy((void*) data->result->output_data, outputData, outputData_size); | |
data->result->release = call_free_result; | |
} | |
napi_value node_create_address; | |
NAPI_CALL(env, napi_get_named_property(env, result, "createAddress", &node_create_address)); | |
get_evmc_address_from_uint8array(env, node_create_address, &data->result->create_address); | |
} | |
void call_js( | |
napi_env env, napi_value js_callback, struct evmc_js_context* ctx, struct js_call_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[1]; | |
// create message object | |
NAPI_CALL(env, napi_create_object(env, &values[0])); | |
napi_value node_gas; | |
NAPI_CALL(env, napi_create_bigint_int64(env, data->msg->gas, &node_gas)); | |
NAPI_CALL(env, napi_set_named_property(env, values[0], "gas", node_gas)); | |
napi_value node_depth; | |
NAPI_CALL(env, napi_create_int32(env, data->msg->depth, &node_depth)); | |
NAPI_CALL(env, napi_set_named_property(env, values[0], "depth", node_depth)); | |
napi_value node_flags; | |
NAPI_CALL(env, napi_create_uint32(env, data->msg->flags, &node_flags)); | |
NAPI_CALL(env, napi_set_named_property(env, values[0], "flags", node_flags)); | |
napi_value node_kind; | |
NAPI_CALL(env, napi_create_int64(env, data->msg->kind, &node_kind)); | |
NAPI_CALL(env, napi_set_named_property(env, values[0], "kind", node_kind)); | |
napi_value node_recipient; | |
create_uint8array_from_evmc_address(env, &data->msg->recipient, &node_recipient); | |
NAPI_CALL(env, napi_set_named_property(env, values[0], "recipient", node_recipient)); | |
napi_value node_sender; | |
create_uint8array_from_evmc_address(env, &data->msg->sender, &node_sender); | |
NAPI_CALL(env, napi_set_named_property(env, values[0], "sender", node_sender)); | |
napi_value node_input; | |
void* input_node_buf; | |
NAPI_CALL( | |
env, | |
napi_create_buffer_copy( | |
env, | |
data->msg->input_size, | |
data->msg->input_data, | |
&input_node_buf, | |
&node_input | |
) | |
); | |
NAPI_CALL(env, napi_set_named_property(env, values[0], "inputData", node_input)); | |
napi_value node_value; | |
create_bigint_from_evmc_bytes32(env, &data->msg->value, &node_value); | |
NAPI_CALL(env, napi_set_named_property(env, values[0], "value", node_value)); | |
napi_value node_create2_salt; | |
create_uint8array_from_evmc_bytes32(env, &data->msg->create2_salt, &node_create2_salt); | |
NAPI_CALL(env, napi_set_named_property(env, values[0], "create2Salt", node_create2_salt)); | |
napi_value node_code_address; | |
create_uint8array_from_evmc_address(env, &data->msg->code_address, &node_code_address); | |
NAPI_CALL(env, napi_set_named_property(env, values[0], "codeAddress", node_code_address)); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 1, values, &result)); | |
js_return_or_await(env, result, (struct js_call*) data, (converter_fn) call_js_converter); | |
} | |
struct evmc_result call(struct evmc_js_context* context, const struct evmc_message* msg) { | |
struct evmc_result result; | |
result.status_code = 0; | |
result.output_data = NULL; | |
result.output_size = 0; | |
result.gas_left = 0; | |
result.release = NULL; | |
struct js_call_call callinfo; | |
callinfo.msg = msg; | |
callinfo.result = &result; | |
js_call_and_wait(context->call_fn, (struct js_call*) &callinfo); | |
return result; | |
} | |
struct js_tx_context_call { | |
struct js_call; | |
struct evmc_tx_context result; | |
}; | |
void get_tx_context_js_converter(napi_env env, napi_value result, struct js_tx_context_call* data) { | |
napi_value node_tx_gas_price; | |
NAPI_CALL(env, napi_get_named_property(env, result, "txGasPrice", &node_tx_gas_price)); | |
get_evmc_bytes32_from_bigint(env, node_tx_gas_price, &data->result.tx_gas_price); | |
napi_value node_tx_origin; | |
NAPI_CALL(env, napi_get_named_property(env, result, "txOrigin", &node_tx_origin)); | |
get_evmc_address_from_uint8array(env, node_tx_origin, &data->result.tx_origin); | |
napi_value node_blockcoinbase; | |
NAPI_CALL(env, napi_get_named_property(env, result, "blockCoinbase", &node_blockcoinbase)); | |
get_evmc_address_from_uint8array(env, node_blockcoinbase, &data->result.block_coinbase); | |
napi_value node_block_number; | |
NAPI_CALL(env, napi_get_named_property(env, result, "blockNumber", &node_block_number)); | |
bool block_number_lossless = true; | |
NAPI_CALL( | |
env, | |
napi_get_value_bigint_int64( | |
env, | |
node_block_number, | |
&data->result.block_number, | |
&block_number_lossless | |
) | |
); | |
napi_value node_timestamp; | |
NAPI_CALL(env, napi_get_named_property(env, result, "blockTimestamp", &node_timestamp)); | |
bool block_timestamp_lossless = true; | |
NAPI_CALL( | |
env, | |
napi_get_value_bigint_int64( | |
env, | |
node_timestamp, | |
&data->result.block_timestamp, | |
&block_timestamp_lossless | |
) | |
); | |
napi_value node_gas_limit; | |
NAPI_CALL(env, napi_get_named_property(env, result, "blockGasLimit", &node_gas_limit)); | |
bool block_gas_limit_lossless = true; | |
NAPI_CALL( | |
env, | |
napi_get_value_bigint_int64( | |
env, | |
node_gas_limit, | |
&data->result.block_gas_limit, | |
&block_gas_limit_lossless | |
) | |
); | |
napi_value node_block_prev_randao; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, result, "blockPrevRandao", &node_block_prev_randao) | |
); | |
get_evmc_bytes32_from_bigint(env, node_block_prev_randao, &data->result.block_prev_randao); | |
napi_value node_chain_id; | |
NAPI_CALL(env, napi_get_named_property(env, result, "chainId", &node_chain_id)); | |
get_evmc_bytes32_from_bigint(env, node_chain_id, &data->result.chain_id); | |
napi_value node_block_base_fee; | |
NAPI_CALL(env, napi_get_named_property(env, result, "blockBaseFee", &node_block_base_fee)); | |
get_evmc_bytes32_from_bigint(env, node_block_base_fee, &data->result.block_base_fee); | |
} | |
void get_tx_context_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_tx_context_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 0, NULL, &result)); | |
js_return_or_await( | |
env, | |
result, | |
(struct js_call*) data, | |
(converter_fn) get_tx_context_js_converter | |
); | |
} | |
struct evmc_tx_context get_tx_context(struct evmc_js_context* context) { | |
struct js_tx_context_call callinfo; | |
js_call_and_wait(context->get_tx_context_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_get_block_hash_call { | |
struct js_call; | |
uint64_t number; | |
evmc_bytes32 result; | |
}; | |
void get_block_hash_js_converter( | |
napi_env env, napi_value result, struct js_get_block_hash_call* data | |
) { | |
get_evmc_bytes32_from_uint8array(env, result, &data->result); | |
} | |
void get_block_hash_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_get_block_hash_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[1]; | |
NAPI_CALL(env, napi_create_bigint_int64(env, data->number, &values[0])); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 1, values, &result)); | |
js_return_or_await( | |
env, | |
result, | |
(struct js_call*) data, | |
(converter_fn) get_block_hash_js_converter | |
); | |
} | |
evmc_bytes32 get_block_hash(struct evmc_js_context* context, uint64_t number) { | |
struct js_get_block_hash_call callinfo; | |
callinfo.number = number; | |
js_call_and_wait(context->get_block_hash_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_emit_log_call { | |
struct js_call; | |
const evmc_address* address; | |
const uint8_t* data; | |
size_t data_size; | |
const evmc_bytes32* topics; | |
size_t topics_count; | |
}; | |
void emit_log_js( | |
napi_env env, napi_value js_callback, struct evmc_js_context* ctx, struct js_emit_log_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[3]; | |
create_uint8array_from_evmc_address(env, data->address, &values[0]); | |
uint8_t* buffer; | |
NAPI_CALL( | |
env, | |
napi_create_buffer_copy( | |
env, | |
data->data_size, | |
(void*) data->data, | |
(void**) &buffer, | |
&values[1] | |
) | |
); | |
NAPI_CALL(env, napi_create_array_with_length(env, data->topics_count, &values[2])); | |
size_t i; | |
for (i = 0; i < data->topics_count; i++) { | |
napi_value topic; | |
create_uint8array_from_evmc_bytes32(env, &data->topics[i], &topic); | |
NAPI_CALL(env, napi_set_element(env, values[2], i, topic)); | |
} | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 3, values, &result)); | |
js_return_or_await(env, result, (struct js_call*) data, NULL); | |
} | |
void emit_log( | |
struct evmc_js_context* context, | |
const evmc_address* address, | |
const uint8_t* data, | |
size_t data_size, | |
const evmc_bytes32 topics[], | |
size_t topics_count | |
) { | |
struct js_emit_log_call callinfo; | |
callinfo.address = address; | |
callinfo.data = data; | |
callinfo.data_size = data_size; | |
callinfo.topics = topics; | |
callinfo.topics_count = topics_count; | |
js_call_and_wait(context->emit_log_fn, (struct js_call*) &callinfo); | |
} | |
struct js_access_account_call { | |
struct js_call; | |
const evmc_address* account; | |
enum evmc_access_status result; | |
}; | |
void access_account_js_converter( | |
napi_env env, napi_value result, struct js_access_account_call* data | |
) { | |
int64_t enumVal; | |
NAPI_CALL(env, napi_get_value_int64(env, result, &enumVal)); | |
data->result = enumVal; | |
} | |
void access_account_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_access_account_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[1]; | |
create_uint8array_from_evmc_address(env, data->account, &values[0]); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 1, values, &result)); | |
js_return_or_await( | |
env, | |
result, | |
(struct js_call*) data, | |
(converter_fn) access_account_js_converter | |
); | |
} | |
enum evmc_access_status access_account( | |
struct evmc_js_context* context, const evmc_address* account | |
) { | |
struct js_access_account_call callinfo; | |
callinfo.account = account; | |
js_call_and_wait(context->access_account_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_access_storage_call { | |
struct js_call; | |
const evmc_address* address; | |
const evmc_bytes32* key; | |
enum evmc_access_status result; | |
}; | |
void access_storage_js_converter( | |
napi_env env, napi_value result, struct js_access_storage_call* data | |
) { | |
int64_t enumVal; | |
NAPI_CALL(env, napi_get_value_int64(env, result, &enumVal)); | |
data->result = enumVal; | |
} | |
void access_storage_js( | |
napi_env env, | |
napi_value js_callback, | |
struct evmc_js_context* ctx, | |
struct js_access_storage_call* data | |
) { | |
napi_value object; | |
NAPI_CALL(env, napi_get_reference_value(env, ctx->object, &object)); | |
napi_value values[2]; | |
create_uint8array_from_evmc_address(env, data->address, &values[0]); | |
create_uint8array_from_evmc_bytes32(env, data->key, &values[1]); | |
napi_value result; | |
NAPI_CALL(env, napi_call_function(env, object, js_callback, 2, values, &result)); | |
js_return_or_await( | |
env, | |
result, | |
(struct js_call*) data, | |
(converter_fn) access_storage_js_converter | |
); | |
} | |
enum evmc_access_status access_storage( | |
struct evmc_js_context* context, const evmc_address* address, const evmc_bytes32* key | |
) { | |
struct js_access_storage_call callinfo; | |
callinfo.address = address; | |
callinfo.key = key; | |
js_call_and_wait(context->access_storage_fn, (struct js_call*) &callinfo); | |
return callinfo.result; | |
} | |
struct js_execution_context { | |
struct evmc_js_context* context; | |
struct evmc_message message; | |
enum evmc_revision revision; | |
struct evmc_result result; | |
uint8_t* code; | |
size_t code_size; | |
napi_deferred deferred; | |
napi_value promise; | |
}; | |
// Forward declaration | |
void completer_js( | |
napi_env env, napi_value js_callback, void* context, struct js_execution_context* data | |
); | |
void create_callbacks_from_context( | |
napi_env env, struct evmc_js_context* ctx, napi_value node_context | |
) { | |
napi_value unnamed; | |
NAPI_CALL(env, napi_create_string_utf8(env, "unnamed", NAPI_AUTO_LENGTH, &unnamed)); | |
napi_value account_exists_callback; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_context, "getAccountExists", &account_exists_callback) | |
); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
account_exists_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) account_exists_js, | |
&ctx->account_exists_fn | |
) | |
); | |
napi_value storage_callback; | |
NAPI_CALL(env, napi_get_named_property(env, node_context, "getStorage", &storage_callback)); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
storage_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) get_storage_js, | |
&ctx->get_storage_fn | |
) | |
); | |
napi_value set_storage_callback; | |
NAPI_CALL(env, napi_get_named_property(env, node_context, "setStorage", &set_storage_callback)); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
set_storage_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) set_storage_js, | |
&ctx->set_storage_fn | |
) | |
); | |
napi_value get_balance_callback; | |
NAPI_CALL(env, napi_get_named_property(env, node_context, "getBalance", &get_balance_callback)); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
get_balance_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) get_balance_js, | |
&ctx->get_balance_fn | |
) | |
); | |
napi_value get_code_size_callback; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_context, "getCodeSize", &get_code_size_callback) | |
); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
get_code_size_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) get_code_size_js, | |
&ctx->get_code_size_fn | |
) | |
); | |
napi_value get_code_hash_callback; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_context, "getCodeHash", &get_code_hash_callback) | |
); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
get_code_hash_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) get_code_hash_js, | |
&ctx->get_code_hash_fn | |
) | |
); | |
napi_value copy_code_callback; | |
NAPI_CALL(env, napi_get_named_property(env, node_context, "copyCode", ©_code_callback)); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
copy_code_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) copy_code_js, | |
&ctx->copy_code_fn | |
) | |
); | |
napi_value self_destruct_callback; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_context, "selfDestruct", &self_destruct_callback) | |
); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
self_destruct_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) selfdestruct_js, | |
&ctx->selfdestruct_fn | |
) | |
); | |
napi_value call_callback; | |
NAPI_CALL(env, napi_get_named_property(env, node_context, "call", &call_callback)); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
call_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) call_js, | |
&ctx->call_fn | |
) | |
); | |
napi_value tx_context_callback; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_context, "getTxContext", &tx_context_callback) | |
); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
tx_context_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) get_tx_context_js, | |
&ctx->get_tx_context_fn | |
) | |
); | |
napi_value get_block_hash_callback; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_context, "getBlockHash", &get_block_hash_callback) | |
); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
get_block_hash_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) get_block_hash_js, | |
&ctx->get_block_hash_fn | |
) | |
); | |
napi_value emit_log_callback; | |
NAPI_CALL(env, napi_get_named_property(env, node_context, "emitLog", &emit_log_callback)); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
emit_log_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) emit_log_js, | |
&ctx->emit_log_fn | |
) | |
); | |
napi_value access_account_callback; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_context, "accessAccount", &access_account_callback) | |
); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
access_account_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) access_account_js, | |
&ctx->access_account_fn | |
) | |
); | |
napi_value access_storage_callback; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_context, "accessStorage", &access_storage_callback) | |
); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
access_storage_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) access_storage_js, | |
&ctx->access_storage_fn | |
) | |
); | |
napi_value execute_complete_callback; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_context, "executeComplete", &execute_complete_callback) | |
); | |
NAPI_CALL( | |
env, | |
napi_create_threadsafe_function( | |
env, | |
execute_complete_callback, | |
NULL, | |
unnamed, | |
0, | |
1, | |
NULL, | |
NULL, | |
(void*) ctx, | |
(napi_threadsafe_function_call_js) completer_js, | |
&ctx->completer | |
) | |
); | |
} | |
void release_callbacks_from_context(napi_env env, struct evmc_js_context* ctx) { | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->account_exists_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->get_storage_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->set_storage_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->get_balance_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->get_code_size_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->get_code_hash_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->copy_code_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->selfdestruct_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->call_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->get_tx_context_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->get_block_hash_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->emit_log_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->access_account_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->access_storage_fn, napi_tsfn_release)); | |
NAPI_CALL(env, napi_release_threadsafe_function(ctx->completer, napi_tsfn_release)); | |
} | |
void completer_js( | |
napi_env env, napi_value js_callback, void* context, struct js_execution_context* data | |
) { | |
napi_value out; | |
NAPI_CALL(env, napi_create_object(env, &out)); | |
napi_value statusCode; | |
NAPI_CALL(env, napi_create_int32(env, data->result.status_code, &statusCode)); | |
NAPI_CALL(env, napi_set_named_property(env, out, "statusCode", statusCode)); | |
napi_value gasLeft; | |
NAPI_CALL(env, napi_create_bigint_int64(env, data->result.gas_left, &gasLeft)); | |
NAPI_CALL(env, napi_set_named_property(env, out, "gasLeft", gasLeft)); | |
napi_value outputData; | |
void* outputDataBuffer; | |
NAPI_CALL( | |
env, | |
napi_create_buffer_copy( | |
env, | |
data->result.output_size, | |
data->result.output_data, | |
&outputDataBuffer, | |
&outputData | |
) | |
); | |
NAPI_CALL(env, napi_set_named_property(env, out, "outputData", outputData)); | |
if (data->result.status_code == EVMC_SUCCESS) { | |
napi_value createAddress; | |
create_uint8array_from_evmc_address(env, &(data->result.create_address), &createAddress); | |
NAPI_CALL(env, napi_set_named_property(env, out, "createAddress", createAddress)); | |
} | |
if (data->result.release != NULL) { | |
data->result.release(&(data->result)); | |
} | |
NAPI_CALL(env, napi_resolve_deferred(env, data->deferred, out)); | |
free(data); | |
} | |
void execute_done(uv_work_t* work, int status) { | |
free(work); | |
} | |
void execute(uv_work_t* work) { | |
struct js_execution_context* data = (struct js_execution_context*) work->data; | |
data->result = data->context->instance->execute( | |
data->context->instance, | |
data->context->host, | |
(struct evmc_host_context*) data->context, | |
data->revision, | |
&data->message, | |
data->code, | |
data->code_size | |
); | |
if (data->code_size != 0) { | |
free(data->code); | |
} | |
if (data->message.input_size != 0) { | |
free((void*) data->message.input_data); | |
} | |
napi_call_threadsafe_function(data->context->completer, data, napi_tsfn_blocking); | |
} | |
struct evmc_host_interface host_interface; | |
napi_value evmc_execute_evm(napi_env env, napi_callback_info info) { | |
napi_value argv[2]; | |
size_t argc = 2; | |
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); | |
if (argc < 2) { | |
napi_throw_error(env, "EINVAL", "Too few arguments"); | |
return NULL; | |
} | |
// this needs to run on another thread, apparently, so we need to return a promise | |
struct js_execution_context* js_ctx = | |
(struct js_execution_context*) malloc(sizeof(struct js_execution_context)); | |
NAPI_CALL(env, napi_get_value_external(env, argv[0], (void*) &js_ctx->context)); | |
napi_value node_revision; | |
NAPI_CALL(env, napi_get_named_property(env, argv[1], "revision", &node_revision)); | |
NAPI_CALL(env, napi_get_value_int32(env, node_revision, (int32_t*) &js_ctx->revision)); | |
napi_value node_message; | |
NAPI_CALL(env, napi_get_named_property(env, argv[1], "message", &node_message)); | |
napi_value node_message_gas; | |
NAPI_CALL(env, napi_get_named_property(env, node_message, "gas", &node_message_gas)); | |
bool node_message_gas_lossless; | |
NAPI_CALL( | |
env, | |
napi_get_value_bigint_int64( | |
env, | |
node_message_gas, | |
&js_ctx->message.gas, | |
&node_message_gas_lossless | |
) | |
); | |
napi_value node_message_depth; | |
NAPI_CALL(env, napi_get_named_property(env, node_message, "depth", &node_message_depth)); | |
NAPI_CALL(env, napi_get_value_int32(env, node_message_depth, &js_ctx->message.depth)); | |
napi_value node_message_flags; | |
NAPI_CALL(env, napi_get_named_property(env, node_message, "flags", &node_message_flags)); | |
NAPI_CALL(env, napi_coerce_to_number(env, node_message_flags, &node_message_flags)); | |
NAPI_CALL(env, napi_get_value_uint32(env, node_message_flags, &js_ctx->message.flags)); | |
napi_value node_message_recipient; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_message, "recipient", &node_message_recipient) | |
); | |
get_evmc_address_from_uint8array(env, node_message_recipient, &js_ctx->message.recipient); | |
napi_value node_message_sender; | |
NAPI_CALL(env, napi_get_named_property(env, node_message, "sender", &node_message_sender)); | |
get_evmc_address_from_uint8array(env, node_message_sender, &js_ctx->message.sender); | |
napi_value node_message_input_data; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_message, "inputData", &node_message_input_data) | |
); | |
uint8_t* input_buffer; | |
NAPI_CALL( | |
env, | |
napi_get_buffer_info( | |
env, | |
node_message_input_data, | |
(void**) &input_buffer, | |
&js_ctx->message.input_size | |
) | |
); | |
if (js_ctx->message.input_size != 0) { | |
js_ctx->message.input_data = (uint8_t*) malloc(js_ctx->message.input_size); | |
memcpy((void*) js_ctx->message.input_data, input_buffer, js_ctx->message.input_size); | |
} else { | |
js_ctx->message.input_data = NULL; | |
} | |
napi_value node_message_value; | |
NAPI_CALL(env, napi_get_named_property(env, node_message, "value", &node_message_value)); | |
get_evmc_bytes32_from_bigint(env, node_message_value, &js_ctx->message.value); | |
napi_value node_message_create2_salt; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_message, "create2Salt", &node_message_create2_salt) | |
); | |
get_evmc_bytes32_from_uint8array(env, node_message_create2_salt, &js_ctx->message.create2_salt); | |
napi_value node_message_code_address; | |
NAPI_CALL( | |
env, | |
napi_get_named_property(env, node_message, "codeAddress", &node_message_code_address) | |
); | |
get_evmc_address_from_uint8array(env, node_message_code_address, &js_ctx->message.code_address); | |
napi_value node_message_kind; | |
NAPI_CALL(env, napi_get_named_property(env, node_message, "kind", &node_message_kind)); | |
int64_t message_kind; | |
NAPI_CALL(env, napi_get_value_int64(env, node_message_kind, &message_kind)); | |
js_ctx->message.kind = (enum evmc_call_kind) message_kind; | |
size_t code_size; | |
uint8_t* code; | |
napi_value node_code; | |
NAPI_CALL(env, napi_get_named_property(env, argv[1], "code", &node_code)); | |
NAPI_CALL(env, napi_get_buffer_info(env, node_code, (void**) &code, &code_size)); | |
js_ctx->code_size = code_size; | |
if (code_size > 0) { | |
js_ctx->code = (uint8_t*) malloc(code_size); | |
memcpy(js_ctx->code, code, code_size); | |
} | |
NAPI_CALL(env, napi_create_promise(env, &js_ctx->deferred, &js_ctx->promise)); | |
uv_work_t* work = (uv_work_t*) malloc((sizeof(uv_work_t))); | |
work->data = (void*) js_ctx; | |
uv_queue_work(uv_default_loop(), work, execute, execute_done); | |
return js_ctx->promise; | |
} | |
void evmc_cleanup_evm(napi_env env, void* finalize_data, void* finalize_hint) { | |
struct evmc_js_context* context = (struct evmc_js_context*) finalize_data; | |
if (!context->released) { | |
context->instance->destroy(context->instance); | |
release_callbacks_from_context(env, context); | |
} | |
free(context); | |
} | |
napi_value evmc_create_evm(napi_env env, napi_callback_info info) { | |
napi_value out; | |
size_t argc = 3; | |
napi_value argv[3]; | |
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); | |
if (argc != 3) { | |
napi_throw_error(env, "EINVAL", "Expected 3 arguments"); | |
} | |
char* path; | |
size_t path_size; | |
NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], NULL, 0, &path_size)); | |
// path_size excludes NULL terminator | |
path = (char*) malloc(path_size + 1); | |
NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], path, path_size + 1, &path_size)); | |
enum evmc_loader_error_code error_code; | |
struct evmc_vm* instance = evmc_load_and_create(path, &error_code); | |
free((void*) path); | |
if (error_code != EVMC_LOADER_SUCCESS) { | |
const napi_extended_error_info* error_info = NULL; | |
napi_get_last_error_info(env, &error_info); | |
napi_throw_error(env, NULL, error_info->error_message); | |
return NULL; | |
} | |
struct evmc_js_context* context = | |
(struct evmc_js_context*) malloc(sizeof(struct evmc_js_context)); | |
context->instance = instance; | |
context->host = &host_interface; | |
context->released = false; | |
// This creates a WEAK reference, which is OK because we only use the refrence from execute() | |
// which requires an instance of the EVM object itself. | |
napi_create_reference(env, argv[2], 0, &context->object); | |
create_callbacks_from_context(env, context, argv[1]); | |
NAPI_CALL(env, napi_create_external(env, context, evmc_cleanup_evm, NULL, &out)); | |
return out; | |
} | |
napi_value evmc_release_evm(napi_env env, napi_callback_info info) { | |
size_t argc = 1; | |
napi_value argv[1]; | |
NAPI_CALL_RETURN_NULL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); | |
if (argc != 1) { | |
napi_throw_error(env, "EINVAL", "Expected 1 argument"); | |
} | |
struct evmc_js_context* context; | |
NAPI_CALL_RETURN_NULL(env, napi_get_value_external(env, argv[0], (void**) &context)); | |
if (!context->released) { | |
context->instance->destroy(context->instance); | |
release_callbacks_from_context(env, context); | |
context->released = true; | |
} | |
return NULL; | |
} | |
napi_value init_all(napi_env env, napi_value exports) { | |
napi_value evmc_create_evm_fn; | |
napi_value evmc_execute_evm_fn; | |
napi_value evmc_release_evm_fn; | |
host_interface.account_exists = (evmc_account_exists_fn) account_exists; | |
host_interface.get_storage = (evmc_get_storage_fn) get_storage; | |
host_interface.set_storage = (evmc_set_storage_fn) set_storage; | |
host_interface.get_balance = (evmc_get_balance_fn) get_balance; | |
host_interface.get_code_size = (evmc_get_code_size_fn) get_code_size; | |
host_interface.get_code_hash = (evmc_get_code_hash_fn) get_code_hash; | |
host_interface.copy_code = (evmc_copy_code_fn) copy_code; | |
host_interface.selfdestruct = (evmc_selfdestruct_fn) selfdestruct; | |
host_interface.call = (evmc_call_fn) call; | |
host_interface.get_tx_context = (evmc_get_tx_context_fn) get_tx_context; | |
host_interface.get_block_hash = (evmc_get_block_hash_fn) get_block_hash; | |
host_interface.emit_log = (evmc_emit_log_fn) emit_log; | |
host_interface.access_account = (evmc_access_account_fn) access_account; | |
host_interface.access_storage = (evmc_access_storage_fn) access_storage; | |
napi_create_function(env, NULL, 0, evmc_create_evm, NULL, &evmc_create_evm_fn); | |
napi_create_function(env, NULL, 0, evmc_execute_evm, NULL, &evmc_execute_evm_fn); | |
napi_create_function(env, NULL, 0, evmc_release_evm, NULL, &evmc_release_evm_fn); | |
napi_set_named_property(env, exports, "createEvmcEvm", evmc_create_evm_fn); | |
napi_set_named_property(env, exports, "executeEvmcEvm", evmc_execute_evm_fn); | |
napi_set_named_property(env, exports, "releaseEvmcEvm", evmc_release_evm_fn); | |
return exports; | |
} | |
NAPI_MODULE(NODE_GYP_MODULE_NAME, init_all); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment