From 44d73b893f4dd214e4d1f85a07bfc8d522cbc006 Mon Sep 17 00:00:00 2001 From: Eyal Chojnowski Date: Mon, 19 Dec 2022 21:55:46 +0100 Subject: [PATCH 1/8] feat: (de)serialize Rust WASM states using msgpack --- package.json | 3 +- .../modules/impl/HandlerExecutorFactory.ts | 2 +- .../modules/impl/handler/WasmHandlerApi.ts | 8 +- .../impl/wasm/rust-wasm-imports-msgpack.ts | 549 ++++++++++++++++++ yarn.lock | 56 ++ 5 files changed, 613 insertions(+), 5 deletions(-) create mode 100644 src/core/modules/impl/wasm/rust-wasm-imports-msgpack.ts diff --git a/package.json b/package.json index 2b1062a1..05265f03 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "mjs:burn:in:hell": "bash mjs-package.sh", "build:types": "tsc -b tsconfig.types.json", "bundle": "node bundle.js", - "build": "yarn run clean && yarn build:cjs && yarn build:mjs && yarn mjs:burn:in:hell && yarn build:types && yarn bundle", + "build": "yarn build:cjs && yarn build:mjs && yarn mjs:burn:in:hell && yarn build:types && yarn bundle", "format": "prettier --write 'src/**/*.ts'", "clean": "rimraf ./lib", "lint": "eslint . --ext .ts", @@ -71,6 +71,7 @@ "fast-copy": "^3.0.0", "level": "^8.0.0", "memory-level": "^1.0.0", + "msgpackr": "^1.8.1", "redstone-isomorphic": "1.1.8", "redstone-wasm-metering": "1.0.3", "safe-stable-stringify": "2.4.1", diff --git a/src/core/modules/impl/HandlerExecutorFactory.ts b/src/core/modules/impl/HandlerExecutorFactory.ts index 09298aa0..0e23bd29 100644 --- a/src/core/modules/impl/HandlerExecutorFactory.ts +++ b/src/core/modules/impl/HandlerExecutorFactory.ts @@ -1,7 +1,7 @@ import Arweave from 'arweave'; import loader from '@assemblyscript/loader'; import { asWasmImports } from './wasm/as-wasm-imports'; -import { rustWasmImports } from './wasm/rust-wasm-imports'; +import { rustWasmImports } from './wasm/rust-wasm-imports-msgpack'; import { Go } from './wasm/go-wasm-imports'; import * as vm2 from 'vm2'; import { WarpCache } from '../../../cache/WarpCache'; diff --git a/src/core/modules/impl/handler/WasmHandlerApi.ts b/src/core/modules/impl/handler/WasmHandlerApi.ts index 7ec7cd2f..c2b29ba8 100644 --- a/src/core/modules/impl/handler/WasmHandlerApi.ts +++ b/src/core/modules/impl/handler/WasmHandlerApi.ts @@ -1,4 +1,6 @@ /* eslint-disable */ +import { unpack, pack } from 'msgpackr'; + import { ContractDefinition } from '../../../../core/ContractDefinition'; import { ExecutionContext } from '../../../../core/ExecutionContext'; import { EvalStateResult } from '../../../../core/modules/StateEvaluator'; @@ -81,7 +83,7 @@ export class WasmHandlerApi extends AbstractContractHandler { break; } case 'rust': { - this.wasmExports.initState(state); + this.wasmExports.initState(pack(state)); break; } case 'go': { @@ -104,7 +106,7 @@ export class WasmHandlerApi extends AbstractContractHandler { return JSON.parse(result); } case 'rust': { - let handleResult = await this.wasmExports.handle(action.input); + let handleResult = await this.wasmExports.handle(pack(action.input)); if (!handleResult) { return; } @@ -145,7 +147,7 @@ export class WasmHandlerApi extends AbstractContractHandler { return JSON.parse(this.wasmExports.__getString(currentStatePtr)); } case 'rust': { - return this.wasmExports.currentState(); + return unpack(this.wasmExports.currentState()); } case 'go': { const result = this.wasmExports.currentState(); diff --git a/src/core/modules/impl/wasm/rust-wasm-imports-msgpack.ts b/src/core/modules/impl/wasm/rust-wasm-imports-msgpack.ts new file mode 100644 index 00000000..07541512 --- /dev/null +++ b/src/core/modules/impl/wasm/rust-wasm-imports-msgpack.ts @@ -0,0 +1,549 @@ +/* tslint:disable */ +/* eslint-disable */ +/* a kind of magic */ + +import { LoggerFactory } from '../../../../logging/LoggerFactory'; + +// note: this is (somewhat heavily) modified code +// of the js that is normally generated by the wasm-bindgen +export const rustWasmImports = (swGlobal, wbindgenImports, wasmInstance, dtorValue): any => { + const wasmLogger = LoggerFactory.INST.create('WASM:Rust'); + + // the raw functions, that we want to make available from the + // wasm module + const rawImports = { + metering: { + usegas: swGlobal.useGas + }, + console: { + log: function (value) { + wasmLogger.error(`${swGlobal.contract.id}: ${value}`); + } + }, + Block: { + height: function () { + return swGlobal.block.height; + }, + indep_hash: function () { + return swGlobal.block.indep_hash; + }, + timestamp: function () { + return swGlobal.block.timestamp; + } + }, + Transaction: { + id: function () { + return swGlobal.transaction.id; + }, + owner: function () { + return swGlobal.transaction.owner; + }, + target: function () { + return swGlobal.transaction.target; + } + }, + Contract: { + id: function () { + return swGlobal.contract.id; + }, + owner: function () { + return swGlobal.contract.owner; + } + }, + SmartWeave: { + caller: function (): string { + return swGlobal.caller; + }, + readContractState: async function (contractTxId): Promise { + return await swGlobal.contracts.readContractState(contractTxId); + }, + write: async function (contractId: string, input: any) { + return await swGlobal.contracts.write(contractId, input); + } + }, + Vrf: { + value: function (): string { + return swGlobal.vrf.value; + }, + + randomInt: function (maxValue: number): number { + return swGlobal.vrf.randomInt(maxValue); + } + } + }; + + const baseImports = { + __wbg_log_: function (arg0, arg1) { + rawImports.console.log(getStringFromWasm0(arg0, arg1)); + }, + __wbindgen_object_drop_ref: function (arg0) { + takeObject(arg0); + }, + __wbg_owner: function (arg0) { + var ret = rawImports.Transaction.owner(); + var ptr0 = passStringToWasm0( + ret, + wasmInstance.exports.__wbindgen_malloc, + wasmInstance.exports.__wbindgen_realloc + ); + var len0 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len0; + getInt32Memory0()[arg0 / 4 + 0] = ptr0; + }, + __wbg_caller: function (arg0) { + var ret = rawImports.SmartWeave.caller(); + var ptr0 = passStringToWasm0( + ret, + wasmInstance.exports.__wbindgen_malloc, + wasmInstance.exports.__wbindgen_realloc + ); + var len0 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len0; + getInt32Memory0()[arg0 / 4 + 0] = ptr0; + }, + __wbg_id: function (arg0) { + var ret = rawImports.Transaction.id(); + var ptr0 = passStringToWasm0( + ret, + wasmInstance.exports.__wbindgen_malloc, + wasmInstance.exports.__wbindgen_realloc + ); + var len0 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len0; + getInt32Memory0()[arg0 / 4 + 0] = ptr0; + }, + __wbindgen_cb_drop: function (arg0) { + const obj = takeObject(arg0).original; + if (obj.cnt-- == 1) { + obj.a = 0; + return true; + } + var ret = false; + return ret; + }, + __wbg_call: function () { + return handleError(function (arg0, arg1, arg2) { + var ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); + return addHeapObject(ret); + }, arguments); + }, + __wbg_new_4: function (arg0, arg1) { + try { + var state0 = { a: arg0, b: arg1 }; + var cb0 = (arg0, arg1) => { + const a = state0.a; + state0.a = 0; + try { + return __wbg_adapter_26(a, state0.b, arg0, arg1); + } finally { + state0.a = a; + } + }; + var ret = new Promise(cb0); + return addHeapObject(ret); + } finally { + state0.a = state0.b = 0; + } + }, + __wbg_new_e: function (arg0) { + var ret = new Uint8Array(getObject(arg0)); + return addHeapObject(ret); + }, + // __wbg_new: function (arg0, arg1) { + // if (arg1 === undefined) { + // try { + // var state0 = { a: arg0, b: arg1 }; + // var cb0 = (arg0, arg1) => { + // const a = state0.a; + // state0.a = 0; + // try { + // return __wbg_adapter_26(a, state0.b, arg0, arg1); + // } finally { + // state0.a = a; + // } + // }; + // var ret = new Promise(cb0) as any; + // return addHeapObject(ret); + // } finally { + // state0.a = state0.b = 0; + // } + // } else { + // var ret = new Uint8Array(getObject(arg0)) as any; + // return addHeapObject(ret); + // } + // }, + __wbg_resolve: function (arg0) { + var ret = Promise.resolve(getObject(arg0)); + return addHeapObject(ret); + }, + __wbg_then: function (arg0, arg1) { + var ret = getObject(arg0).then(getObject(arg1)); + return addHeapObject(ret); + }, + __wbg_buffer: function (arg0) { + var ret = getObject(arg0).buffer; + return addHeapObject(ret); + }, + __wbg_newwithbyteoffsetandlength: function (arg0, arg1, arg2) { + var ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }, + __wbindgen_throw: function (arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }, + __wbindgen_memory: function () { + var ret = wasmInstance.exports.memory; + return addHeapObject(ret); + }, + __wbindgen_closure_wrapper: function (arg0, arg1, arg2) { + var ret = makeMutClosure(arg0, arg1, dtorValue, __wbg_adapter_10); + return addHeapObject(ret); + }, + __wbg_set: function (arg0, arg1, arg2) { + getObject(arg0).set(getObject(arg1), arg2 >>> 0); + }, + __wbg_length: function (arg0) { + var ret = getObject(arg0).length; + return ret; + } + }; + + const baseImportsKeys = Object.keys(baseImports); + + // assigning functions to "real" import names from the currently + // compiled wasm module + let module: any = wbindgenImports.reduce((acc, wbindgenKey) => { + const baseImportsKey = baseImportsKeys.find((key) => wbindgenKey.startsWith(key)); + if (baseImportsKey === undefined) { + throw new Error(`Cannot find import mapping for ${wbindgenKey}`); + } + acc[wbindgenKey] = baseImports[baseImportsKey]; + return acc; + }, {}); + + // the rest of the code is basically left untouched from what + // wasm-bindgen generates + let imports: any = {}; + + imports['__wbindgen_placeholder__'] = module; + + let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + + cachedTextDecoder.decode(); + + let cachegetUint8Memory0 = null; + + function getUint8Memory0() { + if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasmInstance.exports.memory.buffer) { + cachegetUint8Memory0 = new Uint8Array(wasmInstance.exports.memory.buffer); + } + return cachegetUint8Memory0; + } + + function getStringFromWasm0(ptr, len) { + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); + } + + const heap = new Array(32).fill(undefined); + + heap.push(undefined, null, true, false); + + let heap_next = heap.length; + + function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; + } + + function getObject(idx) { + return heap[idx]; + } + + let WASM_VECTOR_LEN = 0; + + // @ts-ignore + let cachedTextEncoder = new TextEncoder('utf-8'); + + const encodeString = + typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); + } + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; + }; + + function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length); + getUint8Memory0() + .subarray(ptr, ptr + buf.length) + .set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len); + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7f) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, (len = offset + arg.length * 3)); + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + } + + WASM_VECTOR_LEN = offset; + return ptr; + } + + let cachegetInt32Memory0 = null; + + function getInt32Memory0() { + if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasmInstance.exports.memory.buffer) { + cachegetInt32Memory0 = new Int32Array(wasmInstance.exports.memory.buffer); + } + return cachegetInt32Memory0; + } + + function dropObject(idx) { + if (idx < 36) return; + heap[idx] = heap_next; + heap_next = idx; + } + + function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; + } + + function debugString(val) { + // primitive types + const type = typeof val; + if (type == 'number' || type == 'boolean' || val == null) { + return `${val}`; + } + if (type == 'string') { + return `"${val}"`; + } + if (type == 'symbol') { + const description = val.description; + if (description == null) { + return 'Symbol'; + } else { + return `Symbol(${description})`; + } + } + if (type == 'function') { + const name = val.name; + if (typeof name == 'string' && name.length > 0) { + return `Function(${name})`; + } else { + return 'Function'; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = '['; + if (length > 0) { + debug += debugString(val[0]); + } + for (let i = 1; i < length; i++) { + debug += ', ' + debugString(val[i]); + } + debug += ']'; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == 'Object') { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return 'Object(' + JSON.stringify(val) + ')'; + } catch (_) { + return 'Object'; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; + } + + function makeMutClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + if (--state.cnt === 0) { + wasmInstance.exports.__wbindgen_export_2.get(state.dtor)(a, state.b); + } else { + state.a = a; + } + } + }; + real.original = state; + + return real; + } + + function __wbg_adapter_10(arg0, arg1, arg2) { + wasmInstance.modifiedExports._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__( + arg0, + arg1, + addHeapObject(arg2) + ); + } + + function passArray8ToWasm0(arg, malloc) { + const ptr = malloc(arg.length * 1); + getUint8Memory0().set(arg, ptr / 1); + WASM_VECTOR_LEN = arg.length; + return ptr; + } + + function getArrayU8FromWasm0(ptr, len) { + return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); + } + + /** + * @param {any} interaction + * @returns {Promise} + */ + module.handle = function (interaction) { + var ret = wasmInstance.exports.handle(addHeapObject(interaction)); + return takeObject(ret); + }; + + /** + * @param {any} state + */ + module.initState = function (state) { + wasmInstance.exports.initState(addHeapObject(state)); + }; + + /** + * @returns {any} + */ + module.currentState = function () { + try { + const retptr = wasmInstance.exports.__wbindgen_add_to_stack_pointer(-16); + wasmInstance.exports.currentState(retptr); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var v0 = getArrayU8FromWasm0(r0, r1).slice(); + wasmInstance.exports.__wbindgen_free(r0, r1 * 1); + return v0; + } finally { + wasmInstance.exports.__wbindgen_add_to_stack_pointer(16); + } + }; + + /** + * @returns {string} + */ + module.lang = function () { + try { + const retptr = wasmInstance.exports.__wbindgen_add_to_stack_pointer(-16); + wasmInstance.exports.lang(retptr); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + return getStringFromWasm0(r0, r1); + } finally { + wasmInstance.exports.__wbindgen_add_to_stack_pointer(16); + wasmInstance.exports.__wbindgen_free(r0, r1); + } + }; + + /** + * @returns {number} + */ + module.type = function () { + var ret = wasmInstance.exports.type(); + return ret; + }; + + function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasmInstance.exports.__wbindgen_exn_store(addHeapObject(e)); + } + } + + function __wbg_adapter_26(arg0, arg1, arg2, arg3) { + wasmInstance.modifiedExports.wasm_bindgen__convert__closures__invoke2_mut__( + arg0, + arg1, + addHeapObject(arg2), + addHeapObject(arg3) + ); + } + + /** + */ + class StateWrapper { + __destroy_into_raw() { + // @ts-ignore + const ptr = this.ptr; + // @ts-ignore + this.ptr = 0; + + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasmInstance.exports.__wbg_statewrapper_free(ptr); + } + } + + module.StateWrapper = StateWrapper; + imports.metering = rawImports.metering; + + return { imports, exports: module }; +}; diff --git a/yarn.lock b/yarn.lock index 44163534..672b5e84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1158,6 +1158,36 @@ semver "^7.3.5" tar "^6.1.11" +"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.2.0.tgz#901c5937e1441572ea23e631fe6deca68482fe76" + integrity sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ== + +"@msgpackr-extract/msgpackr-extract-darwin-x64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.2.0.tgz#fb877fe6bae3c4d3cea29786737840e2ae689066" + integrity sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw== + +"@msgpackr-extract/msgpackr-extract-linux-arm64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.2.0.tgz#986179c38b10ac41fbdaf7d036c825cbc72855d9" + integrity sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA== + +"@msgpackr-extract/msgpackr-extract-linux-arm@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.2.0.tgz#15f2c6fe9e0adc06c21af7e95f484ff4880d79ce" + integrity sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg== + +"@msgpackr-extract/msgpackr-extract-linux-x64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.2.0.tgz#30cae5c9a202f3e1fa1deb3191b18ffcb2f239a2" + integrity sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw== + +"@msgpackr-extract/msgpackr-extract-win32-x64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.2.0.tgz#016d855b6bc459fd908095811f6826e45dd4ba64" + integrity sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA== + "@noble/ed25519@^1.6.1": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.1.tgz#6899660f6fbb97798a6fbd227227c4589a454724" @@ -5341,6 +5371,27 @@ ms@2.1.3, ms@^2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msgpackr-extract@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-2.2.0.tgz#4bb749b58d9764cfdc0d91c7977a007b08e8f262" + integrity sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog== + dependencies: + node-gyp-build-optional-packages "5.0.3" + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.2.0" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.2.0" + "@msgpackr-extract/msgpackr-extract-linux-arm" "2.2.0" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.2.0" + "@msgpackr-extract/msgpackr-extract-linux-x64" "2.2.0" + "@msgpackr-extract/msgpackr-extract-win32-x64" "2.2.0" + +msgpackr@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.1.tgz#2298aed8a14f83e99df77d344cbda3e436f29b5b" + integrity sha512-05fT4J8ZqjYlR4QcRDIhLCYKUOHXk7C/xa62GzMKj74l3up9k2QZ3LgFc6qWdsPHl91QA2WLWqWc8b8t7GLNNw== + optionalDependencies: + msgpackr-extract "^2.2.0" + multistream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/multistream/-/multistream-4.1.0.tgz#7bf00dfd119556fbc153cff3de4c6d477909f5a8" @@ -5425,6 +5476,11 @@ node-fetch@2.6.7, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-gyp-build-optional-packages@5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17" + integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA== + node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: version "4.5.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" From 8355d895b886b6cce9648917b7696a12bc7b98f0 Mon Sep 17 00:00:00 2001 From: Eyal Chojnowski Date: Mon, 19 Dec 2022 23:24:40 +0100 Subject: [PATCH 2/8] feat: wip allow the initial state to be stored as MsgPack binary --- src/contract/deploy/CreateContract.ts | 2 +- src/contract/deploy/impl/DefaultCreateContract.ts | 5 ++++- src/core/modules/impl/ContractDefinitionLoader.ts | 8 +++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/contract/deploy/CreateContract.ts b/src/contract/deploy/CreateContract.ts index 67a51050..839e7496 100644 --- a/src/contract/deploy/CreateContract.ts +++ b/src/contract/deploy/CreateContract.ts @@ -20,7 +20,7 @@ export const emptyTransfer: ArTransfer = { export interface CommonContractData { wallet: ArWallet | SignatureType; - initState: string; + initState: string | Buffer; tags?: Tags; transfer?: ArTransfer; data?: { diff --git a/src/contract/deploy/impl/DefaultCreateContract.ts b/src/contract/deploy/impl/DefaultCreateContract.ts index 3c404f3d..f6614bd2 100644 --- a/src/contract/deploy/impl/DefaultCreateContract.ts +++ b/src/contract/deploy/impl/DefaultCreateContract.ts @@ -85,7 +85,10 @@ export class DefaultCreateContract implements CreateContract { contractTX.addTag(SmartWeaveTags.SDK, 'RedStone'); if (data) { contractTX.addTag(SmartWeaveTags.CONTENT_TYPE, data['Content-Type']); - contractTX.addTag(SmartWeaveTags.INIT_STATE, initState); + contractTX.addTag( + SmartWeaveTags.INIT_STATE, + typeof initState === 'string' ? initState : new TextDecoder().decode(initState) + ); } else { contractTX.addTag(SmartWeaveTags.CONTENT_TYPE, 'application/json'); } diff --git a/src/core/modules/impl/ContractDefinitionLoader.ts b/src/core/modules/impl/ContractDefinitionLoader.ts index 20a8d0bd..31e3491b 100644 --- a/src/core/modules/impl/ContractDefinitionLoader.ts +++ b/src/core/modules/impl/ContractDefinitionLoader.ts @@ -12,6 +12,7 @@ import { TagsParser } from './TagsParser'; import { WasmSrc } from './wasm/WasmSrc'; import { WarpEnvironment } from '../../Warp'; import { SortKeyCache } from 'cache/SortKeyCache'; +import { unpack } from 'msgpackr'; const supportedSrcContentTypes = ['application/javascript', 'application/wasm']; @@ -55,9 +56,10 @@ export class ContractDefinitionLoader implements DefinitionLoader { const minFee = this.tagsParser.getTag(contractTx, SmartWeaveTags.MIN_FEE); this.logger.debug('Tags decoding', benchmark.elapsed()); benchmark.reset(); - const s = await this.evalInitialState(contractTx); - this.logger.debug('init state', s); - const initState = JSON.parse(await this.evalInitialState(contractTx)); + // TODO: It should be stored somewhere whether the initial state is stored as a JSON string or + // as a MsgPack binary. For the sake of this experiment, we assume it's always MsgPack. + // const initState = JSON.parse(await this.evalInitialState(contractTx)); + const initState = unpack(await this.arweaveWrapper.txData(contractTx.id)); this.logger.debug('Parsing src and init state', benchmark.elapsed()); const { src, srcBinary, srcWasmLang, contractType, metadata, srcTx } = await this.loadContractSource( From ddeabf4b925f9db9d88b17e59a2042e0bdb5d4f8 Mon Sep 17 00:00:00 2001 From: Eyal Chojnowski Date: Thu, 22 Dec 2022 20:22:59 +0100 Subject: [PATCH 3/8] feat: allow choosing rust wasm contracts serialization format --- package.json | 2 +- src/contract/deploy/CreateContract.ts | 16 +++--- .../deploy/impl/DefaultCreateContract.ts | 21 +++++--- src/core/SmartWeaveTags.ts | 1 + src/core/Warp.ts | 14 ++++-- src/core/modules/StateEvaluator.ts | 49 +++++++++++++++++++ .../modules/impl/CacheableStateEvaluator.ts | 9 ++-- .../modules/impl/ContractDefinitionLoader.ts | 44 +++++++++++++---- .../modules/impl/DefaultStateEvaluator.ts | 15 ++++-- .../modules/impl/HandlerExecutorFactory.ts | 21 ++++++-- .../impl/handler/AbstractContractHandler.ts | 4 +- .../modules/impl/handler/WasmHandlerApi.ts | 33 ++++++++----- ...m-imports.ts => rust-wasm-imports-json.ts} | 2 +- .../impl/wasm/rust-wasm-imports-msgpack.ts | 2 +- src/plugins/Evolve.ts | 2 +- src/utils/utils.ts | 4 ++ 16 files changed, 180 insertions(+), 59 deletions(-) rename src/core/modules/impl/wasm/{rust-wasm-imports.ts => rust-wasm-imports-json.ts} (99%) diff --git a/package.json b/package.json index 05265f03..d202c9d0 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "mjs:burn:in:hell": "bash mjs-package.sh", "build:types": "tsc -b tsconfig.types.json", "bundle": "node bundle.js", - "build": "yarn build:cjs && yarn build:mjs && yarn mjs:burn:in:hell && yarn build:types && yarn bundle", + "build": "yarn run clean && yarn build:cjs && yarn build:mjs && yarn mjs:burn:in:hell && yarn build:types && yarn bundle", "format": "prettier --write 'src/**/*.ts'", "clean": "rimraf ./lib", "lint": "eslint . --ext .ts", diff --git a/src/contract/deploy/CreateContract.ts b/src/contract/deploy/CreateContract.ts index 839e7496..63404c39 100644 --- a/src/contract/deploy/CreateContract.ts +++ b/src/contract/deploy/CreateContract.ts @@ -1,4 +1,5 @@ import { JWKInterface } from 'arweave/node/lib/wallet'; +import { SerializationFormat } from 'core/modules/StateEvaluator'; import { SignatureType } from '../../contract/Signature'; import { Source } from './Source'; @@ -18,9 +19,10 @@ export const emptyTransfer: ArTransfer = { winstonQty: '0' }; -export interface CommonContractData { +export interface CommonContractData { wallet: ArWallet | SignatureType; - initState: string | Buffer; + stateFormat: T; + initState: T extends SerializationFormat.JSON ? string : Buffer; tags?: Tags; transfer?: ArTransfer; data?: { @@ -29,13 +31,13 @@ export interface CommonContractData { }; } -export interface ContractData extends CommonContractData { +export interface ContractData extends CommonContractData { src: string | Buffer; wasmSrcCodeDir?: string; wasmGlueCode?: string; } -export interface FromSrcTxContractData extends CommonContractData { +export interface FromSrcTxContractData extends CommonContractData { srcTxId: string; } @@ -44,10 +46,10 @@ export interface ContractDeploy { srcTxId?: string; } -export interface CreateContract extends Source { - deploy(contractData: ContractData, disableBundling?: boolean): Promise; +export interface CreateContract extends Source { + deploy(contractData: ContractData, disableBundling?: boolean): Promise; - deployFromSourceTx(contractData: FromSrcTxContractData, disableBundling?: boolean): Promise; + deployFromSourceTx(contractData: FromSrcTxContractData, disableBundling?: boolean): Promise; deployBundled(rawDataItem: Buffer): Promise; } diff --git a/src/contract/deploy/impl/DefaultCreateContract.ts b/src/contract/deploy/impl/DefaultCreateContract.ts index f6614bd2..aa71eeea 100644 --- a/src/contract/deploy/impl/DefaultCreateContract.ts +++ b/src/contract/deploy/impl/DefaultCreateContract.ts @@ -9,8 +9,10 @@ import { LoggerFactory } from '../../../logging/LoggerFactory'; import { CreateContract, ContractData, ContractDeploy, FromSrcTxContractData, ArWallet } from '../CreateContract'; import { SourceData, SourceImpl } from './SourceImpl'; import { Buffer } from 'redstone-isomorphic'; +import { SerializationFormat } from 'core/modules/StateEvaluator'; +import { exhaustive } from 'utils/utils'; -export class DefaultCreateContract implements CreateContract { +export class DefaultCreateContract implements CreateContract { private readonly logger = LoggerFactory.INST.create('DefaultCreateContract'); private readonly source: SourceImpl; @@ -21,8 +23,11 @@ export class DefaultCreateContract implements CreateContract { this.source = new SourceImpl(this.warp); } - async deploy(contractData: ContractData, disableBundling?: boolean): Promise { - const { wallet, initState, tags, transfer, data } = contractData; + async deploy( + contractData: ContractData, + disableBundling?: boolean + ): Promise { + const { wallet, stateFormat, initState, tags, transfer, data } = contractData; const effectiveUseBundler = disableBundling == undefined ? this.warp.definitionLoader.type() == 'warp' : !disableBundling; @@ -38,6 +43,7 @@ export class DefaultCreateContract implements CreateContract { { srcTxId: srcTx.id, wallet, + stateFormat, initState, tags, transfer, @@ -48,13 +54,13 @@ export class DefaultCreateContract implements CreateContract { ); } - async deployFromSourceTx( - contractData: FromSrcTxContractData, + async deployFromSourceTx( + contractData: FromSrcTxContractData, disableBundling?: boolean, srcTx: Transaction = null ): Promise { this.logger.debug('Creating new contract from src tx'); - const { wallet, srcTxId, initState, tags, transfer, data } = contractData; + const { wallet, srcTxId, stateFormat, initState, tags, transfer, data } = contractData; this.signature = new Signature(this.warp, wallet); const signer = this.signature.signer; @@ -85,12 +91,13 @@ export class DefaultCreateContract implements CreateContract { contractTX.addTag(SmartWeaveTags.SDK, 'RedStone'); if (data) { contractTX.addTag(SmartWeaveTags.CONTENT_TYPE, data['Content-Type']); + contractTX.addTag(SmartWeaveTags.INIT_STATE_FORMAT, stateFormat); contractTX.addTag( SmartWeaveTags.INIT_STATE, typeof initState === 'string' ? initState : new TextDecoder().decode(initState) ); } else { - contractTX.addTag(SmartWeaveTags.CONTENT_TYPE, 'application/json'); + contractTX.addTag(SmartWeaveTags.CONTENT_TYPE, stateFormat); } if (this.warp.environment === 'testnet') { diff --git a/src/core/SmartWeaveTags.ts b/src/core/SmartWeaveTags.ts index 044cd377..4f293335 100644 --- a/src/core/SmartWeaveTags.ts +++ b/src/core/SmartWeaveTags.ts @@ -11,6 +11,7 @@ export enum SmartWeaveTags { SDK = 'SDK', MIN_FEE = 'Min-Fee', INIT_STATE = 'Init-State', + INIT_STATE_FORMAT = 'Init-State-Format', INIT_STATE_TX = 'Init-State-TX', INTERACT_WRITE = 'Interact-Write', WASM_LANG = 'Wasm-Lang', diff --git a/src/core/Warp.ts b/src/core/Warp.ts index 19b722bf..98489241 100644 --- a/src/core/Warp.ts +++ b/src/core/Warp.ts @@ -16,7 +16,7 @@ import { DefinitionLoader } from './modules/DefinitionLoader'; import { ExecutorFactory } from './modules/ExecutorFactory'; import { HandlerApi } from './modules/impl/HandlerExecutorFactory'; import { InteractionsLoader } from './modules/InteractionsLoader'; -import { EvalStateResult, StateEvaluator } from './modules/StateEvaluator'; +import { EvalStateResult, SerializationFormat, StateEvaluator } from './modules/StateEvaluator'; import { WarpBuilder } from './WarpBuilder'; import { WarpPluginType, WarpPlugin, knownWarpPlugins } from './WarpPlugin'; import { SortKeyCache } from '../cache/SortKeyCache'; @@ -39,7 +39,7 @@ export class Warp { /** * @deprecated createContract will be a private field, please use its methods directly e.g. await warp.deploy(...) */ - readonly createContract: CreateContract; + readonly createContract: CreateContract; readonly testing: Testing; private readonly plugins: Map> = new Map(); @@ -73,11 +73,17 @@ export class Warp { return new HandlerBasedContract(contractTxId, this, callingContract, innerCallData); } - async deploy(contractData: ContractData, disableBundling?: boolean): Promise { + async deploy( + contractData: ContractData, + disableBundling?: boolean + ): Promise { return await this.createContract.deploy(contractData, disableBundling); } - async deployFromSourceTx(contractData: FromSrcTxContractData, disableBundling?: boolean): Promise { + async deployFromSourceTx( + contractData: FromSrcTxContractData, + disableBundling?: boolean + ): Promise { return await this.createContract.deployFromSourceTx(contractData, disableBundling); } diff --git a/src/core/modules/StateEvaluator.ts b/src/core/modules/StateEvaluator.ts index add07fe8..7862f1ec 100644 --- a/src/core/modules/StateEvaluator.ts +++ b/src/core/modules/StateEvaluator.ts @@ -1,3 +1,7 @@ +import { pack, unpack } from 'msgpackr'; +import stringify from 'safe-stable-stringify'; +import { exhaustive } from 'utils/utils'; + import { SortKeyCache, SortKeyCacheResult } from '../../cache/SortKeyCache'; import { CurrentTx } from '../../contract/Contract'; import { ExecutionContext } from '../../core/ExecutionContext'; @@ -138,8 +142,46 @@ export class DefaultEvaluationOptions implements EvaluationOptions { throwOnInternalWriteError = true; cacheEveryNInteractions = -1; + + wasmSerializationFormat = SerializationFormat.JSON; +} + +export enum SerializationFormat { + JSON = 'application/json', + // NOTE: There is still no officially registered Media Type for Messagepack and there are + // apparently multiple different versions used in the wild like: + // - application/msgpack + // - application/x-msgpack + // - application/x.msgpack + // - [...] + // See . I've decided to use the first one + // as it looks like the one that makes the most sense. + MSGPACK = 'application/msgpack' } +export const stringToSerializationFormat = (format: string): SerializationFormat => { + const formatTyped = format as SerializationFormat; + + switch (formatTyped) { + case SerializationFormat.JSON: + return SerializationFormat.JSON; + case SerializationFormat.MSGPACK: + return SerializationFormat.MSGPACK; + default: + return exhaustive(formatTyped, `Unsupported serialization format: "${formatTyped}"`); + } +}; + +export const Serializers = { + [SerializationFormat.JSON]: stringify, + [SerializationFormat.MSGPACK]: pack +} as const satisfies Record; + +export const Deserializers = { + [SerializationFormat.JSON]: JSON.parse, + [SerializationFormat.MSGPACK]: unpack +} as const satisfies Record; + // an interface for the contract EvaluationOptions - can be used to change the behaviour of some features. export interface EvaluationOptions { // whether exceptions from given transaction interaction should be ignored @@ -214,4 +256,11 @@ export interface EvaluationOptions { // force SDK to cache the state after evaluating each N interactions // defaults to -1, which effectively turns off this feature cacheEveryNInteractions: number; + + /** + * What serialization format should be used for the WASM<->JS bridge. Note that changing this + * currently only affects Rust smartcontracts. AssemblyScript and Go smartcontracts will always + * use JSON. Defaults to JSON. + */ + wasmSerializationFormat: SerializationFormat; } diff --git a/src/core/modules/impl/CacheableStateEvaluator.ts b/src/core/modules/impl/CacheableStateEvaluator.ts index 41ee407d..26098d72 100644 --- a/src/core/modules/impl/CacheableStateEvaluator.ts +++ b/src/core/modules/impl/CacheableStateEvaluator.ts @@ -6,7 +6,7 @@ import { ExecutionContextModifier } from '../../../core/ExecutionContextModifier import { GQLNodeInterface } from '../../../legacy/gqlResult'; import { LoggerFactory } from '../../../logging/LoggerFactory'; import { indent } from '../../../utils/utils'; -import { EvalStateResult } from '../StateEvaluator'; +import { EvalStateResult, SerializationFormat } from '../StateEvaluator'; import { DefaultStateEvaluator } from './DefaultStateEvaluator'; import { HandlerApi } from './HandlerExecutorFactory'; import { genesisSortKey } from './LexicographicalInteractionsSorter'; @@ -35,11 +35,12 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator { currentTx: CurrentTx[] ): Promise>> { const cachedState = executionContext.cachedState; + const { wasmSerializationFormat: serializationFormat } = executionContext.evaluationOptions; if (cachedState && cachedState.sortKey == executionContext.requestedSortKey) { this.cLogger.info( `Exact cache hit for sortKey ${executionContext?.contractDefinition?.txId}:${cachedState.sortKey}` ); - executionContext.handler?.initState(cachedState.cachedValue.state); + executionContext.handler?.initState(cachedState.cachedValue.state, serializationFormat); return cachedState; } @@ -71,10 +72,10 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator { if (missingInteractions.length == 0) { this.cLogger.info(`No missing interactions ${contractTxId}`); if (cachedState) { - executionContext.handler?.initState(cachedState.cachedValue.state); + executionContext.handler?.initState(cachedState.cachedValue.state, serializationFormat); return cachedState; } else { - executionContext.handler?.initState(executionContext.contractDefinition.initState); + executionContext.handler?.initState(executionContext.contractDefinition.initState, serializationFormat); this.cLogger.debug('Inserting initial state into cache'); const stateToCache = new EvalStateResult(executionContext.contractDefinition.initState, {}, {}); // no real sort-key - as we're returning the initial state diff --git a/src/core/modules/impl/ContractDefinitionLoader.ts b/src/core/modules/impl/ContractDefinitionLoader.ts index 31e3491b..90649103 100644 --- a/src/core/modules/impl/ContractDefinitionLoader.ts +++ b/src/core/modules/impl/ContractDefinitionLoader.ts @@ -12,7 +12,8 @@ import { TagsParser } from './TagsParser'; import { WasmSrc } from './wasm/WasmSrc'; import { WarpEnvironment } from '../../Warp'; import { SortKeyCache } from 'cache/SortKeyCache'; -import { unpack } from 'msgpackr'; +import { Deserializers, SerializationFormat, stringToSerializationFormat } from '../StateEvaluator'; +import { exhaustive } from 'utils/utils'; const supportedSrcContentTypes = ['application/javascript', 'application/wasm']; @@ -56,10 +57,7 @@ export class ContractDefinitionLoader implements DefinitionLoader { const minFee = this.tagsParser.getTag(contractTx, SmartWeaveTags.MIN_FEE); this.logger.debug('Tags decoding', benchmark.elapsed()); benchmark.reset(); - // TODO: It should be stored somewhere whether the initial state is stored as a JSON string or - // as a MsgPack binary. For the sake of this experiment, we assume it's always MsgPack. - // const initState = JSON.parse(await this.evalInitialState(contractTx)); - const initState = unpack(await this.arweaveWrapper.txData(contractTx.id)); + const initState = await this.evalInitialState(contractTx); this.logger.debug('Parsing src and init state', benchmark.elapsed()); const { src, srcBinary, srcWasmLang, contractType, metadata, srcTx } = await this.loadContractSource( @@ -123,14 +121,42 @@ export class ContractDefinitionLoader implements DefinitionLoader { }; } - private async evalInitialState(contractTx: Transaction): Promise { + private async evalInitialState(contractTx: Transaction): Promise { if (this.tagsParser.getTag(contractTx, SmartWeaveTags.INIT_STATE)) { - return this.tagsParser.getTag(contractTx, SmartWeaveTags.INIT_STATE); + const format = stringToSerializationFormat( + this.tagsParser.getTag(contractTx, SmartWeaveTags.INIT_STATE_FORMAT) ?? 'application/json' + ); + const initState = this.tagsParser.getTag(contractTx, SmartWeaveTags.INIT_STATE); + + switch (format) { + case SerializationFormat.JSON: + return Deserializers[format](initState); + case SerializationFormat.MSGPACK: + return Deserializers[format](new TextEncoder().encode(initState)); + default: + exhaustive(format); + } } else if (this.tagsParser.getTag(contractTx, SmartWeaveTags.INIT_STATE_TX)) { const stateTX = this.tagsParser.getTag(contractTx, SmartWeaveTags.INIT_STATE_TX); - return this.arweaveWrapper.txDataString(stateTX); + + return this.getInitialStateFromTx(await this.arweave.transactions.get(stateTX)); } else { - return this.arweaveWrapper.txDataString(contractTx.id); + return this.getInitialStateFromTx(contractTx); + } + } + + private async getInitialStateFromTx(tx: Transaction): Promise { + const format = stringToSerializationFormat( + this.tagsParser.getTag(tx, SmartWeaveTags.CONTENT_TYPE) ?? 'application/json' + ); + + switch (format) { + case SerializationFormat.JSON: + return Deserializers[format](await this.arweaveWrapper.txDataString(tx.id)); + case SerializationFormat.MSGPACK: + return Deserializers[format](await this.arweaveWrapper.txData(tx.id)); + default: + exhaustive(format); } } diff --git a/src/core/modules/impl/DefaultStateEvaluator.ts b/src/core/modules/impl/DefaultStateEvaluator.ts index 30387434..74f22bdb 100644 --- a/src/core/modules/impl/DefaultStateEvaluator.ts +++ b/src/core/modules/impl/DefaultStateEvaluator.ts @@ -53,8 +53,13 @@ export abstract class DefaultStateEvaluator implements StateEvaluator { executionContext: ExecutionContext>, currentTx: CurrentTx[] ): Promise>> { - const { ignoreExceptions, stackTrace, internalWrites, cacheEveryNInteractions } = - executionContext.evaluationOptions; + const { + ignoreExceptions, + stackTrace, + internalWrites, + cacheEveryNInteractions, + wasmSerializationFormat: serializationFormat + } = executionContext.evaluationOptions; const { contract, contractDefinition, sortedInteractions, warp } = executionContext; let currentState = baseState.state; @@ -62,7 +67,7 @@ export abstract class DefaultStateEvaluator implements StateEvaluator { const validity = baseState.validity; const errorMessages = baseState.errorMessages; - executionContext?.handler.initState(currentState); + executionContext?.handler.initState(currentState, serializationFormat); const depth = executionContext.contract.callDepth(); @@ -76,7 +81,7 @@ export abstract class DefaultStateEvaluator implements StateEvaluator { let lastConfirmedTxState: { tx: GQLNodeInterface; state: EvalStateResult } = null; const missingInteractionsLength = missingInteractions.length; - executionContext.handler.initState(currentState); + executionContext.handler.initState(currentState, serializationFormat); const evmSignatureVerificationPlugin = warp.hasPlugin('evm-signature-verification') ? warp.loadPlugin>('evm-signature-verification') @@ -166,7 +171,7 @@ export abstract class DefaultStateEvaluator implements StateEvaluator { if (newState !== null) { currentState = newState.cachedValue.state; // we need to update the state in the wasm module - executionContext?.handler.initState(currentState); + executionContext?.handler.initState(currentState, serializationFormat); validity[missingInteraction.id] = newState.cachedValue.validity[missingInteraction.id]; if (newState.cachedValue.errorMessages?.[missingInteraction.id]) { diff --git a/src/core/modules/impl/HandlerExecutorFactory.ts b/src/core/modules/impl/HandlerExecutorFactory.ts index 0e23bd29..94ac7536 100644 --- a/src/core/modules/impl/HandlerExecutorFactory.ts +++ b/src/core/modules/impl/HandlerExecutorFactory.ts @@ -1,7 +1,8 @@ import Arweave from 'arweave'; import loader from '@assemblyscript/loader'; import { asWasmImports } from './wasm/as-wasm-imports'; -import { rustWasmImports } from './wasm/rust-wasm-imports-msgpack'; +import { rustWasmImportsJson } from './wasm/rust-wasm-imports-json'; +import { rustWasmImportsMsgpack } from './wasm/rust-wasm-imports-msgpack'; import { Go } from './wasm/go-wasm-imports'; import * as vm2 from 'vm2'; import { WarpCache } from '../../../cache/WarpCache'; @@ -12,14 +13,14 @@ import { SmartWeaveGlobal } from '../../../legacy/smartweave-global'; import { Benchmark } from '../../../logging/Benchmark'; import { LoggerFactory } from '../../../logging/LoggerFactory'; import { ExecutorFactory } from '../ExecutorFactory'; -import { EvalStateResult, EvaluationOptions } from '../StateEvaluator'; +import { EvalStateResult, EvaluationOptions, SerializationFormat } from '../StateEvaluator'; import { JsHandlerApi } from './handler/JsHandlerApi'; import { WasmHandlerApi } from './handler/WasmHandlerApi'; import { normalizeContractSource } from './normalize-source'; import { MemCache } from '../../../cache/impl/MemCache'; import BigNumber from '../../../legacy/bignumber'; import { Warp } from '../../Warp'; -import { isBrowser } from '../../../utils/utils'; +import { exhaustive, isBrowser } from '../../../utils/utils'; import { Buffer } from 'redstone-isomorphic'; class ContractError extends Error { @@ -105,6 +106,18 @@ export class HandlerExecutorFactory implements ExecutorFactory imp.name); + let rustWasmImports; + switch (evaluationOptions.wasmSerializationFormat) { + case SerializationFormat.JSON: + rustWasmImports = rustWasmImportsJson; + break; + case SerializationFormat.MSGPACK: + rustWasmImports = rustWasmImportsMsgpack; + break; + default: + return exhaustive(evaluationOptions.wasmSerializationFormat); + } + const { imports, exports } = rustWasmImports( swGlobal, wbindgenImports, @@ -245,7 +258,7 @@ export interface HandlerApi { interactionData: InteractionData ): Promise>; - initState(state: State): void; + initState(state: State, format: SerializationFormat): void; } export type HandlerFunction = ( diff --git a/src/core/modules/impl/handler/AbstractContractHandler.ts b/src/core/modules/impl/handler/AbstractContractHandler.ts index 0c4a82ff..d25ed9ba 100644 --- a/src/core/modules/impl/handler/AbstractContractHandler.ts +++ b/src/core/modules/impl/handler/AbstractContractHandler.ts @@ -1,7 +1,7 @@ import { ContractError, CurrentTx } from '../../../../contract/Contract'; import { ContractDefinition } from '../../../../core/ContractDefinition'; import { ExecutionContext } from '../../../../core/ExecutionContext'; -import { EvalStateResult } from '../../../../core/modules/StateEvaluator'; +import { EvalStateResult, SerializationFormat } from '../../../../core/modules/StateEvaluator'; import { GQLNodeInterface } from '../../../../legacy/gqlResult'; import { SmartWeaveGlobal } from '../../../../legacy/smartweave-global'; import { LoggerFactory } from '../../../../logging/LoggerFactory'; @@ -27,7 +27,7 @@ export abstract class AbstractContractHandler implements HandlerApi ): Promise>; - abstract initState(state: State): void; + abstract initState(state: State, format: SerializationFormat): void; async dispose(): Promise { // noop by default; diff --git a/src/core/modules/impl/handler/WasmHandlerApi.ts b/src/core/modules/impl/handler/WasmHandlerApi.ts index c2b29ba8..640ce62c 100644 --- a/src/core/modules/impl/handler/WasmHandlerApi.ts +++ b/src/core/modules/impl/handler/WasmHandlerApi.ts @@ -1,13 +1,17 @@ /* eslint-disable */ -import { unpack, pack } from 'msgpackr'; - import { ContractDefinition } from '../../../../core/ContractDefinition'; import { ExecutionContext } from '../../../../core/ExecutionContext'; -import { EvalStateResult } from '../../../../core/modules/StateEvaluator'; +import { + Deserializers, + EvalStateResult, + SerializationFormat, + Serializers +} from '../../../../core/modules/StateEvaluator'; import { SmartWeaveGlobal } from '../../../../legacy/smartweave-global'; import stringify from 'safe-stable-stringify'; -import { InteractionData, InteractionResult } from '../HandlerExecutorFactory'; +import { ContractInteraction, InteractionData, InteractionResult } from '../HandlerExecutorFactory'; import { AbstractContractHandler } from './AbstractContractHandler'; +import { exhaustive } from 'utils/utils'; export class WasmHandlerApi extends AbstractContractHandler { constructor( @@ -26,21 +30,22 @@ export class WasmHandlerApi extends AbstractContractHandler { ): Promise> { try { const { interaction, interactionTx, currentTx } = interactionData; + const { gasLimit, wasmSerializationFormat } = executionContext.evaluationOptions; this.swGlobal._activeTx = interactionTx; this.swGlobal.caller = interaction.caller; // either contract tx id (for internal writes) or transaction.owner - this.swGlobal.gasLimit = executionContext.evaluationOptions.gasLimit; + this.swGlobal.gasLimit = gasLimit; this.swGlobal.gasUsed = 0; this.assignReadContractState(executionContext, currentTx, currentResult, interactionTx); this.assignWrite(executionContext, currentTx); - const handlerResult = await this.doHandle(interaction); + const handlerResult = await this.doHandle(interaction, wasmSerializationFormat); return { type: 'ok', result: handlerResult, - state: this.doGetCurrentState(), // TODO: return only at the end of evaluation and when caching is required + state: this.doGetCurrentState(wasmSerializationFormat), // TODO: return only at the end of evaluation and when caching is required gasUsed: this.swGlobal.gasUsed }; } catch (e) { @@ -75,7 +80,8 @@ export class WasmHandlerApi extends AbstractContractHandler { } } - initState(state: State): void { + // TODO:noom support SerializationFormat here too + initState(state: State, format: SerializationFormat): void { switch (this.contractDefinition.srcWasmLang) { case 'assemblyscript': { const statePtr = this.wasmExports.__newString(stringify(state)); @@ -83,7 +89,7 @@ export class WasmHandlerApi extends AbstractContractHandler { break; } case 'rust': { - this.wasmExports.initState(pack(state)); + this.wasmExports.initState(Serializers[format](state)); break; } case 'go': { @@ -96,7 +102,7 @@ export class WasmHandlerApi extends AbstractContractHandler { } } - private async doHandle(action: any): Promise { + private async doHandle(action: ContractInteraction, format: SerializationFormat): Promise { switch (this.contractDefinition.srcWasmLang) { case 'assemblyscript': { const actionPtr = this.wasmExports.__newString(stringify(action.input)); @@ -106,7 +112,8 @@ export class WasmHandlerApi extends AbstractContractHandler { return JSON.parse(result); } case 'rust': { - let handleResult = await this.wasmExports.handle(pack(action.input)); + const handleResult = await this.wasmExports.handle(Serializers[format](action.input)); + if (!handleResult) { return; } @@ -140,14 +147,14 @@ export class WasmHandlerApi extends AbstractContractHandler { } } - private doGetCurrentState(): State { + private doGetCurrentState(format: SerializationFormat): State { switch (this.contractDefinition.srcWasmLang) { case 'assemblyscript': { const currentStatePtr = this.wasmExports.currentState(); return JSON.parse(this.wasmExports.__getString(currentStatePtr)); } case 'rust': { - return unpack(this.wasmExports.currentState()); + return Deserializers[format](this.wasmExports.currentState()); } case 'go': { const result = this.wasmExports.currentState(); diff --git a/src/core/modules/impl/wasm/rust-wasm-imports.ts b/src/core/modules/impl/wasm/rust-wasm-imports-json.ts similarity index 99% rename from src/core/modules/impl/wasm/rust-wasm-imports.ts rename to src/core/modules/impl/wasm/rust-wasm-imports-json.ts index 86609b04..0adaae70 100644 --- a/src/core/modules/impl/wasm/rust-wasm-imports.ts +++ b/src/core/modules/impl/wasm/rust-wasm-imports-json.ts @@ -6,7 +6,7 @@ import { LoggerFactory } from '../../../../logging/LoggerFactory'; // note: this is (somewhat heavily) modified code // of the js that is normally generated by the wasm-bindgen -export const rustWasmImports = (swGlobal, wbindgenImports, wasmInstance, dtorValue): any => { +export const rustWasmImportsJson = (swGlobal, wbindgenImports, wasmInstance, dtorValue): any => { const wasmLogger = LoggerFactory.INST.create('WASM:Rust'); // the raw functions, that we want to make available from the diff --git a/src/core/modules/impl/wasm/rust-wasm-imports-msgpack.ts b/src/core/modules/impl/wasm/rust-wasm-imports-msgpack.ts index 07541512..00ffb77c 100644 --- a/src/core/modules/impl/wasm/rust-wasm-imports-msgpack.ts +++ b/src/core/modules/impl/wasm/rust-wasm-imports-msgpack.ts @@ -6,7 +6,7 @@ import { LoggerFactory } from '../../../../logging/LoggerFactory'; // note: this is (somewhat heavily) modified code // of the js that is normally generated by the wasm-bindgen -export const rustWasmImports = (swGlobal, wbindgenImports, wasmInstance, dtorValue): any => { +export const rustWasmImportsMsgpack = (swGlobal, wbindgenImports, wasmInstance, dtorValue): any => { const wasmLogger = LoggerFactory.INST.create('WASM:Rust'); // the raw functions, that we want to make available from the diff --git a/src/plugins/Evolve.ts b/src/plugins/Evolve.ts index d90fe443..e95e628c 100644 --- a/src/plugins/Evolve.ts +++ b/src/plugins/Evolve.ts @@ -52,7 +52,7 @@ export class Evolve implements ExecutionContextModifier { //FIXME: side-effect... executionContext.contractDefinition = newContractDefinition; executionContext.handler = newHandler; - executionContext.handler.initState(state); + executionContext.handler.initState(state, executionContext.evaluationOptions.wasmSerializationFormat); this.logger.debug('evolved to:', { evolve: evolvedSrcTxId, newSrcTxId: executionContext.contractDefinition.srcTxId, diff --git a/src/utils/utils.ts b/src/utils/utils.ts index b4192c8a..b41affe6 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -3,6 +3,10 @@ import copy from 'fast-copy'; import { Buffer } from 'redstone-isomorphic'; import { randomUUID } from 'crypto'; +export const exhaustive = (_: never, errorMessage = 'Exhaustive check failed') => { + throw new Error(errorMessage); +}; + export const sleep = (ms: number): Promise => { return new Promise((resolve) => setTimeout(resolve, ms)); }; From a29d5ba96a3699fa7e1e625e038110f866d6abb4 Mon Sep 17 00:00:00 2001 From: Eyal Chojnowski Date: Tue, 17 Jan 2023 15:01:15 +0100 Subject: [PATCH 4/8] Use SerializationFormat instead of strings for media types --- src/contract/deploy/impl/DefaultCreateContract.ts | 1 - src/core/modules/impl/ContractDefinitionLoader.ts | 4 ++-- src/core/modules/impl/handler/WasmHandlerApi.ts | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/contract/deploy/impl/DefaultCreateContract.ts b/src/contract/deploy/impl/DefaultCreateContract.ts index aa71eeea..d01b85f6 100644 --- a/src/contract/deploy/impl/DefaultCreateContract.ts +++ b/src/contract/deploy/impl/DefaultCreateContract.ts @@ -10,7 +10,6 @@ import { CreateContract, ContractData, ContractDeploy, FromSrcTxContractData, Ar import { SourceData, SourceImpl } from './SourceImpl'; import { Buffer } from 'redstone-isomorphic'; import { SerializationFormat } from 'core/modules/StateEvaluator'; -import { exhaustive } from 'utils/utils'; export class DefaultCreateContract implements CreateContract { private readonly logger = LoggerFactory.INST.create('DefaultCreateContract'); diff --git a/src/core/modules/impl/ContractDefinitionLoader.ts b/src/core/modules/impl/ContractDefinitionLoader.ts index 90649103..97b89e37 100644 --- a/src/core/modules/impl/ContractDefinitionLoader.ts +++ b/src/core/modules/impl/ContractDefinitionLoader.ts @@ -124,7 +124,7 @@ export class ContractDefinitionLoader implements DefinitionLoader { private async evalInitialState(contractTx: Transaction): Promise { if (this.tagsParser.getTag(contractTx, SmartWeaveTags.INIT_STATE)) { const format = stringToSerializationFormat( - this.tagsParser.getTag(contractTx, SmartWeaveTags.INIT_STATE_FORMAT) ?? 'application/json' + this.tagsParser.getTag(contractTx, SmartWeaveTags.INIT_STATE_FORMAT) ?? SerializationFormat.JSON ); const initState = this.tagsParser.getTag(contractTx, SmartWeaveTags.INIT_STATE); @@ -147,7 +147,7 @@ export class ContractDefinitionLoader implements DefinitionLoader { private async getInitialStateFromTx(tx: Transaction): Promise { const format = stringToSerializationFormat( - this.tagsParser.getTag(tx, SmartWeaveTags.CONTENT_TYPE) ?? 'application/json' + this.tagsParser.getTag(tx, SmartWeaveTags.CONTENT_TYPE) ?? SerializationFormat.JSON ); switch (format) { diff --git a/src/core/modules/impl/handler/WasmHandlerApi.ts b/src/core/modules/impl/handler/WasmHandlerApi.ts index 640ce62c..b824974d 100644 --- a/src/core/modules/impl/handler/WasmHandlerApi.ts +++ b/src/core/modules/impl/handler/WasmHandlerApi.ts @@ -80,7 +80,6 @@ export class WasmHandlerApi extends AbstractContractHandler { } } - // TODO:noom support SerializationFormat here too initState(state: State, format: SerializationFormat): void { switch (this.contractDefinition.srcWasmLang) { case 'assemblyscript': { From b8a139d2db400017547f0f019dd70b72783a2438 Mon Sep 17 00:00:00 2001 From: Eyal Chojnowski Date: Tue, 17 Jan 2023 15:32:35 +0100 Subject: [PATCH 5/8] fix some utils imports --- src/core/modules/StateEvaluator.ts | 2 +- src/core/modules/impl/ContractDefinitionLoader.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/modules/StateEvaluator.ts b/src/core/modules/StateEvaluator.ts index 79f4b8ff..0f4237fa 100644 --- a/src/core/modules/StateEvaluator.ts +++ b/src/core/modules/StateEvaluator.ts @@ -1,7 +1,7 @@ import { pack, unpack } from 'msgpackr'; import stringify from 'safe-stable-stringify'; -import { exhaustive } from 'utils/utils'; +import { exhaustive } from '../../utils/utils'; import { SortKeyCache, SortKeyCacheResult } from '../../cache/SortKeyCache'; import { CurrentTx } from '../../contract/Contract'; import { ExecutionContext } from '../../core/ExecutionContext'; diff --git a/src/core/modules/impl/ContractDefinitionLoader.ts b/src/core/modules/impl/ContractDefinitionLoader.ts index c307e2c4..021e0843 100644 --- a/src/core/modules/impl/ContractDefinitionLoader.ts +++ b/src/core/modules/impl/ContractDefinitionLoader.ts @@ -12,7 +12,7 @@ import { TagsParser } from './TagsParser'; import { WasmSrc } from './wasm/WasmSrc'; import { WarpEnvironment } from '../../Warp'; import { Deserializers, SerializationFormat, stringToSerializationFormat } from '../StateEvaluator'; -import { exhaustive } from 'utils/utils'; +import { exhaustive } from '../../../utils/utils'; import { SortKeyCache } from '../../../cache/SortKeyCache'; const supportedSrcContentTypes = ['application/javascript', 'application/wasm']; From e041121c2adbe3f28d8344bf32276717afd69706 Mon Sep 17 00:00:00 2001 From: Eyal Chojnowski Date: Tue, 17 Jan 2023 15:34:12 +0100 Subject: [PATCH 6/8] upgrade esbuild to make it compatible with 'satisfies' keyword --- package.json | 2 +- yarn.lock | 270 +++++++++++++++++++++++++-------------------------- 2 files changed, 136 insertions(+), 136 deletions(-) diff --git a/package.json b/package.json index 5193c355..c499f91e 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "arlocal": "1.1.42", "cheerio": "^1.0.0-rc.10", "colors": "^1.4.0", - "esbuild": "0.15.12", + "esbuild": "0.17.2", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^3.4.1", diff --git a/yarn.lock b/yarn.lock index 8d46d18e..12e3778f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -417,15 +417,115 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@esbuild/android-arm@0.15.12": - version "0.15.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.12.tgz#e548b10a5e55b9e10537a049ebf0bc72c453b769" - integrity sha512-IC7TqIqiyE0MmvAhWkl/8AEzpOtbhRNDo7aph47We1NbE5w2bt/Q+giAhe0YYeVpYnIhGMcuZY92qDK6dQauvA== - -"@esbuild/linux-loong64@0.15.12": - version "0.15.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.12.tgz#475b33a2631a3d8ca8aa95ee127f9a61d95bf9c1" - integrity sha512-tZEowDjvU7O7I04GYvWQOS4yyP9E/7YlsB0jjw1Ycukgr2ycEzKyIk5tms5WnLBymaewc6VmRKnn5IJWgK4eFw== +"@esbuild/android-arm64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.2.tgz#73aa058f1fdc43770afd9a7b39654ce7e1b2e774" + integrity sha512-QSkmYISXr2uFoR+NdmmKyR5svYb0cXDCfzwNblLsrC8wTpx/I1L7u/zrjrf4aLoHoRTycZFIewJwBiUrO5DWtQ== + +"@esbuild/android-arm@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.2.tgz#7cdb67672350177edbaa1de1bedd71b295989fab" + integrity sha512-Art7v3xYfqH1gEMUSP0Nx67pNAlC/Y3qSg3mOw8Wg7MP9bJLXL0DrmJaV1Qz1o4FwagtvDgkVOeBDpZgxdj13Q== + +"@esbuild/android-x64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.2.tgz#58cb40ea9502a619551dab8145ec19de3192f3d8" + integrity sha512-5VOaFBI0RK8jJVDHdeU1YJmpxXoOf1RPoiOBhk/Tvpulw7R1SwCsxHvC3eDQcoF0gV7YM4V2wJO0PR9tem6gCQ== + +"@esbuild/darwin-arm64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.2.tgz#d9d60f704e13611db85acf2cc1ce2ed34fe5e46a" + integrity sha512-iQJu1Zn1Wi91D5x/sslEn/jwae1tgSAEHK0R/kYzIr5jO992IJwDDuWhSGll23jHt18RECxahhGG0BWY/bVUTw== + +"@esbuild/darwin-x64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.2.tgz#4ae5735e1cd09b584cff4b8066a246cc62b06c97" + integrity sha512-j750nyrwoRZd3VnPo5sd12/5U27TxFGmvmoDv93G2jiaGJPYKJ/+5IfRAvHahGePTUIRPyOlE5YLFw9MlzuBnw== + +"@esbuild/freebsd-arm64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.2.tgz#0265bd51eb1951b27eb693fd4989a4154e32bd58" + integrity sha512-ti7GU+/KUQQXEPmSUep7efZpA3KR2SkKsVuSL2FE7Yxka9apuqKfymAgQmVPMxstzAgCRBIu8uEu0KFmTfs3/Q== + +"@esbuild/freebsd-x64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.2.tgz#7b29d68def0ab7c5a21e3d8ec67a7a47db5f9993" + integrity sha512-NgooSKWSnrNKRuiumY1dg7KAGpsyXIMcwyOXN9imnqe8VFjqqrEOMqZRik0C1wlfLjiSCuMsj+YUSmBMAJMt0A== + +"@esbuild/linux-arm64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.2.tgz#4ac9edc5011e0d5e3f8673c3c3b00dc5c9bf4459" + integrity sha512-jcJ4cxwQyqEqgDwkqj7820nKx9cM5WBPCCU4oUXvTeG+DkkJE6/P75od0VPHmItFfEJu+/2vV85ebvFVomZcBg== + +"@esbuild/linux-arm@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.2.tgz#5b3f46608b682e32255f6dce10ddcc150826df4d" + integrity sha512-8dfrRTd39n+THdAetwQKNwK6zBPR5oPjMtgRNXvRq8gsn/J5o69zTaOWVi3QO09BljqdShxU2dxDA09lDhdIqQ== + +"@esbuild/linux-ia32@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.2.tgz#21e123e2557236c847b38c2ea4dac3d8fbd1081c" + integrity sha512-dXZ3m++zaRVD2fqOUPP8QTh1Lfg6WO6uZDo/QJ3KdfnIR7dDToDtaA12AgKYvCed9Nuzf/gpKs/7/f6I02b/sg== + +"@esbuild/linux-loong64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.2.tgz#05e2ca319a925de0a28fe2d8a31e158f8172dac9" + integrity sha512-/vntXkzSe9TUp0Rh35Wgye1EOhDtmIMjwC4rtahHcALmDXL+iuQGvwGFvXrP+sBigia/ltLryMAvCiqGV6plqw== + +"@esbuild/linux-mips64el@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.2.tgz#98f0e25b86153d725d4379bc267a2cd4c9bcdd24" + integrity sha512-guYcNHjMRO1BMxWAeb8LDfgQaU8oeUO65xtlclwBD+hX3163KBifEHyao1hK96J10BP9n0UmZug6GhtGZaNm2Q== + +"@esbuild/linux-ppc64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.2.tgz#45252f5343c5178dae93f8f7fc97aa4304cc5cca" + integrity sha512-fzHTnIGIVqgUGZcFnnisguKD4UneF4uwWwkG+i8kBspMDdU1wJ0jha1VdtxWP7Ob1KGxuXcoUlrQkCVO+Z5iOw== + +"@esbuild/linux-riscv64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.2.tgz#6c4446ad19a4d8b070ea0ddf124b6ea53750d5e2" + integrity sha512-Sa+z7csvNVeAsTD83tVSggOb8CAU7EdDuihC8WhtoJfuDVkF5+Vi0imaiCjXQ7Ci5rz/a8IJ1H1MWX3eI9AmuQ== + +"@esbuild/linux-s390x@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.2.tgz#5c03feb73b0c3fa80834eb150cd9c14206681b4e" + integrity sha512-jUFCO+/VA1Y/oeauSNBubp2UtGu4xjBUEFVgMPm0qLuw6xw18yOagKwBOPVmyE3ZSFqGd9BAPZM/JrtadgBryA== + +"@esbuild/linux-x64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.2.tgz#60405f2a40fb792557293a11ba0c380cfe744fcc" + integrity sha512-naygxkSmr6x9tuvpa8iGefnXo3Rc3Noz7c4+Dn0MSfSWJwLaN2YR686e7HkI09irfjDdU5UAq9wcxUwjkYQNUA== + +"@esbuild/netbsd-x64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.2.tgz#be8afb6d91827ecb8a8f42a43c63b528bbdd9c53" + integrity sha512-Hagbdq4EpiG9XXJY6Ozfrl2RN5jkXZXd6BD39f43tWz0d8yyOrRZlofM1eA6JYQbdv6c8BUsUOcgopavIqwx4Q== + +"@esbuild/openbsd-x64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.2.tgz#18e9f4c8284ade701039df1de246a35161dd382e" + integrity sha512-Pkby+VEXY7+aWP8J2RUCfqWbbZz2M1GavRGGnE2kEPzwarba/BOk3B45PSaKwc3iKdK2rgCPCTjC/p9JoKNejA== + +"@esbuild/sunos-x64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.2.tgz#c45c5b6fa406af451e3ebe2ba610bfaad106d20b" + integrity sha512-WAyg4dBTUsAPJ9cRnuQ23cwJWYRhP4e4y0M/l2+EpRjWW+g1MNAXKQQNNhRQ71zc8UixRVrqj+43ReHeZC8mJQ== + +"@esbuild/win32-arm64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.2.tgz#6b6d31077cba24bd8bc9e173b9ae052b0bef5b0c" + integrity sha512-rMbO3gPpxuENd+AnZLgo4J/g+BkwxT3NK7nYpSZ0KlYtSdlxYMIMG5pznX7a1ISZKo67aGStne+K41jdkBywpA== + +"@esbuild/win32-ia32@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.2.tgz#88bb3a510006114d8291506b6ec9ff93f66d0d5c" + integrity sha512-73dWKDMhFk+4owS19OjEVbEDGFPRS1fyga3qOu5HPd5eTxJTjtlVTT/fG/S7AchA0vXS7hOqY70AAir1CkmICg== + +"@esbuild/win32-x64@0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.2.tgz#a7ce1ae475e14febb80e2690430e399491206a61" + integrity sha512-QFJlhf73HCBjTqAWWSIlD8JQBtmue0Dd6UV+KGccycJ3HKj1dCkXdRKJGwc5bZWiI9hrxcWsVEa1kVFaltC4vQ== "@eslint/eslintrc@^0.4.3": version "0.4.3" @@ -3195,133 +3295,33 @@ es6-weak-map@~0.1.4: es6-iterator "~0.1.3" es6-symbol "~2.0.1" -esbuild-android-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.12.tgz#5e8151d5f0a748c71a7fbea8cee844ccf008e6fc" - integrity sha512-MJKXwvPY9g0rGps0+U65HlTsM1wUs9lbjt5CU19RESqycGFDRijMDQsh68MtbzkqWSRdEtiKS1mtPzKneaAI0Q== - -esbuild-android-arm64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.12.tgz#5ee72a6baa444bc96ffcb472a3ba4aba2cc80666" - integrity sha512-Hc9SEcZbIMhhLcvhr1DH+lrrec9SFTiRzfJ7EGSBZiiw994gfkVV6vG0sLWqQQ6DD7V4+OggB+Hn0IRUdDUqvA== - -esbuild-darwin-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.12.tgz#70047007e093fa1b3ba7ef86f9b3fa63db51fe25" - integrity sha512-qkmqrTVYPFiePt5qFjP8w/S+GIUMbt6k8qmiPraECUWfPptaPJUGkCKrWEfYFRWB7bY23FV95rhvPyh/KARP8Q== - -esbuild-darwin-arm64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.12.tgz#41c951f23d9a70539bcca552bae6e5196696ae04" - integrity sha512-z4zPX02tQ41kcXMyN3c/GfZpIjKoI/BzHrdKUwhC/Ki5BAhWv59A9M8H+iqaRbwpzYrYidTybBwiZAIWCLJAkw== - -esbuild-freebsd-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.12.tgz#a761b5afd12bbedb7d56c612e9cfa4d2711f33f0" - integrity sha512-XFL7gKMCKXLDiAiBjhLG0XECliXaRLTZh6hsyzqUqPUf/PY4C6EJDTKIeqqPKXaVJ8+fzNek88285krSz1QECw== - -esbuild-freebsd-arm64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.12.tgz#6b0839d4d58deabc6cbd96276eb8cbf94f7f335e" - integrity sha512-jwEIu5UCUk6TjiG1X+KQnCGISI+ILnXzIzt9yDVrhjug2fkYzlLbl0K43q96Q3KB66v6N1UFF0r5Ks4Xo7i72g== - -esbuild-linux-32@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.12.tgz#bd50bfe22514d434d97d5150977496e2631345b4" - integrity sha512-uSQuSEyF1kVzGzuIr4XM+v7TPKxHjBnLcwv2yPyCz8riV8VUCnO/C4BF3w5dHiVpCd5Z1cebBtZJNlC4anWpwA== - -esbuild-linux-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.12.tgz#074bb2b194bf658245f8490f29c01ffcdfa8c931" - integrity sha512-QcgCKb7zfJxqT9o5z9ZUeGH1k8N6iX1Y7VNsEi5F9+HzN1OIx7ESxtQXDN9jbeUSPiRH1n9cw6gFT3H4qbdvcA== - -esbuild-linux-arm64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.12.tgz#3bf789c4396dc032875a122988efd6f3733f28f5" - integrity sha512-HtNq5xm8fUpZKwWKS2/YGwSfTF+339L4aIA8yphNKYJckd5hVdhfdl6GM2P3HwLSCORS++++7++//ApEwXEuAQ== - -esbuild-linux-arm@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.12.tgz#b91b5a8d470053f6c2c9c8a5e67ec10a71fe4a67" - integrity sha512-Wf7T0aNylGcLu7hBnzMvsTfEXdEdJY/hY3u36Vla21aY66xR0MS5I1Hw8nVquXjTN0A6fk/vnr32tkC/C2lb0A== - -esbuild-linux-mips64le@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.12.tgz#2fb54099ada3c950a7536dfcba46172c61e580e2" - integrity sha512-Qol3+AvivngUZkTVFgLpb0H6DT+N5/zM3V1YgTkryPYFeUvuT5JFNDR3ZiS6LxhyF8EE+fiNtzwlPqMDqVcc6A== - -esbuild-linux-ppc64le@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.12.tgz#9e3b8c09825fb27886249dfb3142a750df29a1b7" - integrity sha512-4D8qUCo+CFKaR0cGXtGyVsOI7w7k93Qxb3KFXWr75An0DHamYzq8lt7TNZKoOq/Gh8c40/aKaxvcZnTgQ0TJNg== - -esbuild-linux-riscv64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.12.tgz#923d0f5b6e12ee0d1fe116b08e4ae4478fe40693" - integrity sha512-G9w6NcuuCI6TUUxe6ka0enjZHDnSVK8bO+1qDhMOCtl7Tr78CcZilJj8SGLN00zO5iIlwNRZKHjdMpfFgNn1VA== - -esbuild-linux-s390x@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.12.tgz#3b1620220482b96266a0c6d9d471d451a1eab86f" - integrity sha512-Lt6BDnuXbXeqSlVuuUM5z18GkJAZf3ERskGZbAWjrQoi9xbEIsj/hEzVnSAFLtkfLuy2DE4RwTcX02tZFunXww== - -esbuild-netbsd-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.12.tgz#276730f80da646859b1af5a740e7802d8cd73e42" - integrity sha512-jlUxCiHO1dsqoURZDQts+HK100o0hXfi4t54MNRMCAqKGAV33JCVvMplLAa2FwviSojT/5ZG5HUfG3gstwAG8w== - -esbuild-openbsd-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.12.tgz#bd0eea1dd2ca0722ed489d88c26714034429f8ae" - integrity sha512-1o1uAfRTMIWNOmpf8v7iudND0L6zRBYSH45sofCZywrcf7NcZA+c7aFsS1YryU+yN7aRppTqdUK1PgbZVaB1Dw== - -esbuild-sunos-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.12.tgz#5e56bf9eef3b2d92360d6d29dcde7722acbecc9e" - integrity sha512-nkl251DpoWoBO9Eq9aFdoIt2yYmp4I3kvQjba3jFKlMXuqQ9A4q+JaqdkCouG3DHgAGnzshzaGu6xofGcXyPXg== - -esbuild-windows-32@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.12.tgz#a4f1a301c1a2fa7701fcd4b91ef9d2620cf293d0" - integrity sha512-WlGeBZHgPC00O08luIp5B2SP4cNCp/PcS+3Pcg31kdcJPopHxLkdCXtadLU9J82LCfw4TVls21A6lilQ9mzHrw== - -esbuild-windows-64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.12.tgz#bc2b467541744d653be4fe64eaa9b0dbbf8e07f6" - integrity sha512-VActO3WnWZSN//xjSfbiGOSyC+wkZtI8I4KlgrTo5oHJM6z3MZZBCuFaZHd8hzf/W9KPhF0lY8OqlmWC9HO5AA== - -esbuild-windows-arm64@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.12.tgz#9a7266404334a86be800957eaee9aef94c3df328" - integrity sha512-Of3MIacva1OK/m4zCNIvBfz8VVROBmQT+gRX6pFTLPngFYcj6TFH/12VveAqq1k9VB2l28EoVMNMUCcmsfwyuA== - -esbuild@0.15.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.12.tgz#6c8e22d6d3b7430d165c33848298d3fc9a1f251c" - integrity sha512-PcT+/wyDqJQsRVhaE9uX/Oq4XLrFh0ce/bs2TJh4CSaw9xuvI+xFrH2nAYOADbhQjUgAhNWC5LKoUsakm4dxng== +esbuild@0.17.2: + version "0.17.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.2.tgz#c37ee608434be1c0e79f872c8bd484fb46af59df" + integrity sha512-odaHSgtYafOXt2nSISwdWlfRkb4ceMX3akY1mWspQpT08jsqVYEK1XtVusr250Rmbx8AVNWjMPI/yyvKqxOKMw== optionalDependencies: - "@esbuild/android-arm" "0.15.12" - "@esbuild/linux-loong64" "0.15.12" - esbuild-android-64 "0.15.12" - esbuild-android-arm64 "0.15.12" - esbuild-darwin-64 "0.15.12" - esbuild-darwin-arm64 "0.15.12" - esbuild-freebsd-64 "0.15.12" - esbuild-freebsd-arm64 "0.15.12" - esbuild-linux-32 "0.15.12" - esbuild-linux-64 "0.15.12" - esbuild-linux-arm "0.15.12" - esbuild-linux-arm64 "0.15.12" - esbuild-linux-mips64le "0.15.12" - esbuild-linux-ppc64le "0.15.12" - esbuild-linux-riscv64 "0.15.12" - esbuild-linux-s390x "0.15.12" - esbuild-netbsd-64 "0.15.12" - esbuild-openbsd-64 "0.15.12" - esbuild-sunos-64 "0.15.12" - esbuild-windows-32 "0.15.12" - esbuild-windows-64 "0.15.12" - esbuild-windows-arm64 "0.15.12" + "@esbuild/android-arm" "0.17.2" + "@esbuild/android-arm64" "0.17.2" + "@esbuild/android-x64" "0.17.2" + "@esbuild/darwin-arm64" "0.17.2" + "@esbuild/darwin-x64" "0.17.2" + "@esbuild/freebsd-arm64" "0.17.2" + "@esbuild/freebsd-x64" "0.17.2" + "@esbuild/linux-arm" "0.17.2" + "@esbuild/linux-arm64" "0.17.2" + "@esbuild/linux-ia32" "0.17.2" + "@esbuild/linux-loong64" "0.17.2" + "@esbuild/linux-mips64el" "0.17.2" + "@esbuild/linux-ppc64" "0.17.2" + "@esbuild/linux-riscv64" "0.17.2" + "@esbuild/linux-s390x" "0.17.2" + "@esbuild/linux-x64" "0.17.2" + "@esbuild/netbsd-x64" "0.17.2" + "@esbuild/openbsd-x64" "0.17.2" + "@esbuild/sunos-x64" "0.17.2" + "@esbuild/win32-arm64" "0.17.2" + "@esbuild/win32-ia32" "0.17.2" + "@esbuild/win32-x64" "0.17.2" escalade@^3.1.1: version "3.1.1" From 17e1338a799e0501a155ff18ec9fe370a5e41780 Mon Sep 17 00:00:00 2001 From: Eyal Chojnowski Date: Tue, 17 Jan 2023 17:22:15 +0100 Subject: [PATCH 7/8] make CreateContract's functions generic instead of itself --- src/contract/deploy/CreateContract.ts | 12 +++++++++--- src/contract/deploy/impl/DefaultCreateContract.ts | 2 +- src/core/Warp.ts | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/contract/deploy/CreateContract.ts b/src/contract/deploy/CreateContract.ts index 41f65d0b..e1051e3b 100644 --- a/src/contract/deploy/CreateContract.ts +++ b/src/contract/deploy/CreateContract.ts @@ -57,10 +57,16 @@ export interface ContractDeploy { srcTxId?: string; } -export interface CreateContract extends Source { - deploy(contractData: ContractData, disableBundling?: boolean): Promise; +export interface CreateContract extends Source { + deploy( + contractData: ContractData, + disableBundling?: boolean + ): Promise; - deployFromSourceTx(contractData: FromSrcTxContractData, disableBundling?: boolean): Promise; + deployFromSourceTx( + contractData: FromSrcTxContractData, + disableBundling?: boolean + ): Promise; deployBundled(rawDataItem: Buffer): Promise; diff --git a/src/contract/deploy/impl/DefaultCreateContract.ts b/src/contract/deploy/impl/DefaultCreateContract.ts index e7ca1b66..00207549 100644 --- a/src/contract/deploy/impl/DefaultCreateContract.ts +++ b/src/contract/deploy/impl/DefaultCreateContract.ts @@ -20,7 +20,7 @@ import { SourceData, SourceImpl } from './SourceImpl'; import { Buffer } from 'redstone-isomorphic'; import { SerializationFormat } from 'core/modules/StateEvaluator'; -export class DefaultCreateContract implements CreateContract { +export class DefaultCreateContract implements CreateContract { private readonly logger = LoggerFactory.INST.create('DefaultCreateContract'); private readonly source: SourceImpl; diff --git a/src/core/Warp.ts b/src/core/Warp.ts index dc5737e9..941036dc 100644 --- a/src/core/Warp.ts +++ b/src/core/Warp.ts @@ -47,7 +47,7 @@ export class Warp { /** * @deprecated createContract will be a private field, please use its methods directly e.g. await warp.deploy(...) */ - readonly createContract: CreateContract; + readonly createContract: CreateContract; readonly testing: Testing; private readonly plugins: Map> = new Map(); From 83e3bb2c6040a9bd64b0751beb468a181a6144e9 Mon Sep 17 00:00:00 2001 From: Eyal Chojnowski Date: Wed, 18 Jan 2023 12:32:29 +0100 Subject: [PATCH 8/8] include tests making use of msgpack for the WASM-JS bridge --- .../data/wasm/rust-erc1155-msgpack/README.md | 6 + .../rust-erc1155-msgpack/build/package.json | 14 + .../build/rust-contract.d.ts | 23 ++ .../build/rust-contract.js | 322 ++++++++++++++++++ .../build/rust-contract_bg.wasm | Bin 0 -> 315255 bytes .../build/rust-contract_bg.wasm.d.ts | 16 + .../wasm/rust-erc1155-msgpack/types/Action.ts | 64 ++++ .../types/ContractError.ts | 54 +++ .../wasm/rust-erc1155-msgpack/types/State.ts | 30 ++ .../wasm/rust-erc1155-msgpack/types/index.ts | 3 + ...-erc1155-msgpack-deploy-write-read.test.ts | 138 ++++++++ 11 files changed, 670 insertions(+) create mode 100644 src/__tests__/integration/data/wasm/rust-erc1155-msgpack/README.md create mode 100644 src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/package.json create mode 100644 src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract.d.ts create mode 100644 src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract.js create mode 100644 src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract_bg.wasm create mode 100644 src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract_bg.wasm.d.ts create mode 100644 src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/Action.ts create mode 100644 src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/ContractError.ts create mode 100644 src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/State.ts create mode 100644 src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/index.ts create mode 100644 src/__tests__/integration/wasm/rust-erc1155-msgpack-deploy-write-read.test.ts diff --git a/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/README.md b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/README.md new file mode 100644 index 00000000..e737fab0 --- /dev/null +++ b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/README.md @@ -0,0 +1,6 @@ +This directory includes the build and the types of Pianity's ERC1155 WASM contract written in Rust. + +The specific commit used to make this build is available here +. +It includes patches that makes it use Msgpack instead of JSON (that is the default at the time of +writing) as its serialization format for the WASM<->JS bridge. diff --git a/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/package.json b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/package.json new file mode 100644 index 00000000..61f8d6fc --- /dev/null +++ b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/package.json @@ -0,0 +1,14 @@ +{ + "name": "warp-erc1155-implementation", + "collaborators": [ + "Eyal Chojnowski" + ], + "version": "0.1.0", + "files": [ + "rust-contract_bg.wasm", + "rust-contract.js", + "rust-contract.d.ts" + ], + "main": "rust-contract.js", + "types": "rust-contract.d.ts" +} \ No newline at end of file diff --git a/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract.d.ts b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract.d.ts new file mode 100644 index 00000000..63c1f3a0 --- /dev/null +++ b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract.d.ts @@ -0,0 +1,23 @@ +/* tslint:disable */ +/* eslint-disable */ +/** +* @param {Uint8Array} interaction +* @returns {Promise} +*/ +export function handle(interaction: Uint8Array): Promise; +/** +* @param {Uint8Array} state +*/ +export function initState(state: Uint8Array): void; +/** +* @returns {Uint8Array} +*/ +export function currentState(): Uint8Array; +/** +* @returns {number} +*/ +export function version(): number; +/** +* @returns {number} +*/ +export function lang(): number; diff --git a/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract.js b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract.js new file mode 100644 index 00000000..f93d692e --- /dev/null +++ b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract.js @@ -0,0 +1,322 @@ +let imports = {}; +imports['__wbindgen_placeholder__'] = module.exports; +let wasm; +const { TextDecoder, TextEncoder } = require(`util`); + +const heap = new Array(32).fill(undefined); + +heap.push(undefined, null, true, false); + +function getObject(idx) { return heap[idx]; } + +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 36) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +let cachegetUint8Memory0 = null; +function getUint8Memory0() { + if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) { + cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachegetUint8Memory0; +} + +function getStringFromWasm0(ptr, len) { + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +function makeMutClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + if (--state.cnt === 0) { + wasm.__wbindgen_export_0.get(state.dtor)(a, state.b); + + } else { + state.a = a; + } + } + }; + real.original = state; + + return real; +} +function __wbg_adapter_10(arg0, arg1, arg2) { + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb281bc39dbcfb59f(arg0, arg1, addHeapObject(arg2)); +} + +/** +* @param {Uint8Array} interaction +* @returns {Promise} +*/ +module.exports.handle = function(interaction) { + var ret = wasm.handle(addHeapObject(interaction)); + return takeObject(ret); +}; + +/** +* @param {Uint8Array} state +*/ +module.exports.initState = function(state) { + wasm.initState(addHeapObject(state)); +}; + +let cachegetInt32Memory0 = null; +function getInt32Memory0() { + if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { + cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachegetInt32Memory0; +} + +function getArrayU8FromWasm0(ptr, len) { + return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); +} +/** +* @returns {Uint8Array} +*/ +module.exports.currentState = function() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.currentState(retptr); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var v0 = getArrayU8FromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 1); + return v0; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +}; + +/** +* @returns {number} +*/ +module.exports.version = function() { + var ret = wasm.version(); + return ret; +}; + +/** +* @returns {number} +*/ +module.exports.lang = function() { + var ret = wasm.lang(); + return ret; +}; + +let WASM_VECTOR_LEN = 0; + +let cachedTextEncoder = new TextEncoder('utf-8'); + +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +} + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); + +function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length); + getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len); + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3); + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); + } +} +function __wbg_adapter_24(arg0, arg1, arg2, arg3) { + wasm.wasm_bindgen__convert__closures__invoke2_mut__he0b27a323ce8a8e6(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); +} + +module.exports.__wbindgen_object_drop_ref = function(arg0) { + takeObject(arg0); +}; + +module.exports.__wbg_owner_8557539ae4390681 = function(arg0) { + var ret = Transaction.owner(); + var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len0 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len0; + getInt32Memory0()[arg0 / 4 + 0] = ptr0; +}; + +module.exports.__wbg_caller_d4b16475811bcec3 = function(arg0) { + var ret = SmartWeave.caller(); + var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len0 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len0; + getInt32Memory0()[arg0 / 4 + 0] = ptr0; +}; + +module.exports.__wbg_id_9dc3f8f1cb3d8f46 = function(arg0) { + var ret = Transaction.id(); + var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len0 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len0; + getInt32Memory0()[arg0 / 4 + 0] = ptr0; +}; + +module.exports.__wbindgen_cb_drop = function(arg0) { + const obj = takeObject(arg0).original; + if (obj.cnt-- == 1) { + obj.a = 0; + return true; + } + var ret = false; + return ret; +}; + +module.exports.__wbg_call_94697a95cb7e239c = function() { return handleError(function (arg0, arg1, arg2) { + var ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); + return addHeapObject(ret); +}, arguments) }; + +module.exports.__wbg_new_4beacc9c71572250 = function(arg0, arg1) { + try { + var state0 = {a: arg0, b: arg1}; + var cb0 = (arg0, arg1) => { + const a = state0.a; + state0.a = 0; + try { + return __wbg_adapter_24(a, state0.b, arg0, arg1); + } finally { + state0.a = a; + } + }; + var ret = new Promise(cb0); + return addHeapObject(ret); + } finally { + state0.a = state0.b = 0; + } +}; + +module.exports.__wbg_resolve_4f8f547f26b30b27 = function(arg0) { + var ret = Promise.resolve(getObject(arg0)); + return addHeapObject(ret); +}; + +module.exports.__wbg_then_a6860c82b90816ca = function(arg0, arg1) { + var ret = getObject(arg0).then(getObject(arg1)); + return addHeapObject(ret); +}; + +module.exports.__wbg_buffer_5e74a88a1424a2e0 = function(arg0) { + var ret = getObject(arg0).buffer; + return addHeapObject(ret); +}; + +module.exports.__wbg_newwithbyteoffsetandlength_278ec7532799393a = function(arg0, arg1, arg2) { + var ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); +}; + +module.exports.__wbg_new_e3b800e570795b3c = function(arg0) { + var ret = new Uint8Array(getObject(arg0)); + return addHeapObject(ret); +}; + +module.exports.__wbg_set_5b8081e9d002f0df = function(arg0, arg1, arg2) { + getObject(arg0).set(getObject(arg1), arg2 >>> 0); +}; + +module.exports.__wbg_length_30803400a8f15c59 = function(arg0) { + var ret = getObject(arg0).length; + return ret; +}; + +module.exports.__wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +}; + +module.exports.__wbindgen_memory = function() { + var ret = wasm.memory; + return addHeapObject(ret); +}; + +module.exports.__wbindgen_closure_wrapper336 = function(arg0, arg1, arg2) { + var ret = makeMutClosure(arg0, arg1, 92, __wbg_adapter_10); + return addHeapObject(ret); +}; + +const path = require('path').join(__dirname, 'rust-contract_bg.wasm'); +const bytes = require('fs').readFileSync(path); + +const wasmModule = new WebAssembly.Module(bytes); +const wasmInstance = new WebAssembly.Instance(wasmModule, imports); +wasm = wasmInstance.exports; +module.exports.__wasm = wasm; + diff --git a/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract_bg.wasm b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract_bg.wasm new file mode 100644 index 0000000000000000000000000000000000000000..8b86dfa2e6716391662be00acbd967415c4d2650 GIT binary patch literal 315255 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

VQ!fVlXIXEEsO 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 literal 0 HcmV?d00001 diff --git a/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract_bg.wasm.d.ts b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract_bg.wasm.d.ts new file mode 100644 index 00000000..51878ec4 --- /dev/null +++ b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/build/rust-contract_bg.wasm.d.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +export const memory: WebAssembly.Memory; +export function handle(a: number): number; +export function initState(a: number): void; +export function currentState(a: number): void; +export function version(): number; +export function lang(): number; +export const __wbindgen_export_0: WebAssembly.Table; +export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb281bc39dbcfb59f(a: number, b: number, c: number): void; +export function __wbindgen_add_to_stack_pointer(a: number): number; +export function __wbindgen_free(a: number, b: number): void; +export function __wbindgen_malloc(a: number): number; +export function __wbindgen_realloc(a: number, b: number, c: number): number; +export function __wbindgen_exn_store(a: number): void; +export function wasm_bindgen__convert__closures__invoke2_mut__he0b27a323ce8a8e6(a: number, b: number, c: number, d: number): void; diff --git a/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/Action.ts b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/Action.ts new file mode 100644 index 00000000..b3de2d59 --- /dev/null +++ b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/Action.ts @@ -0,0 +1,64 @@ +export type Action = + | { + function: "balanceOf"; + target: string; + tokenId?: string | null; + } + | { + from?: string | null; + function: "transfer"; + qty: string; + to: string; + tokenId?: string | null; + } + | { + allowFreeTransfer?: boolean | null; + canEvolve?: boolean | null; + function: "configure"; + operators?: string[] | null; + paused?: boolean | null; + proxies?: string[] | null; + superOperators?: string[] | null; + } + | { + approved: boolean; + function: "setApprovalForAll"; + operator: string; + } + | { + function: "isApprovedForAll"; + operator: string; + owner: string; + } + | { + function: "evolve"; + value: string; + } + | { + baseId?: string | null; + function: "mint"; + prefix?: string | null; + qty: string; + } + | { + function: "burn"; + owner?: string | null; + qty: string; + tokenId?: string | null; + } + | { + actions: Action[]; + function: "batch"; + }; + +/** + * This type allows to restrict the type of an interaction to a specific action. + * + * Example: + * ```typescript + * const specificAction: Actions["specificAction"] = { function: "specificAction", foo: "bar" }; + * ``` + */ +export type Actions = { + [K in Action["function"]]: Action & { function: K }; +}; \ No newline at end of file diff --git a/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/ContractError.ts b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/ContractError.ts new file mode 100644 index 00000000..fa8cda33 --- /dev/null +++ b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/ContractError.ts @@ -0,0 +1,54 @@ +export type ContractError = + | { + data: string; + kind: "RuntimeError"; + } + | { + kind: "TransferAmountMustBeHigherThanZero"; + } + | { + kind: "TransferFromAndToCannotBeEqual"; + } + | { + data: string; + kind: "TokenNotFound"; + } + | { + kind: "IDontLikeThisContract"; + } + | { + data: string; + kind: "OwnerBalanceNotEnough"; + } + | { + kind: "OnlyOwnerCanEvolve"; + } + | { + kind: "EvolveNotAllowed"; + } + | { + kind: "ForbiddenNestedBatch"; + } + | { + kind: "CannotMixeReadAndWrite"; + } + | { + kind: "EmptyBatch"; + } + | { + kind: "UnauthorizedConfiguration"; + } + | { + data: string; + kind: "UnauthorizedAddress"; + } + | { + data: string; + kind: "UnauthorizedTransfer"; + } + | { + kind: "TokenAlreadyExists"; + } + | { + kind: "ContractIsPaused"; + }; diff --git a/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/State.ts b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/State.ts new file mode 100644 index 00000000..6d630a2a --- /dev/null +++ b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/State.ts @@ -0,0 +1,30 @@ +export interface State { + approvals: { + [k: string]: { + [k: string]: boolean; + }; + }; + defaultToken: string; + evolve?: string | null; + name: string; + settings: Settings; + tickerNonce: number; + tokens: { + [k: string]: Token; + }; +} +export interface Settings { + allowFreeTransfer: boolean; + canEvolve: boolean; + operators: string[]; + paused: boolean; + proxies: string[]; + superOperators: string[]; +} +export interface Token { + balances: { + [k: string]: string; + }; + ticker: string; + txId?: string | null; +} diff --git a/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/index.ts b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/index.ts new file mode 100644 index 00000000..21a79f80 --- /dev/null +++ b/src/__tests__/integration/data/wasm/rust-erc1155-msgpack/types/index.ts @@ -0,0 +1,3 @@ +export * from "./Action"; +export * from "./State"; +export * from "./ContractError"; diff --git a/src/__tests__/integration/wasm/rust-erc1155-msgpack-deploy-write-read.test.ts b/src/__tests__/integration/wasm/rust-erc1155-msgpack-deploy-write-read.test.ts new file mode 100644 index 00000000..068fc2da --- /dev/null +++ b/src/__tests__/integration/wasm/rust-erc1155-msgpack-deploy-write-read.test.ts @@ -0,0 +1,138 @@ +import fs from 'fs'; +import path from 'path'; + +import ArLocal from 'arlocal'; +import { JWKInterface } from 'arweave/node/lib/wallet'; +import { pack } from 'msgpackr'; + +import { mineBlock } from '../_helpers'; +import { WasmSrc } from '../../../core/modules/impl/wasm/WasmSrc'; +import { SmartWeaveTags } from '../../../core/SmartWeaveTags'; +import { Warp } from '../../../core/Warp'; +import { WarpFactory } from '../../../core/WarpFactory'; +import { LoggerFactory } from '../../../logging/LoggerFactory'; +import { ArweaveWrapper } from '../../../utils/ArweaveWrapper'; +import { TagsParser } from '../../../core/modules/impl/TagsParser'; +import { SerializationFormat } from '../../../core/modules/StateEvaluator'; + +import * as Erc1155 from '../data/wasm/rust-erc1155-mspack/types'; +import { Contract, WriteInteractionResponse } from 'contract/Contract'; + +const DEFAULT_TOKEN = 'PTY'; +const ARLOCAL_PORT = 1210; + +describe('Testing a Rust contract that uses Msgpack as its WASM<->JS serialization format', () => { + LoggerFactory.INST.logLevel('error'); + + let wallet: JWKInterface; + let walletAddress: string; + + let initialState: Erc1155.State; + + let arlocal: ArLocal; + let warp: Warp; + + let contractTxId: string; + let contract: Contract; + let interact: (input: Erc1155.Action) => Promise; + + let arweaveWrapper: ArweaveWrapper; + let tagsParser: TagsParser; + + beforeAll(async () => { + // note: each tests suit (i.e. file with tests that Jest is running concurrently + // with another files has to have ArLocal set to a different port!) + arlocal = new ArLocal(ARLOCAL_PORT, false); + await arlocal.start(); + + warp = WarpFactory.forLocal(ARLOCAL_PORT); + arweaveWrapper = new ArweaveWrapper(warp.arweave); + tagsParser = new TagsParser(); + + ({ jwk: wallet, address: walletAddress } = await warp.generateWallet()); + + const contractSrc = fs.readFileSync( + path.join(__dirname, '../data/wasm/rust-erc1155-mspack/build/rust-contract_bg.wasm') + ); + + initialState = { + defaultToken: DEFAULT_TOKEN, + name: 'TEST-ERC1155-MSGPACK', + tickerNonce: 0, + tokens: { + [DEFAULT_TOKEN]: { + ticker: DEFAULT_TOKEN, + balances: { + [walletAddress]: '200' + } + } + }, + approvals: {}, + settings: { + allowFreeTransfer: true, + paused: false, + proxies: [], + canEvolve: false, + operators: [], + superOperators: [] + } + }; + + // deploying contract using the new SDK. + contractTxId = ( + await warp.deploy({ + wallet, + stateFormat: SerializationFormat.MSGPACK, + initState: pack(initialState), + src: contractSrc, + wasmSrcCodeDir: path.join(__dirname, '../data/wasm/rust/src'), + wasmGlueCode: path.join(__dirname, '../data/wasm/rust/rust-pst.js') + }) + ).contractTxId; + + contract = warp + .contract(contractTxId) + .setEvaluationOptions({ wasmSerializationFormat: SerializationFormat.MSGPACK }) + .connect(wallet); + + interact = async (input: Erc1155.Action) => contract.writeInteraction(input); + + await mineBlock(warp); + }, 50000); + + afterAll(async () => { + await arlocal.stop(); + }); + + it('should properly deploy contract', async () => { + const contractTx = await warp.arweave.transactions.get(contractTxId); + expect(contractTx).not.toBeNull(); + expect(tagsParser.getTag(contractTx, SmartWeaveTags.CONTENT_TYPE)).toEqual(SerializationFormat.MSGPACK); + + const contractSrcTxId = tagsParser.getTag(contractTx, SmartWeaveTags.CONTRACT_SRC_TX_ID); + const contractSrcTx = await warp.arweave.transactions.get(contractSrcTxId); + + expect(tagsParser.getTag(contractSrcTx, SmartWeaveTags.CONTENT_TYPE)).toEqual('application/wasm'); + expect(tagsParser.getTag(contractSrcTx, SmartWeaveTags.WASM_LANG)).toEqual('rust'); + expect(tagsParser.getTag(contractSrcTx, SmartWeaveTags.WASM_META)).toEqual(JSON.stringify({ dtor: 74 })); + + const srcTxData = await arweaveWrapper.txData(contractSrcTxId); + const wasmSrc = new WasmSrc(srcTxData); + expect(wasmSrc.wasmBinary()).not.toBeNull(); + expect(wasmSrc.additionalCode()).toEqual( + fs.readFileSync(path.join(__dirname, '../data/wasm/rust/rust-pst.js'), 'utf-8') + ); + expect((await wasmSrc.sourceCode()).size).toEqual(11); + }); + + it('should properly transfer tokens', async () => { + await interact({ function: 'transfer', qty: '100', to: 'bob' }); + + await mineBlock(warp); + + const { state } = (await contract.readState()).cachedValue; + + expect(state.tokens['PTY'].balances[walletAddress]).toEqual('100'); + expect(state.tokens['PTY'].balances['bob']).toEqual('100'); + }); +});