diff --git a/cli/builder/builder.c b/cli/builder/builder.c index 4fc5153e0..2d67a723a 100644 --- a/cli/builder/builder.c +++ b/cli/builder/builder.c @@ -145,12 +145,28 @@ extern void alt_bn128_g1_sum(uint64_t value_len, uint64_t value_ptr, uint64_t re extern uint64_t alt_bn128_pairing_check(uint64_t value_len, uint64_t value_ptr); #endif +static uint8_t* JS_Uint8Array_to_C(JSContext *ctx, JSValue array, size_t *len) { + uint8_t *ptr; + JSValue buffer; + size_t pbyte_offset, psize, pbytes_per_element = 0; + + buffer = JS_GetTypedArrayBuffer(ctx, array, &pbyte_offset, len, &pbytes_per_element); + if (JS_IsException(buffer) || pbytes_per_element != 1) { + return NULL; + } + ptr = JS_GetArrayBuffer(ctx, &psize, buffer); + if (ptr == NULL) { + return NULL; + } + return ptr + pbyte_offset; +} + static JSValue near_read_register(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t register_id; - char *data; + uint8_t *data; uint64_t data_len; - JSValue ret; + JSValue arraybuffer, ret; if (JS_ToUint64Ext(ctx, ®ister_id, argv[0]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); @@ -159,9 +175,8 @@ static JSValue near_read_register(JSContext *ctx, JSValueConst this_val, int arg if (data_len != UINT64_MAX) { data = malloc(data_len); read_register(register_id, (uint64_t)data); - ret = JS_NewStringLenRaw(ctx, data, data_len); - free(data); - return ret; + arraybuffer = JS_NewArrayBuffer(ctx, data, (size_t)data_len, NULL, NULL, TRUE); + return JS_CallConstructor(ctx, JS_GetPropertyStr(ctx, JS_GetGlobalObject(ctx), "Uint8Array"), 1, (JSValueConst *)&arraybuffer); } else { return JS_UNDEFINED; } @@ -181,13 +196,16 @@ static JSValue near_register_len(JSContext *ctx, JSValueConst this_val, int argc static JSValue near_write_register(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t register_id; - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; if (JS_ToUint64Ext(ctx, ®ister_id, argv[0]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); } - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[1]); + data_ptr = JS_Uint8Array_to_C(ctx, argv[1], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for data"); + } write_register(register_id, data_len, (uint64_t)data_ptr); return JS_UNDEFINED; @@ -392,10 +410,13 @@ static JSValue near_random_seed(JSContext *ctx, JSValueConst this_val, int argc, static JSValue near_sha256(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t register_id; - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[0]); + data_ptr = JS_Uint8Array_to_C(ctx, argv[0], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for data"); + } if (JS_ToUint64Ext(ctx, ®ister_id, argv[1]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); } @@ -407,10 +428,13 @@ static JSValue near_sha256(JSContext *ctx, JSValueConst this_val, int argc, JSVa static JSValue near_keccak256(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t register_id; - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[0]); + data_ptr = JS_Uint8Array_to_C(ctx, argv[0], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for data"); + } if (JS_ToUint64Ext(ctx, ®ister_id, argv[1]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); } @@ -421,10 +445,13 @@ static JSValue near_keccak256(JSContext *ctx, JSValueConst this_val, int argc, J static JSValue near_keccak512(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t register_id; - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[0]); + data_ptr = JS_Uint8Array_to_C(ctx, argv[0], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for data"); + } if (JS_ToUint64Ext(ctx, ®ister_id, argv[1]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); } @@ -436,10 +463,13 @@ static JSValue near_keccak512(JSContext *ctx, JSValueConst this_val, int argc, J static JSValue near_ripemd160(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t register_id; - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[0]); + data_ptr = JS_Uint8Array_to_C(ctx, argv[0], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for data"); + } if (JS_ToUint64Ext(ctx, ®ister_id, argv[1]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); } @@ -451,11 +481,17 @@ static JSValue near_ripemd160(JSContext *ctx, JSValueConst this_val, int argc, J static JSValue near_ecrecover(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t malleability_flag, v, register_id, result; - const char *hash_ptr, *sig_ptr; + uint8_t *hash_ptr, *sig_ptr; size_t hash_len, sign_len; - hash_ptr = JS_ToCStringLenRaw(ctx, &hash_len, argv[0]); - sig_ptr = JS_ToCStringLenRaw(ctx, &sign_len, argv[1]); + hash_ptr = JS_Uint8Array_to_C(ctx, argv[0], &hash_len); + if (hash_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for hash"); + } + sig_ptr = JS_Uint8Array_to_C(ctx, argv[1], &sign_len); + if (sig_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for sig"); + } if (JS_ToUint64Ext(ctx, &malleability_flag, argv[2]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for malleability_flag"); } @@ -472,11 +508,14 @@ static JSValue near_ecrecover(JSContext *ctx, JSValueConst this_val, int argc, J static JSValue near_value_return(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *value_ptr; + uint8_t *value_ptr; size_t value_len; - value_ptr = JS_ToCStringLenRaw(ctx, &value_len, argv[0]); - value_return(value_len, (uint64_t)value_ptr); + value_ptr = JS_Uint8Array_to_C(ctx, argv[0], &value_len); + if (value_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for value"); + } + value_return(value_len, (uint64_t)(value_ptr)); return JS_UNDEFINED; } @@ -496,10 +535,13 @@ static JSValue near_panic(JSContext *ctx, JSValueConst this_val, int argc, JSVal static JSValue near_panic_utf8(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[0]); + data_ptr = JS_Uint8Array_to_C(ctx, argv[0], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for message"); + } panic_utf8(data_len, (uint64_t)data_ptr); return JS_UNDEFINED; @@ -518,35 +560,46 @@ static JSValue near_log(JSContext *ctx, JSValueConst this_val, int argc, JSValue static JSValue near_log_utf8(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[0]); - + data_ptr = JS_Uint8Array_to_C(ctx, argv[0], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for message"); + } + log_utf8(data_len, (uint64_t)data_ptr); return JS_UNDEFINED; } static JSValue near_log_utf16(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[0]); + data_ptr = JS_Uint8Array_to_C(ctx, argv[0], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for message"); + } + log_utf16(data_len, (uint64_t)data_ptr); return JS_UNDEFINED; } static JSValue near_promise_create(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *account_id_ptr, *method_name_ptr, *arguments_ptr; + const char *account_id_ptr, *method_name_ptr; + uint8_t *arguments_ptr; size_t account_id_len, method_name_len, arguments_len; uint64_t amount_ptr[2]; // amount is u128 uint64_t gas, ret; account_id_ptr = JS_ToCStringLen(ctx, &account_id_len, argv[0]); method_name_ptr = JS_ToCStringLen(ctx, &method_name_len, argv[1]); - arguments_ptr = JS_ToCStringLenRaw(ctx, &arguments_len, argv[2]); + arguments_ptr = JS_Uint8Array_to_C(ctx, argv[2], &arguments_len); + if (arguments_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for arguments"); + } if (quickjs_to_u128(ctx, argv[3], amount_ptr) != 0) { return JS_ThrowTypeError(ctx, "Expect Uint128 for amount"); } @@ -562,7 +615,8 @@ static JSValue near_promise_create(JSContext *ctx, JSValueConst this_val, int ar static JSValue near_promise_then(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t promise_index; - const char *account_id_ptr, *method_name_ptr, *arguments_ptr; + const char *account_id_ptr, *method_name_ptr; + uint8_t *arguments_ptr; size_t account_id_len, method_name_len, arguments_len; uint64_t amount_ptr[2]; // amount is u128 uint64_t gas, ret; @@ -572,7 +626,10 @@ static JSValue near_promise_then(JSContext *ctx, JSValueConst this_val, int argc } account_id_ptr = JS_ToCStringLen(ctx, &account_id_len, argv[1]); method_name_ptr = JS_ToCStringLen(ctx, &method_name_len, argv[2]); - arguments_ptr = JS_ToCStringLenRaw(ctx, &arguments_len, argv[3]); + arguments_ptr = JS_Uint8Array_to_C(ctx, argv[3], &arguments_len); + if (arguments_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for arguments"); + } if (quickjs_to_u128(ctx, argv[4], amount_ptr) != 0) { return JS_ThrowTypeError(ctx, "Expect Uint128 for amount"); } @@ -638,13 +695,16 @@ static JSValue near_promise_batch_action_create_account(JSContext *ctx, JSValueC static JSValue near_promise_batch_action_deploy_contract(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t promise_index; - const char *code_ptr; + uint8_t *code_ptr; size_t code_len; if (JS_ToUint64Ext(ctx, &promise_index, argv[0]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for promise_index"); } - code_ptr = JS_ToCStringLenRaw(ctx, &code_len, argv[1]); + code_ptr = JS_Uint8Array_to_C(ctx, argv[1], &code_len); + if (code_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for code"); + } promise_batch_action_deploy_contract(promise_index, code_len, (uint64_t)code_ptr); return JS_UNDEFINED; } @@ -652,7 +712,8 @@ static JSValue near_promise_batch_action_deploy_contract(JSContext *ctx, JSValue static JSValue near_promise_batch_action_function_call(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t promise_index; - const char *method_name_ptr, *arguments_ptr; + const char *method_name_ptr; + uint8_t *arguments_ptr; size_t method_name_len, arguments_len; uint64_t amount_ptr[2]; // amount is u128 uint64_t gas; @@ -661,7 +722,10 @@ static JSValue near_promise_batch_action_function_call(JSContext *ctx, JSValueCo return JS_ThrowTypeError(ctx, "Expect Uint64 for promise_index"); } method_name_ptr = JS_ToCStringLen(ctx, &method_name_len, argv[1]); - arguments_ptr = JS_ToCStringLenRaw(ctx, &arguments_len, argv[2]); + arguments_ptr = JS_Uint8Array_to_C(ctx, argv[2], &arguments_len); + if (arguments_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for arguments"); + } if (quickjs_to_u128(ctx, argv[3], amount_ptr) != 0) { return JS_ThrowTypeError(ctx, "Expect Uint128 for amount"); } @@ -691,7 +755,7 @@ static JSValue near_promise_batch_action_stake(JSContext *ctx, JSValueConst this { uint64_t promise_index; uint64_t amount_ptr[2]; - const char *public_key_ptr; + uint8_t *public_key_ptr; size_t public_key_len; if (JS_ToUint64Ext(ctx, &promise_index, argv[0]) < 0) { @@ -700,7 +764,10 @@ static JSValue near_promise_batch_action_stake(JSContext *ctx, JSValueConst this if (quickjs_to_u128(ctx, argv[1], amount_ptr) != 0) { return JS_ThrowTypeError(ctx, "Expect Uint128 for amount"); } - public_key_ptr = JS_ToCStringLenRaw(ctx, &public_key_len, argv[2]); + public_key_ptr = JS_Uint8Array_to_C(ctx, argv[2], &public_key_len); + if (public_key_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for public key"); + } promise_batch_action_stake(promise_index, (uint64_t)amount_ptr, public_key_len, (uint64_t)public_key_ptr); return JS_UNDEFINED; @@ -709,14 +776,17 @@ static JSValue near_promise_batch_action_stake(JSContext *ctx, JSValueConst this static JSValue near_promise_batch_action_add_key_with_full_access(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t promise_index; - const char *public_key_ptr; + uint8_t *public_key_ptr; size_t public_key_len; uint64_t nonce; if (JS_ToUint64Ext(ctx, &promise_index, argv[0]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for promise_index"); } - public_key_ptr = JS_ToCStringLenRaw(ctx, &public_key_len, argv[1]); + public_key_ptr = JS_Uint8Array_to_C(ctx, argv[1], &public_key_len); + if (public_key_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for public key"); + } if (JS_ToUint64Ext(ctx, &nonce, argv[2]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for nonce"); } @@ -727,14 +797,18 @@ static JSValue near_promise_batch_action_add_key_with_full_access(JSContext *ctx static JSValue near_promise_batch_action_add_key_with_function_call(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t promise_index; - const char *public_key_ptr, *receiver_id_ptr, *method_names_ptr; + const char *receiver_id_ptr, *method_names_ptr; + uint8_t *public_key_ptr; size_t public_key_len, receiver_id_len, method_names_len; uint64_t nonce, allowance_ptr[2]; if (JS_ToUint64Ext(ctx, &promise_index, argv[0]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for promise_index"); } - public_key_ptr = JS_ToCStringLenRaw(ctx, &public_key_len, argv[1]); + public_key_ptr = JS_Uint8Array_to_C(ctx, argv[1], &public_key_len); + if (public_key_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for public key"); + } if (JS_ToUint64Ext(ctx, &nonce, argv[2]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for nonce"); } @@ -751,13 +825,16 @@ static JSValue near_promise_batch_action_add_key_with_function_call(JSContext *c static JSValue near_promise_batch_action_delete_key(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t promise_index; - const char *public_key_ptr; + uint8_t *public_key_ptr; size_t public_key_len; if (JS_ToUint64Ext(ctx, &promise_index, argv[0]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for promise_index"); } - public_key_ptr = JS_ToCStringLenRaw(ctx, &public_key_len, argv[1]); + public_key_ptr = JS_Uint8Array_to_C(ctx, argv[1], &public_key_len); + if (public_key_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for public key"); + } promise_batch_action_delete_key(promise_index, public_key_len, (uint64_t)public_key_ptr); return JS_UNDEFINED; } @@ -840,12 +917,18 @@ static JSValue near_promise_return(JSContext *ctx, JSValueConst this_val, int ar static JSValue near_storage_write(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *key_ptr, *value_ptr; + uint8_t *key_ptr, *value_ptr; size_t key_len, value_len; uint64_t register_id, ret; - key_ptr = JS_ToCStringLenRaw(ctx, &key_len, argv[0]); - value_ptr = JS_ToCStringLenRaw(ctx, &value_len, argv[1]); + key_ptr = JS_Uint8Array_to_C(ctx, argv[0], &key_len); + if (key_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for key"); + } + value_ptr = JS_Uint8Array_to_C(ctx, argv[1], &value_len); + if (value_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for value"); + } if (JS_ToUint64Ext(ctx, ®ister_id, argv[2]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); } @@ -855,12 +938,15 @@ static JSValue near_storage_write(JSContext *ctx, JSValueConst this_val, int arg static JSValue near_storage_read(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *key_ptr; + uint8_t *key_ptr; size_t key_len; uint64_t register_id; uint64_t ret; - key_ptr = JS_ToCStringLenRaw(ctx, &key_len, argv[0]); + key_ptr = JS_Uint8Array_to_C(ctx, argv[0], &key_len); + if (key_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for key"); + } if (JS_ToUint64Ext(ctx, ®ister_id, argv[1]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); } @@ -870,12 +956,15 @@ static JSValue near_storage_read(JSContext *ctx, JSValueConst this_val, int argc static JSValue near_storage_remove(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *key_ptr; + uint8_t *key_ptr; size_t key_len; uint64_t register_id; uint64_t ret; - key_ptr = JS_ToCStringLenRaw(ctx, &key_len, argv[0]); + key_ptr = JS_Uint8Array_to_C(ctx, argv[0], &key_len); + if (key_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for key"); + } if (JS_ToUint64Ext(ctx, ®ister_id, argv[1]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); } @@ -885,11 +974,14 @@ static JSValue near_storage_remove(JSContext *ctx, JSValueConst this_val, int ar static JSValue near_storage_has_key(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *key_ptr; + uint8_t *key_ptr; size_t key_len; uint64_t ret; - key_ptr = JS_ToCStringLenRaw(ctx, &key_len, argv[0]); + key_ptr = JS_Uint8Array_to_C(ctx, argv[0], &key_len); + if (key_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for key"); + } ret = storage_has_key(key_len, (uint64_t)key_ptr); return JS_NewBigUint64(ctx, ret); } @@ -918,10 +1010,13 @@ static JSValue near_validator_total_stake(JSContext *ctx, JSValueConst this_val, static JSValue near_alt_bn128_g1_multiexp(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t register_id; - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[0]); + data_ptr = JS_Uint8Array_to_C(ctx, argv[0], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for data"); + } if (JS_ToUint64Ext(ctx, ®ister_id, argv[1]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); } @@ -933,10 +1028,13 @@ static JSValue near_alt_bn128_g1_multiexp(JSContext *ctx, JSValueConst this_val, static JSValue near_alt_bn128_g1_sum(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t register_id; - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[0]); + data_ptr = JS_Uint8Array_to_C(ctx, argv[0], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for data"); + } if (JS_ToUint64Ext(ctx, ®ister_id, argv[1]) < 0) { return JS_ThrowTypeError(ctx, "Expect Uint64 for register_id"); } @@ -947,12 +1045,14 @@ static JSValue near_alt_bn128_g1_sum(JSContext *ctx, JSValueConst this_val, int static JSValue near_alt_bn128_pairing_check(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *data_ptr; + uint8_t *data_ptr; size_t data_len; uint64_t ret; - data_ptr = JS_ToCStringLenRaw(ctx, &data_len, argv[0]); - + data_ptr = JS_Uint8Array_to_C(ctx, argv[0], &data_len); + if (data_ptr == NULL) { + return JS_ThrowTypeError(ctx, "Expect Uint8Array for data"); + } ret = alt_bn128_pairing_check(data_len, (uint64_t)data_ptr); return JS_NewBigUint64(ctx, ret); } @@ -991,7 +1091,7 @@ static void js_add_near_host_functions(JSContext* ctx) { JS_SetPropertyStr(ctx, env, "panic", JS_NewCFunction(ctx, near_panic, "panic", 1)); JS_SetPropertyStr(ctx, env, "panic_utf8", JS_NewCFunction(ctx, near_panic_utf8, "panic_utf8", 1)); JS_SetPropertyStr(ctx, env, "log", JS_NewCFunction(ctx, near_log, "log", 1)); - JS_SetPropertyStr(ctx, env, "log_utf8", JS_NewCFunction(ctx, near_log_utf8, "log_utf8", 1)); + JS_SetPropertyStr(ctx, env, "log_utf8", JS_NewCFunction(ctx, near_log_utf8, "log_ut8", 1)); JS_SetPropertyStr(ctx, env, "log_utf16", JS_NewCFunction(ctx, near_log_utf16, "log_utf16", 1)); JS_SetPropertyStr(ctx, env, "promise_create", JS_NewCFunction(ctx, near_promise_create, "promise_create", 5)); JS_SetPropertyStr(ctx, env, "promise_then", JS_NewCFunction(ctx, near_promise_then, "promise_then", 6)); diff --git a/lib/api.d.ts b/lib/api.d.ts index 951d64150..233ab9f1b 100644 --- a/lib/api.d.ts +++ b/lib/api.d.ts @@ -2,56 +2,56 @@ import { Bytes } from "./utils"; import { PromiseResult } from "./types"; export declare function log(...params: any[]): void; export declare function signerAccountId(): string; -export declare function signerAccountPk(): Bytes; +export declare function signerAccountPk(): Uint8Array; export declare function predecessorAccountId(): string; -export declare function blockIndex(): BigInt; -export declare function blockHeight(): BigInt; -export declare function blockTimestamp(): BigInt; -export declare function epochHeight(): BigInt; -export declare function attachedDeposit(): BigInt; -export declare function prepaidGas(): BigInt; -export declare function usedGas(): BigInt; -export declare function randomSeed(): Bytes; -export declare function sha256(value: Bytes): Bytes; -export declare function keccak256(value: Bytes): Bytes; -export declare function keccak512(value: Bytes): Bytes; -export declare function ripemd160(value: Bytes): Bytes; -export declare function ecrecover(hash: Bytes, sig: Bytes, v: number, malleabilityFlag: number): Bytes | null; -export declare function panicUtf8(msg: string): never; -export declare function logUtf8(msg: string): void; -export declare function logUtf16(msg: string): void; -export declare function storageRead(key: Bytes): Bytes | null; +export declare function blockIndex(): bigint; +export declare function blockHeight(): bigint; +export declare function blockTimestamp(): bigint; +export declare function epochHeight(): bigint; +export declare function attachedDeposit(): bigint; +export declare function prepaidGas(): bigint; +export declare function usedGas(): bigint; +export declare function randomSeed(): Uint8Array; +export declare function sha256(value: Bytes): Uint8Array; +export declare function keccak256(value: Bytes): Uint8Array; +export declare function keccak512(value: Bytes): Uint8Array; +export declare function ripemd160(value: Bytes): Uint8Array; +export declare function ecrecover(hash: Bytes, sig: Bytes, v: number, malleabilityFlag: number): Uint8Array | null; +export declare function panicUtf8(msg: Bytes): never; +export declare function logUtf8(msg: Bytes): void; +export declare function logUtf16(msg: Bytes): void; +export declare function storageRead(key: Bytes): Uint8Array | null; export declare function storageHasKey(key: Bytes): boolean; -export declare function validatorStake(accountId: string): any; -export declare function validatorTotalStake(): BigInt; -export declare function altBn128G1Multiexp(value: Bytes): Bytes; -export declare function altBn128G1Sum(value: Bytes): Bytes; +export declare function validatorStake(accountId: string): bigint; +export declare function validatorTotalStake(): bigint; +export declare function altBn128G1Multiexp(value: Bytes): Uint8Array; +export declare function altBn128G1Sum(value: Bytes): Uint8Array; export declare function altBn128PairingCheck(value: Bytes): boolean; -export declare function storageGetEvicted(): Bytes; +export declare function storageGetEvicted(): Uint8Array; export declare function currentAccountId(): string; -export declare function input(): Bytes; -export declare function storageUsage(): BigInt; -export declare function accountBalance(): BigInt; -export declare function accountLockedBalance(): BigInt; +export declare function input(): Uint8Array; +export declare function storageUsage(): bigint; +export declare function accountBalance(): bigint; +export declare function accountLockedBalance(): bigint; export declare function valueReturn(value: Bytes): void; -export declare function promiseCreate(accountId: string, methodName: string, args: Bytes, amount: number | BigInt, gas: number | BigInt): BigInt; -export declare function promiseThen(promiseIndex: number | BigInt, accountId: string, methodName: string, args: Bytes, amount: number | BigInt, gas: number | BigInt): any; -export declare function promiseAnd(...promiseIndex: number[] | BigInt[]): BigInt; -export declare function promiseBatchCreate(accountId: string): BigInt; -export declare function promiseBatchThen(promiseIndex: number | BigInt, accountId: string): BigInt; -export declare function promiseBatchActionCreateAccount(promiseIndex: number | BigInt): void; -export declare function promiseBatchActionDeployContract(promiseIndex: number | BigInt, code: Bytes): void; -export declare function promiseBatchActionFunctionCall(promiseIndex: number | BigInt, methodName: string, args: Bytes, amount: number | BigInt, gas: number | BigInt): void; -export declare function promiseBatchActionTransfer(promiseIndex: number | BigInt, amount: number | BigInt): void; -export declare function promiseBatchActionStake(promiseIndex: number | BigInt, amount: number | BigInt, publicKey: Bytes): void; -export declare function promiseBatchActionAddKeyWithFullAccess(promiseIndex: number | BigInt, publicKey: Bytes, nonce: number | BigInt): void; -export declare function promiseBatchActionAddKeyWithFunctionCall(promiseIndex: number | BigInt, publicKey: Bytes, nonce: number | BigInt, allowance: number | BigInt, receiverId: string, methodNames: string): void; -export declare function promiseBatchActionDeleteKey(promiseIndex: number | BigInt, publicKey: Bytes): void; -export declare function promiseBatchActionDeleteAccount(promiseIndex: number | BigInt, beneficiaryId: string): void; -export declare function promiseBatchActionFunctionCallWeight(promiseIndex: number | BigInt, methodName: string, args: Bytes, amount: number | BigInt, gas: number | BigInt, weight: number | BigInt): void; -export declare function promiseResultsCount(): BigInt; -export declare function promiseResult(resultIdx: number | BigInt): Bytes | PromiseResult.NotReady | PromiseResult.Failed; -export declare function promiseReturn(promiseIdx: number | BigInt): void; +export declare function promiseCreate(accountId: string, methodName: string, args: Bytes, amount: number | bigint, gas: number | bigint): bigint; +export declare function promiseThen(promiseIndex: number | bigint, accountId: string, methodName: string, args: Bytes, amount: number | bigint, gas: number | bigint): bigint; +export declare function promiseAnd(...promiseIndex: number[] | bigint[]): bigint; +export declare function promiseBatchCreate(accountId: string): bigint; +export declare function promiseBatchThen(promiseIndex: number | bigint, accountId: string): bigint; +export declare function promiseBatchActionCreateAccount(promiseIndex: number | bigint): void; +export declare function promiseBatchActionDeployContract(promiseIndex: number | bigint, code: Bytes): void; +export declare function promiseBatchActionFunctionCall(promiseIndex: number | bigint, methodName: string, args: Bytes, amount: number | bigint, gas: number | bigint): void; +export declare function promiseBatchActionTransfer(promiseIndex: number | bigint, amount: number | bigint): void; +export declare function promiseBatchActionStake(promiseIndex: number | bigint, amount: number | bigint, publicKey: Bytes): void; +export declare function promiseBatchActionAddKeyWithFullAccess(promiseIndex: number | bigint, publicKey: Bytes, nonce: number | bigint): void; +export declare function promiseBatchActionAddKeyWithFunctionCall(promiseIndex: number | bigint, publicKey: Bytes, nonce: number | bigint, allowance: number | bigint, receiverId: string, methodNames: string): void; +export declare function promiseBatchActionDeleteKey(promiseIndex: number | bigint, publicKey: Bytes): void; +export declare function promiseBatchActionDeleteAccount(promiseIndex: number | bigint, beneficiaryId: string): void; +export declare function promiseBatchActionFunctionCallWeight(promiseIndex: number | bigint, methodName: string, args: Bytes, amount: number | bigint, gas: number | bigint, weight: number | bigint): void; +export declare function promiseResultsCount(): bigint; +export declare function promiseResult(resultIdx: number | bigint): Uint8Array | PromiseResult.NotReady | PromiseResult.Failed; +export declare function promiseReturn(promiseIdx: number | bigint): void; export declare function storageWrite(key: Bytes, value: Bytes): boolean; export declare function storageRemove(key: Bytes): boolean; -export declare function storageByteCost(): BigInt; +export declare function storageByteCost(): bigint; diff --git a/lib/api.js b/lib/api.js index a1ff7bb61..15b3261b0 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,3 +1,4 @@ +import { bytesToU8Array, u8ArrayToLatin1 } from "./utils"; import { PromiseResult } from "./types"; const U64_MAX = 2n ** 64n - 1n; const EVICTED_REGISTER = U64_MAX - 1n; @@ -10,7 +11,7 @@ export function log(...params) { } export function signerAccountId() { env.signer_account_id(0); - return env.read_register(0); + return u8ArrayToLatin1(env.read_register(0)); } export function signerAccountPk() { env.signer_account_pk(0); @@ -18,7 +19,7 @@ export function signerAccountPk() { } export function predecessorAccountId() { env.predecessor_account_id(0); - return env.read_register(0); + return u8ArrayToLatin1(env.read_register(0)); } export function blockIndex() { return env.block_index(); @@ -46,22 +47,28 @@ export function randomSeed() { return env.read_register(0); } export function sha256(value) { + value = bytesToU8Array(value); env.sha256(value, 0); return env.read_register(0); } export function keccak256(value) { + value = bytesToU8Array(value); env.keccak256(value, 0); return env.read_register(0); } export function keccak512(value) { + value = bytesToU8Array(value); env.keccak512(value, 0); return env.read_register(0); } export function ripemd160(value) { + value = bytesToU8Array(value); env.ripemd160(value, 0); return env.read_register(0); } export function ecrecover(hash, sig, v, malleabilityFlag) { + hash = bytesToU8Array(hash); + sig = bytesToU8Array(sig); let ret = env.ecrecover(hash, sig, v, malleabilityFlag, 0); if (ret === 0n) { return null; @@ -70,15 +77,19 @@ export function ecrecover(hash, sig, v, malleabilityFlag) { } // NOTE: "env.panic(msg)" is not exported, use "throw Error(msg)" instead export function panicUtf8(msg) { + msg = bytesToU8Array(msg); env.panic_utf8(msg); } export function logUtf8(msg) { + msg = bytesToU8Array(msg); env.log_utf8(msg); } export function logUtf16(msg) { + msg = bytesToU8Array(msg); env.log_utf16(msg); } export function storageRead(key) { + key = bytesToU8Array(key); let ret = env.storage_read(key, 0); if (ret === 1n) { return env.read_register(0); @@ -88,6 +99,7 @@ export function storageRead(key) { } } export function storageHasKey(key) { + key = bytesToU8Array(key); let ret = env.storage_has_key(key); if (ret === 1n) { return true; @@ -103,14 +115,17 @@ export function validatorTotalStake() { return env.validator_total_stake(); } export function altBn128G1Multiexp(value) { + value = bytesToU8Array(value); env.alt_bn128_g1_multiexp(value, 0); return env.read_register(0); } export function altBn128G1Sum(value) { + value = bytesToU8Array(value); env.alt_bn128_g1_sum(value, 0); return env.read_register(0); } export function altBn128PairingCheck(value) { + value = bytesToU8Array(value); let ret = env.alt_bn128_pairing_check(value); if (ret === 1n) { return true; @@ -124,7 +139,7 @@ export function storageGetEvicted() { } export function currentAccountId() { env.current_account_id(0); - return env.read_register(0); + return u8ArrayToLatin1(env.read_register(0)); } export function input() { env.input(0); @@ -140,12 +155,15 @@ export function accountLockedBalance() { return env.account_locked_balance(); } export function valueReturn(value) { + value = bytesToU8Array(value); env.value_return(value); } export function promiseCreate(accountId, methodName, args, amount, gas) { + args = bytesToU8Array(args); return env.promise_create(accountId, methodName, args, amount, gas); } export function promiseThen(promiseIndex, accountId, methodName, args, amount, gas) { + args = bytesToU8Array(args); return env.promise_then(promiseIndex, accountId, methodName, args, amount, gas); } export function promiseAnd(...promiseIndex) { @@ -161,37 +179,44 @@ export function promiseBatchActionCreateAccount(promiseIndex) { env.promise_batch_action_create_account(promiseIndex); } export function promiseBatchActionDeployContract(promiseIndex, code) { + code = bytesToU8Array(code); env.promise_batch_action_deploy_contract(promiseIndex, code); } export function promiseBatchActionFunctionCall(promiseIndex, methodName, args, amount, gas) { + args = bytesToU8Array(args); env.promise_batch_action_function_call(promiseIndex, methodName, args, amount, gas); } export function promiseBatchActionTransfer(promiseIndex, amount) { env.promise_batch_action_transfer(promiseIndex, amount); } export function promiseBatchActionStake(promiseIndex, amount, publicKey) { + publicKey = bytesToU8Array(publicKey); env.promise_batch_action_stake(promiseIndex, amount, publicKey); } export function promiseBatchActionAddKeyWithFullAccess(promiseIndex, publicKey, nonce) { + publicKey = bytesToU8Array(publicKey); env.promise_batch_action_add_key_with_full_access(promiseIndex, publicKey, nonce); } export function promiseBatchActionAddKeyWithFunctionCall(promiseIndex, publicKey, nonce, allowance, receiverId, methodNames) { + publicKey = bytesToU8Array(publicKey); env.promise_batch_action_add_key_with_function_call(promiseIndex, publicKey, nonce, allowance, receiverId, methodNames); } export function promiseBatchActionDeleteKey(promiseIndex, publicKey) { + publicKey = bytesToU8Array(publicKey); env.promise_batch_action_delete_key(promiseIndex, publicKey); } export function promiseBatchActionDeleteAccount(promiseIndex, beneficiaryId) { env.promise_batch_action_delete_account(promiseIndex, beneficiaryId); } export function promiseBatchActionFunctionCallWeight(promiseIndex, methodName, args, amount, gas, weight) { + args = bytesToU8Array(args); env.promise_batch_action_function_call_weight(promiseIndex, methodName, args, amount, gas, weight); } export function promiseResultsCount() { return env.promise_results_count(); } export function promiseResult(resultIdx) { - let status = env.promise_result(resultIdx, 0); + let status = Number(env.promise_result(resultIdx, 0)); if (status == PromiseResult.Successful) { return env.read_register(0); } @@ -207,6 +232,8 @@ export function promiseReturn(promiseIdx) { env.promise_return(promiseIdx); } export function storageWrite(key, value) { + key = bytesToU8Array(key); + value = bytesToU8Array(value); let exist = env.storage_write(key, value, EVICTED_REGISTER); if (exist === 1n) { return true; @@ -214,6 +241,7 @@ export function storageWrite(key, value) { return false; } export function storageRemove(key) { + key = bytesToU8Array(key); let exist = env.storage_remove(key, EVICTED_REGISTER); if (exist === 1n) { return true; diff --git a/lib/collections/lookup-map.d.ts b/lib/collections/lookup-map.d.ts index ce306dc28..6a54566e7 100644 --- a/lib/collections/lookup-map.d.ts +++ b/lib/collections/lookup-map.d.ts @@ -1,12 +1,11 @@ -import { Bytes } from '../utils'; export declare class LookupMap { - readonly keyPrefix: Bytes; - constructor(keyPrefix: Bytes); - containsKey(key: Bytes): boolean; - get(key: Bytes): unknown | null; - remove(key: Bytes): unknown | null; - set(key: Bytes, value: unknown): unknown | null; - extend(objects: [Bytes, unknown][]): void; + readonly keyPrefix: string; + constructor(keyPrefix: string); + containsKey(key: string): boolean; + get(key: string): unknown | null; + remove(key: string): unknown | null; + set(key: string, value: unknown): unknown | null; + extend(objects: [string, unknown][]): void; serialize(): string; static deserialize(data: LookupMap): LookupMap; } diff --git a/lib/collections/lookup-map.js b/lib/collections/lookup-map.js index ed0b5f47c..d14326fd1 100644 --- a/lib/collections/lookup-map.js +++ b/lib/collections/lookup-map.js @@ -1,4 +1,5 @@ import * as near from '../api'; +import { u8ArrayToLatin1 } from '../utils'; export class LookupMap { constructor(keyPrefix) { this.keyPrefix = keyPrefix; @@ -11,14 +12,14 @@ export class LookupMap { let storageKey = this.keyPrefix + JSON.stringify(key); let raw = near.storageRead(storageKey); if (raw !== null) { - return JSON.parse(raw); + return JSON.parse(u8ArrayToLatin1(raw)); } return null; } remove(key) { let storageKey = this.keyPrefix + JSON.stringify(key); if (near.storageRemove(storageKey)) { - return JSON.parse(near.storageGetEvicted()); + return JSON.parse(u8ArrayToLatin1(near.storageGetEvicted())); } return null; } @@ -26,7 +27,7 @@ export class LookupMap { let storageKey = this.keyPrefix + JSON.stringify(key); let storageValue = JSON.stringify(value); if (near.storageWrite(storageKey, storageValue)) { - return JSON.parse(near.storageGetEvicted()); + return JSON.parse(u8ArrayToLatin1(near.storageGetEvicted())); } return null; } diff --git a/lib/index.d.ts b/lib/index.d.ts index 3e3000f0c..6720a4635 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -2,5 +2,5 @@ import { call, view, NearBindgen } from "./near-bindgen"; import { NearContract } from "./near-contract"; import * as near from "./api"; import { LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet } from "./collections"; -import { bytes, Bytes, assert } from "./utils"; -export { call, view, NearBindgen, NearContract, near, LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet, bytes, Bytes, assert, }; +import { assert, Bytes } from "./utils"; +export { call, view, NearBindgen, NearContract, near, LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet, assert, Bytes }; diff --git a/lib/index.js b/lib/index.js index bc19a391a..506a3c53e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,5 +2,5 @@ import { call, view, NearBindgen } from "./near-bindgen"; import { NearContract } from "./near-contract"; import * as near from "./api"; import { LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet, } from "./collections"; -import { bytes, assert } from "./utils"; -export { call, view, NearBindgen, NearContract, near, LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet, bytes, assert, }; +import { assert } from "./utils"; +export { call, view, NearBindgen, NearContract, near, LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet, assert }; diff --git a/lib/utils.d.ts b/lib/utils.d.ts index 1c5e42a0a..d37c3a0fb 100644 --- a/lib/utils.d.ts +++ b/lib/utils.d.ts @@ -1,7 +1,8 @@ -export declare type Bytes = string; -export declare function u8ArrayToBytes(array: Uint8Array): string; +export declare type Bytes = Uint8Array | string; export declare function bytesToU8Array(bytes: Bytes): Uint8Array; -export declare function bytes(strOrU8Array: string | Uint8Array): Bytes; +export declare function u8ArrayToLatin1(array: Uint8Array): string; +export declare function latin1ToU8Array(latin1: string): Uint8Array; +export declare function u8ArrayConcat(array1: Uint8Array, array2: Uint8Array): Uint8Array; export declare function assert(b: boolean, str: string): void; export declare type Mutable = { -readonly [P in keyof T]: T[P]; diff --git a/lib/utils.js b/lib/utils.js index bc107d717..4c24a4823 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,35 +1,32 @@ -export function u8ArrayToBytes(array) { +export function bytesToU8Array(bytes) { + if (typeof bytes === 'string') { + return latin1ToU8Array(bytes); + } + return bytes; +} +export function u8ArrayToLatin1(array) { let ret = ""; for (let e of array) { ret += String.fromCharCode(e); } return ret; } -// TODO this function is a bit broken and the type can't be string -// TODO for more info: https://github.com/near/near-sdk-js/issues/78 -export function bytesToU8Array(bytes) { - let ret = new Uint8Array(bytes.length); - for (let i = 0; i < bytes.length; i++) { - ret[i] = bytes.charCodeAt(i); +export function latin1ToU8Array(latin1) { + let ret = new Uint8Array(latin1.length); + for (let i = 0; i < latin1.length; i++) { + let code = latin1.charCodeAt(i); + if (code > 255) { + throw new Error(`string at index ${i}: ${latin1[i]} is not a valid latin1 char`); + } + ret[i] = code; } return ret; } -export function bytes(strOrU8Array) { - if (typeof strOrU8Array == "string") { - return checkStringIsBytes(strOrU8Array); - } - else if (strOrU8Array instanceof Uint8Array) { - return u8ArrayToBytes(strOrU8Array); - } - throw new Error("bytes: expected string or Uint8Array"); -} -function checkStringIsBytes(str) { - for (let i = 0; i < str.length; i++) { - if (str.charCodeAt(i) > 255) { - throw new Error(`string ${str} at index ${i}: ${str[i]} is not a valid byte`); - } - } - return str; +export function u8ArrayConcat(array1, array2) { + let mergedArray = new Uint8Array(array1.length + array2.length); + mergedArray.set(array1); + mergedArray.set(array2, array1.length); + return mergedArray; } export function assert(b, str) { if (b) { diff --git a/src/api.ts b/src/api.ts index ce020b3e9..e5e2315d6 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,4 +1,4 @@ -import { Bytes } from "./utils"; +import { Bytes, bytesToU8Array, u8ArrayToLatin1 } from "./utils"; import { PromiseResult } from "./types"; const U64_MAX = 2n ** 64n - 1n; @@ -6,8 +6,59 @@ const EVICTED_REGISTER = U64_MAX - 1n; // Interface available in QuickJS interface Env { - panic_utf8: (msg: string) => never; - [x: string]: any; + log: (msg: string) => void; + signer_account_id: (register_id: number | bigint) => void; + signer_account_pk: (register_id: number | bigint) => void; + read_register: (register_id: number | bigint) => Uint8Array; + predecessor_account_id: (register_id: number | bigint) => void; + block_index: () => bigint; + block_timestamp: () => bigint; + epoch_height: () => bigint; + attached_deposit: () => bigint; + prepaid_gas: () => bigint; + used_gas: () => bigint; + random_seed: (register_id: number | bigint) => void; + sha256: (value: Uint8Array, register_id: number | bigint) => void; + keccak256: (value: Uint8Array, register_id: number | bigint) => void; + keccak512: (value: Uint8Array, register_id: number | bigint) => void; + ripemd160: (value: Uint8Array, read_register: number | bigint) => void; + ecrecover: (hash: Uint8Array, sig: Uint8Array, v: number, malleabilityFlag: number, register_id: number | bigint) => bigint; + panic_utf8: (msg: Uint8Array) => never; + log_utf8: (msg: Uint8Array) => void; + log_utf16: (msg: Uint8Array) => void; + storage_read: (key: Uint8Array, register_id: number | bigint) => bigint; + storage_has_key: (key: Uint8Array) => bigint; + validator_stake: (account_id: string) => bigint; + validator_total_stake: () => bigint; + alt_bn128_g1_multiexp: (value: Uint8Array, register_id: number | bigint) => void; + alt_bn128_g1_sum: (value: Uint8Array, register_id: number | bigint) => void; + alt_bn128_pairing_check: (value: Uint8Array) => bigint; + current_account_id: (register_id: number | bigint) => void; + input: (register_id: number | bigint) => void; + storage_usage: () => bigint; + account_balance: () => bigint; + account_locked_balance: () => bigint; + value_return: (value: Uint8Array) => void; + promise_create: (account_id: string, method_name: string, args: Uint8Array, amount: number | bigint, gas: number | bigint) => bigint; + promise_then: (promise_index: number | bigint, account_id: string, method_name: string, args: Uint8Array, amount: number | bigint, gas: number | bigint) => bigint; + promise_and: (...promise_index: number[] | bigint[]) => bigint; + promise_batch_create: (account_id: string) => bigint; + promise_batch_then: (promise_index: number | bigint, account_id: string) => bigint; + promise_batch_action_create_account: (promise_index: number | bigint) => void; + promise_batch_action_deploy_contract: (promise_index: number | bigint, code: Uint8Array) => void; + promise_batch_action_function_call: (promise_index: number | bigint, method_name: string, args: Uint8Array, amount: number | bigint, gas: number | bigint) => void; + promise_batch_action_transfer: (promise_index: number | bigint, amount: number | bigint) => void; + promise_batch_action_stake: (promise_index: number | bigint, amount: number | bigint, public_key: Uint8Array) => void; + promise_batch_action_add_key_with_full_access: (promise_index: number | bigint, public_key: Uint8Array, nonce: number | bigint) => void; + promise_batch_action_add_key_with_function_call: (promise_index: number | bigint, public_key: Uint8Array, nonce: number | bigint, allowance: number | bigint, receiver_id: string, method_names: string) => void; + promise_batch_action_delete_key: (promise_index: number | bigint, public_key: Uint8Array) => void; + promise_batch_action_delete_account: (promise_index: number | bigint, beneficiary_id: string) => void; + promise_batch_action_function_call_weight: (promise_index: number | bigint, method_names: string, args: Uint8Array, amount: number | bigint, gas: number | bigint, weight: number | bigint) => void; + promise_results_count: () => bigint; + promise_result: (promise_index: number | bigint, register_id: number | bigint) => bigint; + promise_return: (promise_index: number | bigint) => void; + storage_write: (key: Uint8Array, value: Uint8Array, register_id: number | bigint) => bigint; + storage_remove: (key: Uint8Array, register_id: number | bigint) => bigint; } declare let env: Env; @@ -22,68 +73,72 @@ export function log(...params: any[]) { export function signerAccountId(): string { env.signer_account_id(0); - return env.read_register(0); + return u8ArrayToLatin1(env.read_register(0)); } -export function signerAccountPk(): Bytes { +export function signerAccountPk(): Uint8Array { env.signer_account_pk(0); return env.read_register(0); } export function predecessorAccountId(): string { env.predecessor_account_id(0); - return env.read_register(0); + return u8ArrayToLatin1(env.read_register(0)); } -export function blockIndex(): BigInt { +export function blockIndex(): bigint { return env.block_index(); } -export function blockHeight(): BigInt { +export function blockHeight(): bigint { return blockIndex(); } -export function blockTimestamp(): BigInt { +export function blockTimestamp(): bigint { return env.block_timestamp(); } -export function epochHeight(): BigInt { +export function epochHeight(): bigint { return env.epoch_height(); } -export function attachedDeposit(): BigInt { +export function attachedDeposit(): bigint { return env.attached_deposit(); } -export function prepaidGas(): BigInt { +export function prepaidGas(): bigint { return env.prepaid_gas(); } -export function usedGas(): BigInt { +export function usedGas(): bigint { return env.used_gas(); } -export function randomSeed(): Bytes { +export function randomSeed(): Uint8Array { env.random_seed(0); return env.read_register(0); } -export function sha256(value: Bytes): Bytes { +export function sha256(value: Bytes): Uint8Array { + value = bytesToU8Array(value) env.sha256(value, 0); return env.read_register(0); } -export function keccak256(value: Bytes): Bytes { +export function keccak256(value: Bytes): Uint8Array { + value = bytesToU8Array(value) env.keccak256(value, 0); return env.read_register(0); } -export function keccak512(value: Bytes): Bytes { +export function keccak512(value: Bytes): Uint8Array { + value = bytesToU8Array(value) env.keccak512(value, 0); return env.read_register(0); } -export function ripemd160(value: Bytes): Bytes { +export function ripemd160(value: Bytes): Uint8Array { + value = bytesToU8Array(value) env.ripemd160(value, 0); return env.read_register(0); } @@ -93,7 +148,9 @@ export function ecrecover( sig: Bytes, v: number, malleabilityFlag: number -): Bytes | null { +): Uint8Array | null { + hash = bytesToU8Array(hash) + sig = bytesToU8Array(sig) let ret = env.ecrecover(hash, sig, v, malleabilityFlag, 0); if (ret === 0n) { return null; @@ -103,19 +160,23 @@ export function ecrecover( // NOTE: "env.panic(msg)" is not exported, use "throw Error(msg)" instead -export function panicUtf8(msg: string): never { +export function panicUtf8(msg: Bytes): never { + msg = bytesToU8Array(msg) env.panic_utf8(msg); } -export function logUtf8(msg: string) { +export function logUtf8(msg: Bytes) { + msg = bytesToU8Array(msg) env.log_utf8(msg); } -export function logUtf16(msg: string) { +export function logUtf16(msg: Bytes) { + msg = bytesToU8Array(msg) env.log_utf16(msg); } -export function storageRead(key: Bytes): Bytes | null { +export function storageRead(key: Bytes): Uint8Array | null { + key = bytesToU8Array(key) let ret = env.storage_read(key, 0); if (ret === 1n) { return env.read_register(0); @@ -125,6 +186,7 @@ export function storageRead(key: Bytes): Bytes | null { } export function storageHasKey(key: Bytes): boolean { + key = bytesToU8Array(key) let ret = env.storage_has_key(key); if (ret === 1n) { return true; @@ -137,21 +199,24 @@ export function validatorStake(accountId: string) { return env.validator_stake(accountId); } -export function validatorTotalStake(): BigInt { +export function validatorTotalStake(): bigint { return env.validator_total_stake(); } -export function altBn128G1Multiexp(value: Bytes): Bytes { +export function altBn128G1Multiexp(value: Bytes): Uint8Array { + value = bytesToU8Array(value) env.alt_bn128_g1_multiexp(value, 0); return env.read_register(0); } -export function altBn128G1Sum(value: Bytes): Bytes { +export function altBn128G1Sum(value: Bytes): Uint8Array { + value = bytesToU8Array(value) env.alt_bn128_g1_sum(value, 0); return env.read_register(0); } export function altBn128PairingCheck(value: Bytes): boolean { + value = bytesToU8Array(value) let ret = env.alt_bn128_pairing_check(value); if (ret === 1n) { return true; @@ -160,33 +225,34 @@ export function altBn128PairingCheck(value: Bytes): boolean { } } -export function storageGetEvicted(): Bytes { +export function storageGetEvicted(): Uint8Array { return env.read_register(EVICTED_REGISTER); } export function currentAccountId(): string { env.current_account_id(0); - return env.read_register(0); + return u8ArrayToLatin1(env.read_register(0)); } -export function input(): Bytes { +export function input(): Uint8Array { env.input(0); return env.read_register(0); } -export function storageUsage(): BigInt { +export function storageUsage(): bigint { return env.storage_usage(); } -export function accountBalance(): BigInt { +export function accountBalance(): bigint { return env.account_balance(); } -export function accountLockedBalance(): BigInt { +export function accountLockedBalance(): bigint { return env.account_locked_balance(); } export function valueReturn(value: Bytes) { + value = bytesToU8Array(value) env.value_return(value); } @@ -194,20 +260,22 @@ export function promiseCreate( accountId: string, methodName: string, args: Bytes, - amount: number | BigInt, - gas: number | BigInt -): BigInt { + amount: number | bigint, + gas: number | bigint +): bigint { + args = bytesToU8Array(args) return env.promise_create(accountId, methodName, args, amount, gas); } export function promiseThen( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, accountId: string, methodName: string, args: Bytes, - amount: number | BigInt, - gas: number | BigInt + amount: number | bigint, + gas: number | bigint ) { + args = bytesToU8Array(args) return env.promise_then( promiseIndex, accountId, @@ -218,39 +286,41 @@ export function promiseThen( ); } -export function promiseAnd(...promiseIndex: number[] | BigInt[]): BigInt { +export function promiseAnd(...promiseIndex: number[] | bigint[]): bigint { return env.promise_and(...promiseIndex); } -export function promiseBatchCreate(accountId: string): BigInt { +export function promiseBatchCreate(accountId: string): bigint { return env.promise_batch_create(accountId); } export function promiseBatchThen( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, accountId: string -): BigInt { +): bigint { return env.promise_batch_then(promiseIndex, accountId); } -export function promiseBatchActionCreateAccount(promiseIndex: number | BigInt) { +export function promiseBatchActionCreateAccount(promiseIndex: number | bigint) { env.promise_batch_action_create_account(promiseIndex); } export function promiseBatchActionDeployContract( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, code: Bytes ) { + code = bytesToU8Array(code) env.promise_batch_action_deploy_contract(promiseIndex, code); } export function promiseBatchActionFunctionCall( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, methodName: string, args: Bytes, - amount: number | BigInt, - gas: number | BigInt + amount: number | bigint, + gas: number | bigint ) { + args = bytesToU8Array(args) env.promise_batch_action_function_call( promiseIndex, methodName, @@ -261,25 +331,27 @@ export function promiseBatchActionFunctionCall( } export function promiseBatchActionTransfer( - promiseIndex: number | BigInt, - amount: number | BigInt + promiseIndex: number | bigint, + amount: number | bigint ) { env.promise_batch_action_transfer(promiseIndex, amount); } export function promiseBatchActionStake( - promiseIndex: number | BigInt, - amount: number | BigInt, + promiseIndex: number | bigint, + amount: number | bigint, publicKey: Bytes ) { + publicKey = bytesToU8Array(publicKey) env.promise_batch_action_stake(promiseIndex, amount, publicKey); } export function promiseBatchActionAddKeyWithFullAccess( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, publicKey: Bytes, - nonce: number | BigInt + nonce: number | bigint ) { + publicKey = bytesToU8Array(publicKey) env.promise_batch_action_add_key_with_full_access( promiseIndex, publicKey, @@ -288,13 +360,14 @@ export function promiseBatchActionAddKeyWithFullAccess( } export function promiseBatchActionAddKeyWithFunctionCall( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, publicKey: Bytes, - nonce: number | BigInt, - allowance: number | BigInt, + nonce: number | bigint, + allowance: number | bigint, receiverId: string, methodNames: string ) { + publicKey = bytesToU8Array(publicKey) env.promise_batch_action_add_key_with_function_call( promiseIndex, publicKey, @@ -306,27 +379,29 @@ export function promiseBatchActionAddKeyWithFunctionCall( } export function promiseBatchActionDeleteKey( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, publicKey: Bytes ) { + publicKey = bytesToU8Array(publicKey) env.promise_batch_action_delete_key(promiseIndex, publicKey); } export function promiseBatchActionDeleteAccount( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, beneficiaryId: string ) { env.promise_batch_action_delete_account(promiseIndex, beneficiaryId); } export function promiseBatchActionFunctionCallWeight( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, methodName: string, args: Bytes, - amount: number | BigInt, - gas: number | BigInt, - weight: number | BigInt, + amount: number | bigint, + gas: number | bigint, + weight: number | bigint, ) { + args = bytesToU8Array(args) env.promise_batch_action_function_call_weight( promiseIndex, methodName, @@ -337,14 +412,14 @@ export function promiseBatchActionFunctionCallWeight( ); } -export function promiseResultsCount(): BigInt { +export function promiseResultsCount(): bigint { return env.promise_results_count(); } export function promiseResult( - resultIdx: number | BigInt -): Bytes | PromiseResult.NotReady | PromiseResult.Failed { - let status: PromiseResult = env.promise_result(resultIdx, 0); + resultIdx: number | bigint +): Uint8Array | PromiseResult.NotReady | PromiseResult.Failed { + let status: PromiseResult = Number(env.promise_result(resultIdx, 0)); if (status == PromiseResult.Successful) { return env.read_register(0); } else if ( @@ -357,11 +432,13 @@ export function promiseResult( } } -export function promiseReturn(promiseIdx: number | BigInt) { +export function promiseReturn(promiseIdx: number | bigint) { env.promise_return(promiseIdx); } export function storageWrite(key: Bytes, value: Bytes): boolean { + key = bytesToU8Array(key) + value = bytesToU8Array(value) let exist = env.storage_write(key, value, EVICTED_REGISTER); if (exist === 1n) { return true; @@ -370,6 +447,7 @@ export function storageWrite(key: Bytes, value: Bytes): boolean { } export function storageRemove(key: Bytes): boolean { + key = bytesToU8Array(key) let exist = env.storage_remove(key, EVICTED_REGISTER); if (exist === 1n) { return true; @@ -377,6 +455,6 @@ export function storageRemove(key: Bytes): boolean { return false; } -export function storageByteCost(): BigInt { +export function storageByteCost(): bigint { return 10_000_000_000_000_000_000n; } diff --git a/src/collections/lookup-map.ts b/src/collections/lookup-map.ts index 60c33437a..522d709bf 100644 --- a/src/collections/lookup-map.ts +++ b/src/collections/lookup-map.ts @@ -1,45 +1,45 @@ import * as near from '../api' -import { Bytes } from '../utils'; +import { u8ArrayToLatin1 } from '../utils'; export class LookupMap { - readonly keyPrefix: Bytes; + readonly keyPrefix: string; - constructor(keyPrefix: Bytes) { + constructor(keyPrefix: string) { this.keyPrefix = keyPrefix } - containsKey(key: Bytes): boolean { + containsKey(key: string): boolean { let storageKey = this.keyPrefix + JSON.stringify(key) return near.storageHasKey(storageKey) } - get(key: Bytes): unknown | null { + get(key: string): unknown | null { let storageKey = this.keyPrefix + JSON.stringify(key) let raw = near.storageRead(storageKey) if (raw !== null) { - return JSON.parse(raw) + return JSON.parse(u8ArrayToLatin1(raw)) } return null } - remove(key: Bytes): unknown | null { + remove(key: string): unknown | null { let storageKey = this.keyPrefix + JSON.stringify(key) if (near.storageRemove(storageKey)) { - return JSON.parse(near.storageGetEvicted()) + return JSON.parse(u8ArrayToLatin1(near.storageGetEvicted())) } return null } - set(key: Bytes, value: unknown): unknown | null { + set(key: string, value: unknown): unknown | null { let storageKey = this.keyPrefix + JSON.stringify(key) let storageValue = JSON.stringify(value) if (near.storageWrite(storageKey, storageValue)) { - return JSON.parse(near.storageGetEvicted()) + return JSON.parse(u8ArrayToLatin1(near.storageGetEvicted())) } return null } - extend(objects: [Bytes, unknown][]) { + extend(objects: [string, unknown][]) { for (let kv of objects) { this.set(kv[0], kv[1]) } diff --git a/src/index.ts b/src/index.ts index 813ad3920..683ec7ead 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,7 @@ import { UnorderedSet, } from "./collections"; -import { bytes, Bytes, assert } from "./utils"; +import { assert, Bytes } from "./utils"; export { call, @@ -24,7 +24,6 @@ export { LookupSet, UnorderedMap, UnorderedSet, - bytes, - Bytes, assert, + Bytes }; diff --git a/src/promise.ts b/src/promise.ts deleted file mode 100644 index f195811d3..000000000 --- a/src/promise.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Bytes } from ".."; -import { Balance } from "./types"; - -export class CreateAccount {} -export class DeployContract { - constructor(public code: Bytes) {} -} -export class FunctionCall { - constructor(public function_name: string, public args: Bytes, public amount: Balance) {} -} -// TODO add FunctionCallWeight after add that in api.ts -export class Transfer { - constructor(public amount: Balance) {} -} -export class Stake { - constructor(public amount: Balance, public public_key: PublicKey) {} -} - -export type PromiseAction = [string] -export type A = [string] -let a: A = ["a"] -let b: PromiseAction = ["a"] diff --git a/src/utils.ts b/src/utils.ts index e23174df6..7eceb0996 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,41 +1,39 @@ -export type Bytes = string; +export type Bytes = Uint8Array | string; -export function u8ArrayToBytes(array: Uint8Array) { - let ret = ""; - for (let e of array) { - ret += String.fromCharCode(e); - } - return ret; -} - -// TODO this function is a bit broken and the type can't be string -// TODO for more info: https://github.com/near/near-sdk-js/issues/78 export function bytesToU8Array(bytes: Bytes): Uint8Array { - let ret = new Uint8Array(bytes.length); - for (let i = 0; i < bytes.length; i++) { - ret[i] = bytes.charCodeAt(i); + if (typeof bytes === 'string') { + return latin1ToU8Array(bytes); } - return ret; + return bytes; } -export function bytes(strOrU8Array: string | Uint8Array): Bytes { - if (typeof strOrU8Array == "string") { - return checkStringIsBytes(strOrU8Array); - } else if (strOrU8Array instanceof Uint8Array) { - return u8ArrayToBytes(strOrU8Array); +export function u8ArrayToLatin1(array: Uint8Array): string { + let ret = ""; + for (let e of array) { + ret += String.fromCharCode(e); } - throw new Error("bytes: expected string or Uint8Array"); + return ret; } -function checkStringIsBytes(str: string) { - for (let i = 0; i < str.length; i++) { - if (str.charCodeAt(i) > 255) { +export function latin1ToU8Array(latin1: string): Uint8Array { + let ret = new Uint8Array(latin1.length); + for (let i = 0; i < latin1.length; i++) { + let code = latin1.charCodeAt(i); + if (code > 255) { throw new Error( - `string ${str} at index ${i}: ${str[i]} is not a valid byte` + `string at index ${i}: ${latin1[i]} is not a valid latin1 char` ); } + ret[i] = code; } - return str; + return ret; +} + +export function u8ArrayConcat(array1: Uint8Array, array2: Uint8Array): Uint8Array { + let mergedArray = new Uint8Array(array1.length + array2.length); + mergedArray.set(array1); + mergedArray.set(array2, array1.length); + return mergedArray } export function assert(b: boolean, str: string) { diff --git a/tests/src/context_api.js b/tests/src/context_api.js index 6e5e037f0..85b669015 100644 --- a/tests/src/context_api.js +++ b/tests/src/context_api.js @@ -1,7 +1,12 @@ import {near} from 'near-sdk-js' export function get_current_account_id() { - near.valueReturn(near.currentAccountId()) + near.valueReturn(new Uint8Array([ + 97, 108, 105, 46, 116, + 101, 115, 116, 46, 110, + 101, 97, 114 + ])); + // near.valueReturn(near.currentAccountId()) } export function get_signer_account_id() { diff --git a/tests/src/log_panic_api.js b/tests/src/log_panic_api.js index c87e5ee47..23e4eee85 100644 --- a/tests/src/log_panic_api.js +++ b/tests/src/log_panic_api.js @@ -1,21 +1,23 @@ import {near, bytes} from 'near-sdk-js' export function log_expected_input_tests() { - // log ascii string - near.log('abc') - // log string with utf-8 chars - near.log('水') - // log number - near.log(333) - // log aribrary byte sequence - near.log(bytes('\x00\x01\xff')) - // log valid utf8 seqence - near.log(bytes('\xe6\xb0\xb4')) + // // log ascii string + // near.log('abc') + // // log string with utf-8 chars + // near.log('水') + // // log number + // near.log(333) + // // log aribrary byte sequence + // near.log(bytes('\x00\x01\xff')) + // // log valid utf8 seqence + // near.log(bytes('\xe6\xb0\xb4')) - // log valid utf8 sequence - near.logUtf8(bytes('\xe6\xb0\xb4')) - // log valid utf16 sequence - near.logUtf16(bytes('\x34\x6c')) + // // log valid utf8 sequence + // near.logUtf8(bytes('\xe6\xb0\xb4')) + // // log valid utf16 sequence + // near.logUtf16(bytes('\x34\x6c')) + + near.logUtf8(new Uint8Array([0x61, 0x62, 0x63])) } export function log_unexpected_input_tests() {