feat: injecting metering to wasm binary
This commit is contained in:
committed by
Piotr Pędziwiatr
parent
4e38dec7f9
commit
ad1bed08f6
@@ -62,7 +62,8 @@
|
||||
"lodash": "^4.17.21",
|
||||
"redstone-isomorphic": "^1.0.2",
|
||||
"tslog": "^3.2.2",
|
||||
"undici": "^4.12.2"
|
||||
"undici": "^4.12.2",
|
||||
"wasm-metering": "^0.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cheerio": "^0.22.30",
|
||||
|
||||
@@ -6,7 +6,7 @@ import {ContractType} from "./modules/CreateContract";
|
||||
export type ContractDefinition<State> = {
|
||||
txId: string;
|
||||
srcTxId: string;
|
||||
src: ArrayBuffer;
|
||||
src: Buffer;
|
||||
initState: State;
|
||||
minFee: string;
|
||||
owner: string;
|
||||
|
||||
@@ -9,11 +9,12 @@ import {
|
||||
normalizeContractSource,
|
||||
SmartWeaveGlobal
|
||||
} from '@smartweave';
|
||||
import { ContractHandlerApi } from './ContractHandlerApi';
|
||||
import {ContractHandlerApi} from './ContractHandlerApi';
|
||||
import loader from '@assemblyscript/loader/umd';
|
||||
import { imports } from './wasmImports';
|
||||
import { WasmContractHandlerApi } from './WasmContractHandlerApi';
|
||||
import {imports} from './wasmImports';
|
||||
import {WasmContractHandlerApi} from './WasmContractHandlerApi';
|
||||
|
||||
const metering = require('wasm-metering');
|
||||
/**
|
||||
* A factory that produces handlers that are compatible with the "current" style of
|
||||
* writing SW contracts (ie. using "handle" function).
|
||||
@@ -30,111 +31,28 @@ export class HandlerExecutorFactory implements ExecutorFactory<HandlerApi<unknow
|
||||
});
|
||||
|
||||
if (contractDefinition.contractType == 'js') {
|
||||
this.logger.info("Creating handler for js contract", contractDefinition.txId);
|
||||
const normalizedSource = normalizeContractSource(this.arweave.utils.bufferToString(contractDefinition.src));
|
||||
|
||||
const contractFunction = new Function(normalizedSource);
|
||||
|
||||
return new ContractHandlerApi(swGlobal, contractFunction, contractDefinition);
|
||||
} else {
|
||||
let wasmExports;
|
||||
this.logger.info("Creating handler for wasm contract", contractDefinition.txId);
|
||||
|
||||
const wasmModule = loader.instantiateSync(contractDefinition.src, {
|
||||
metering: {
|
||||
usegas: (gas) => {
|
||||
if (gas < 0) {
|
||||
return;
|
||||
}
|
||||
swGlobal.gasUsed += gas;
|
||||
if (swGlobal.gasUsed > swGlobal.gasLimit) {
|
||||
throw new Error(
|
||||
`[RE:OOG] Out of gas! Limit: ${formatGas(swGlobal.gasUsed)}, used: ${formatGas(swGlobal.gasLimit)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
console: {
|
||||
'console.log': function (msgPtr) {
|
||||
console.log(`Contract: ${wasmExports.__getString(msgPtr)}`);
|
||||
},
|
||||
'console.logO': function (msgPtr, objPtr) {
|
||||
console.log(`Contract: ${wasmExports.__getString(msgPtr)}`, JSON.parse(wasmExports.__getString(objPtr)));
|
||||
}
|
||||
},
|
||||
block: {
|
||||
'Block.height': function () {
|
||||
return 875290;
|
||||
},
|
||||
'Block.indep_hash': function () {
|
||||
return wasmExports.__newString('iIMsQJ1819NtkEUEMBRl6-7I6xkeDipn1tK4w_cDFczRuD91oAZx5qlgSDcqq1J1');
|
||||
},
|
||||
'Block.timestamp': function () {
|
||||
return 123123123;
|
||||
}
|
||||
},
|
||||
transaction: {
|
||||
'Transaction.id': function () {
|
||||
return wasmExports.__newString('Transaction.id');
|
||||
},
|
||||
'Transaction.owner': function () {
|
||||
return wasmExports.__newString('Transaction.owner');
|
||||
},
|
||||
'Transaction.target': function () {
|
||||
return wasmExports.__newString('Transaction.target');
|
||||
}
|
||||
},
|
||||
contract: {
|
||||
'Contract.id': function () {
|
||||
return wasmExports.__newString('Contract.id');
|
||||
},
|
||||
'Contract.owner': function () {
|
||||
return wasmExports.__newString('Contract.owner');
|
||||
}
|
||||
},
|
||||
msg: {
|
||||
'msg.sender': function () {
|
||||
return wasmExports.__newString('msg.sender');
|
||||
}
|
||||
},
|
||||
api: {
|
||||
_readContractState: (fnIndex, contractTxIdPtr) => {
|
||||
const contractTxId = wasmExports.__getString(contractTxIdPtr);
|
||||
const callbackFn = getFn(fnIndex);
|
||||
console.log('Simulating read state of', contractTxId);
|
||||
return setTimeout(() => {
|
||||
console.log('calling callback');
|
||||
callbackFn(
|
||||
wasmExports.__newString(
|
||||
JSON.stringify({
|
||||
contractTxId
|
||||
})
|
||||
)
|
||||
);
|
||||
}, 1000);
|
||||
},
|
||||
clearTimeout
|
||||
},
|
||||
env: {
|
||||
abort(messagePtr, fileNamePtr, line, column) {
|
||||
console.error('--------------------- Error message from AssemblyScript ----------------------');
|
||||
console.error(' ' + wasmExports.__getString(messagePtr));
|
||||
console.error(' In file "' + wasmExports.__getString(fileNamePtr) + '"');
|
||||
console.error(` on line ${line}, column ${column}.`);
|
||||
console.error('------------------------------------------------------------------------------\n');
|
||||
}
|
||||
}
|
||||
let wasmModuleData = {
|
||||
exports: null
|
||||
};
|
||||
|
||||
const meteredWasmBinary = metering.meterWASM(contractDefinition.src, {
|
||||
meterType: 'i32'
|
||||
});
|
||||
|
||||
function getFn(idx) {
|
||||
return wasmExports.table.get(idx);
|
||||
}
|
||||
const wasmModule = loader.instantiateSync(meteredWasmBinary, imports(swGlobal, wasmModuleData));
|
||||
|
||||
function formatGas(gas) {
|
||||
return gas * 1e-4;
|
||||
}
|
||||
wasmModuleData.exports = wasmModule.exports;
|
||||
|
||||
wasmExports = wasmModule.exports;
|
||||
|
||||
return new WasmContractHandlerApi(swGlobal, contractDefinition, wasmExports);
|
||||
return new WasmContractHandlerApi(swGlobal, contractDefinition, wasmModule.exports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {SmartWeaveGlobal} from "@smartweave";
|
||||
|
||||
export function imports(swGlobal: SmartWeaveGlobal, wasmExports: any): any {
|
||||
export const imports = (swGlobal: SmartWeaveGlobal, wasmModule: any): any => {
|
||||
return {
|
||||
metering: {
|
||||
usegas: (gas) => {
|
||||
@@ -15,10 +15,10 @@ export function imports(swGlobal: SmartWeaveGlobal, wasmExports: any): any {
|
||||
},
|
||||
console: {
|
||||
"console.log": function (msgPtr) {
|
||||
console.log(`Contract: ${wasmExports.__getString(msgPtr)}`);
|
||||
console.log(`Contract: ${wasmModule.exports.__getString(msgPtr)}`);
|
||||
},
|
||||
"console.logO": function (msgPtr, objPtr) {
|
||||
console.log(`Contract: ${wasmExports.__getString(msgPtr)}`, JSON.parse(wasmExports.__getString(objPtr)));
|
||||
console.log(`Contract: ${wasmModule.exports.__getString(msgPtr)}`, JSON.parse(wasmModule.exports.__getString(objPtr)));
|
||||
},
|
||||
},
|
||||
block: {
|
||||
@@ -26,7 +26,7 @@ export function imports(swGlobal: SmartWeaveGlobal, wasmExports: any): any {
|
||||
return 875290;
|
||||
},
|
||||
"Block.indep_hash": function () {
|
||||
return wasmExports.__newString("iIMsQJ1819NtkEUEMBRl6-7I6xkeDipn1tK4w_cDFczRuD91oAZx5qlgSDcqq1J1");
|
||||
return wasmModule.exports.__newString("iIMsQJ1819NtkEUEMBRl6-7I6xkeDipn1tK4w_cDFczRuD91oAZx5qlgSDcqq1J1");
|
||||
},
|
||||
"Block.timestamp": function () {
|
||||
return 123123123;
|
||||
@@ -34,36 +34,36 @@ export function imports(swGlobal: SmartWeaveGlobal, wasmExports: any): any {
|
||||
},
|
||||
transaction: {
|
||||
"Transaction.id": function () {
|
||||
return wasmExports.__newString("Transaction.id");
|
||||
return wasmModule.exports.__newString("Transaction.id");
|
||||
},
|
||||
"Transaction.owner": function () {
|
||||
return wasmExports.__newString("Transaction.owner");
|
||||
return wasmModule.exports.__newString("Transaction.owner");
|
||||
},
|
||||
"Transaction.target": function () {
|
||||
return wasmExports.__newString("Transaction.target");
|
||||
return wasmModule.exports.__newString("Transaction.target");
|
||||
},
|
||||
},
|
||||
contract: {
|
||||
"Contract.id": function () {
|
||||
return wasmExports.__newString("Contract.id");
|
||||
return wasmModule.exports.__newString("Contract.id");
|
||||
},
|
||||
"Contract.owner": function () {
|
||||
return wasmExports.__newString("Contract.owner");
|
||||
return wasmModule.exports.__newString("Contract.owner");
|
||||
},
|
||||
},
|
||||
msg: {
|
||||
"msg.sender": function () {
|
||||
return wasmExports.__newString("msg.sender");
|
||||
return wasmModule.exports.__newString("msg.sender");
|
||||
},
|
||||
},
|
||||
api: {
|
||||
_readContractState: (fnIndex, contractTxIdPtr) => {
|
||||
const contractTxId = wasmExports.__getString(contractTxIdPtr);
|
||||
const contractTxId = wasmModule.exports.__getString(contractTxIdPtr);
|
||||
const callbackFn = getFn(fnIndex);
|
||||
console.log("Simulating read state of", contractTxId);
|
||||
return setTimeout(() => {
|
||||
console.log('calling callback');
|
||||
callbackFn(wasmExports.__newString(JSON.stringify({
|
||||
callbackFn(wasmModule.exports.__newString(JSON.stringify({
|
||||
contractTxId
|
||||
})));
|
||||
}, 1000);
|
||||
@@ -73,9 +73,9 @@ export function imports(swGlobal: SmartWeaveGlobal, wasmExports: any): any {
|
||||
env: {
|
||||
abort(messagePtr, fileNamePtr, line, column) {
|
||||
console.error("--------------------- Error message from AssemblyScript ----------------------");
|
||||
console.error(" " + wasmExports.__getString(messagePtr));
|
||||
console.error(" " + wasmModule.exports.__getString(messagePtr));
|
||||
console.error(
|
||||
' In file "' + wasmExports.__getString(fileNamePtr) + '"'
|
||||
' In file "' + wasmModule.exports.__getString(fileNamePtr) + '"'
|
||||
);
|
||||
console.error(` on line ${line}, column ${column}.`);
|
||||
console.error("------------------------------------------------------------------------------\n");
|
||||
@@ -84,9 +84,9 @@ export function imports(swGlobal: SmartWeaveGlobal, wasmExports: any): any {
|
||||
}
|
||||
|
||||
function getFn(idx) {
|
||||
return wasmExports.table.get(idx);
|
||||
return wasmModule.exports.table.get(idx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function formatGas(gas) {
|
||||
return gas * 1e-4;
|
||||
|
||||
@@ -20,7 +20,7 @@ export class DebuggableExecutorFactory<Api> implements ExecutorFactory<Api> {
|
||||
|
||||
contractDefinition = {
|
||||
...contractDefinition,
|
||||
src: enc.encode(this.sourceCode[contractDefinition.txId])
|
||||
src: Buffer.from(enc.encode(this.sourceCode[contractDefinition.txId]))
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -88,13 +88,13 @@ export class ArweaveWrapper {
|
||||
});
|
||||
}
|
||||
|
||||
async txData(id: string): Promise<ArrayBuffer> {
|
||||
async txData(id: string): Promise<Buffer> {
|
||||
const response = await fetch(`${this.baseUrl}/${id}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Unable to load tx data ${id}`);
|
||||
}
|
||||
const buffer = await response.arrayBuffer();
|
||||
return buffer;
|
||||
return Buffer.from(buffer);
|
||||
}
|
||||
|
||||
async txDataString(id: string): Promise<string> {
|
||||
|
||||
Reference in New Issue
Block a user