From a7558fd866991aab3a9d5f62e38beb8db166b4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Konopka?= Date: Tue, 14 Mar 2023 11:03:23 +0100 Subject: [PATCH] fix: smartweave global in constructor --- .../integration/basic/constructor.test.ts | 18 +++++++- .../data/constructor/constructor.js | 11 ++++- src/contract/deploy/CreateContract.ts | 2 +- src/core/modules/impl/handler/JsHandlerApi.ts | 42 ++++++++++++++++--- .../modules/impl/handler/WasmHandlerApi.ts | 2 +- 5 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/__tests__/integration/basic/constructor.test.ts b/src/__tests__/integration/basic/constructor.test.ts index eb157510..4b7f7e60 100644 --- a/src/__tests__/integration/basic/constructor.test.ts +++ b/src/__tests__/integration/basic/constructor.test.ts @@ -121,7 +121,7 @@ describe('Constructor', () => { }); }); - describe('Constructor has access to all smartweave globals', () => { + describe('Constructor has access to part of smartweave globals', () => { it('should assign as caller deployer of contract', async () => { const contract = await deployContract({}); @@ -181,6 +181,22 @@ describe('Constructor', () => { expect(state2.counter).toStrictEqual(2); }); + it('should fail to access block data', async () => { + const contract = await deployContract({ addToState: { accessBlock: true } }); + + await expect(contract.readState()).rejects.toThrowError( + 'ConstructorError: SmartWeave.block object is not accessible in constructor' + ); + }); + + it('should fail to access vrf data', async () => { + const contract = await deployContract({ addToState: { accessVrf: true } }); + + await expect(contract.readState()).rejects.toThrowError( + 'ConstructorError: SmartWeave.vrf object is not accessible in constructor' + ); + }); + describe('Internal writes', () => { it('should throw when using internal writes in contract in __init', async () => { const writesInConstructorContract = await deployContract({ diff --git a/src/__tests__/integration/data/constructor/constructor.js b/src/__tests__/integration/data/constructor/constructor.js index ab518b46..066d4d6b 100644 --- a/src/__tests__/integration/data/constructor/constructor.js +++ b/src/__tests__/integration/data/constructor/constructor.js @@ -8,10 +8,19 @@ export async function handle(state, action) { state.caller2 = SmartWeave.caller; await SmartWeave.kv.put("__init", SmartWeave.transaction.id); + state.counter = action.input.args.counter + 1; + if (action.input.args.fail) { throw new ContractError("Fail on purpose") } - state.counter = action.input.args.counter + 1; + + if (action.input.args.accessBlock) { + SmartWeave.block.timestamp; + } + + if (action.input.args.accessVrf) { + SmartWeave.vrf.data; + } } return { state } diff --git a/src/contract/deploy/CreateContract.ts b/src/contract/deploy/CreateContract.ts index 77df7f1b..2811db25 100644 --- a/src/contract/deploy/CreateContract.ts +++ b/src/contract/deploy/CreateContract.ts @@ -22,7 +22,7 @@ export const emptyTransfer: ArTransfer = { }; export type EvaluationManifest = { - evaluationOptions: Partial; + evaluationOptions?: Partial; plugins?: WarpPluginType[]; }; diff --git a/src/core/modules/impl/handler/JsHandlerApi.ts b/src/core/modules/impl/handler/JsHandlerApi.ts index f5d13837..7dc7cd37 100644 --- a/src/core/modules/impl/handler/JsHandlerApi.ts +++ b/src/core/modules/impl/handler/JsHandlerApi.ts @@ -51,17 +51,20 @@ export class JsHandlerApi extends AbstractContractHandler { initialState: State, executionContext: ExecutionContext ): Promise { - if (this.contractDefinition.manifest?.evaluationOptions.useConstructor) { + if (this.contractDefinition.manifest?.evaluationOptions?.useConstructor) { const interaction = { input: { function: INIT_FUNC_NAME, args: initialState } as Input, caller: this.contractDefinition.owner }; - const interactionTx = { ...this.contractDefinition.contractTx, sortKey: genesisSortKey }; - // this is hard corded sortKey to make KV possible + + const interactionTx = (await this.makeInteractionTxFromContractTx( + this.contractDefinition.contractTx, + this.contractDefinition.owner + )) as GQLNodeInterface; const interactionData: InteractionData = { interaction, interactionTx }; this.setupSwGlobal(interactionData); - this.disableInternalWritesForConstructor(); + this.configureSwGlobalForConstructor(); const result = await this.runContractFunction(executionContext, interaction, {} as State); if (result.type !== 'ok') { @@ -73,16 +76,32 @@ export class JsHandlerApi extends AbstractContractHandler { } } + private async makeInteractionTxFromContractTx( + contractTx: ContractDefinition['contractTx'], + owner: string + ): Promise> { + return { + id: contractTx.id, + tags: contractTx.tags, + recipient: contractTx.target, + owner: { address: owner, key: null }, + quantity: { winston: contractTx.quantity, ar: null }, + fee: { winston: contractTx.fee, ar: null }, + sortKey: genesisSortKey + }; + } + private assertNotConstructorCall(interaction: ContractInteraction) { if ( - this.contractDefinition.manifest?.evaluationOptions.useConstructor && + this.contractDefinition.manifest?.evaluationOptions?.useConstructor && interaction.input['function'] === INIT_FUNC_NAME ) { throw new Error(`You have enabled {useConstructor: true} option, so you can't call function ${INIT_FUNC_NAME}`); } } - private disableInternalWritesForConstructor() { + private configureSwGlobalForConstructor() { + // disable internal writes const templateErrorMessage = (op) => `Can't ${op} foreign contract state: Internal writes feature is not available in constructor`; this.swGlobal.contracts.readContractState = () => @@ -92,6 +111,17 @@ export class JsHandlerApi extends AbstractContractHandler { throwErrorWithName('ConstructorError', templateErrorMessage('refreshState')); this.swGlobal.contracts.viewContractState = () => throwErrorWithName('ConstructorError', templateErrorMessage('viewContractState')); + + const disabledVrf = new Proxy(this.swGlobal.vrf, { + get: () => throwErrorWithName('ConstructorError', `SmartWeave.vrf object is not accessible in constructor`) + }); + + const disabledBlock = new Proxy(this.swGlobal.block, { + get: () => throwErrorWithName('ConstructorError', 'SmartWeave.block object is not accessible in constructor') + }); + + this.swGlobal.vrf = disabledVrf; + this.swGlobal.block = disabledBlock; } private async runContractFunction( diff --git a/src/core/modules/impl/handler/WasmHandlerApi.ts b/src/core/modules/impl/handler/WasmHandlerApi.ts index 82fa3405..62c79b77 100644 --- a/src/core/modules/impl/handler/WasmHandlerApi.ts +++ b/src/core/modules/impl/handler/WasmHandlerApi.ts @@ -129,7 +129,7 @@ export class WasmHandlerApi extends AbstractContractHandler { initialState: State, executionContext: ExecutionContext ): Promise { - if (this.contractDefinition.manifest?.evaluationOptions.useConstructor) { + if (this.contractDefinition.manifest?.evaluationOptions?.useConstructor) { throw Error('Constructor is not implemented for wasm'); } return initialState;