diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 197507f..1d7e0fe 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,8 @@ jobs: version: 'latest' - name: Install modules run: yarn + - name: Lint + run: yarn lint - name: Build package run: yarn build - name: Run unit tests diff --git a/src/__tests__/unit/evaluation-options.test.ts b/src/__tests__/unit/evaluation-options.test.ts index 1ab0cac..ed35c5a 100644 --- a/src/__tests__/unit/evaluation-options.test.ts +++ b/src/__tests__/unit/evaluation-options.test.ts @@ -102,13 +102,115 @@ describe('Evaluation options evaluator', () => { walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/' }); - expect(function () { - const result = new EvaluationOptionsEvaluator(contract2.evaluationOptions(), { + expect(new EvaluationOptionsEvaluator(contract2.evaluationOptions(), { internalWrites: false + }).rootOptions).toEqual({ + allowBigInt: false, + cacheEveryNInteractions: -1, + gasLimit: 2222, + ignoreExceptions: true, + internalWrites: false, + maxCallDepth: 5, + maxInteractionEvaluationTimeSeconds: 60, + mineArLocalBlocks: true, + remoteStateSyncEnabled: false, + remoteStateSyncSource: 'https://dre-1.warp.cc/contract', + sequencerUrl: 'https://d1o5nlqr4okus2.cloudfront.net/', + sourceType: 'both', + stackTrace: { + saveState: false + }, + throwOnInternalWriteError: true, + unsafeClient: 'allow', + updateCacheForEachInteraction: false, + useKVStorage: false, + waitForConfirmation: false, + useConstructor: false, + walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/' + }); + + expect(new EvaluationOptionsEvaluator(contract2.evaluationOptions(), { + unsafeClient: 'throw' + }).rootOptions).toEqual({ + allowBigInt: false, + cacheEveryNInteractions: -1, + gasLimit: 2222, + ignoreExceptions: true, + internalWrites: true, + maxCallDepth: 5, + maxInteractionEvaluationTimeSeconds: 60, + mineArLocalBlocks: true, + remoteStateSyncEnabled: false, + remoteStateSyncSource: 'https://dre-1.warp.cc/contract', + sequencerUrl: 'https://d1o5nlqr4okus2.cloudfront.net/', + sourceType: 'both', + stackTrace: { + saveState: false + }, + throwOnInternalWriteError: true, + unsafeClient: 'throw', + updateCacheForEachInteraction: false, + useKVStorage: false, + waitForConfirmation: false, + useConstructor: false, + walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/' + }); + + expect(new EvaluationOptionsEvaluator(contract2.evaluationOptions(), { + unsafeClient: 'skip' + }).rootOptions).toEqual({ + allowBigInt: false, + cacheEveryNInteractions: -1, + gasLimit: 2222, + ignoreExceptions: true, + internalWrites: true, + maxCallDepth: 5, + maxInteractionEvaluationTimeSeconds: 60, + mineArLocalBlocks: true, + remoteStateSyncEnabled: false, + remoteStateSyncSource: 'https://dre-1.warp.cc/contract', + sequencerUrl: 'https://d1o5nlqr4okus2.cloudfront.net/', + sourceType: 'both', + stackTrace: { + saveState: false + }, + throwOnInternalWriteError: true, + unsafeClient: 'skip', + updateCacheForEachInteraction: false, + useKVStorage: false, + waitForConfirmation: false, + useConstructor: false, + walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/' + }); + + + const contract3 = warp.contract(null).setEvaluationOptions({ + internalWrites: false, + unsafeClient: 'throw', + gasLimit: 2222, + maxCallDepth: 5 + }); + + expect(function () { + const result = new EvaluationOptionsEvaluator(contract3.evaluationOptions(), { + internalWrites: true }).rootOptions; - }).toThrow('Option {internalWrites} differs.'); + }).toThrow('Cannot proceed with contract evaluation.'); + + expect(function () { + const result = new EvaluationOptionsEvaluator(contract3.evaluationOptions(), { + unsafeClient: 'allow' + }).rootOptions; + }).toThrow('Cannot proceed with contract evaluation.'); + + expect(function () { + const result = new EvaluationOptionsEvaluator(contract3.evaluationOptions(), { + unsafeClient: 'skip' + }).rootOptions; + }).toThrow('Cannot proceed with contract evaluation.'); }); + it('should properly set foreign evaluation options - unsafeClient - allow', async () => { const contract = warp.contract(null).setEvaluationOptions({ unsafeClient: 'allow' diff --git a/src/contract/EvaluationOptionsEvaluator.ts b/src/contract/EvaluationOptionsEvaluator.ts index 0668bef..21a6165 100644 --- a/src/contract/EvaluationOptionsEvaluator.ts +++ b/src/contract/EvaluationOptionsEvaluator.ts @@ -124,13 +124,35 @@ export class EvaluationOptionsEvaluator { if (manifestOptions) { const errors = []; for (const k in manifestOptions) { - if (this.notConflictingEvaluationOptions.includes(k as keyof EvaluationOptions)) { + const optionKey = k as keyof EvaluationOptions; + const manifestValue = manifestOptions[k]; + const userValue = userSetOptions[k]; + if (this.notConflictingEvaluationOptions.includes(optionKey)) { continue; } - if (userSetOptions[k] !== manifestOptions[k]) { - errors.push( - `Option {${k}} differs. EvaluationOptions: [${userSetOptions[k]}], manifest: [${manifestOptions[k]}]. Use contract.setEvaluationOptions({${k}: ${manifestOptions[k]}}) to evaluate contract state.` - ); + // https://github.com/warp-contracts/warp/issues/425#issuecomment-1591212639 + if (optionKey === 'internalWrites') { + if (userValue === false && manifestValue === true) { + throw new Error( + 'Cannot proceed with contract evaluation. User is blocking internal writes, while contract requires them.' + ); + } + } else if (optionKey === 'unsafeClient') { + // 'allow' | 'skip' | 'throw' + if ( + (userValue === 'throw' && manifestValue !== 'throw') || + (userValue === 'skip' && manifestValue === 'allow') + ) { + throw new Error( + `Cannot proceed with contract evaluation. User requires to ${userValue} on any unsafeClient usage, while contract uses ${manifestValue} option.` + ); + } + } else { + if (userSetOptions[k] !== manifestOptions[k]) { + errors.push( + `Option {${k}} differs. EvaluationOptions: [${userSetOptions[k]}], manifest: [${manifestOptions[k]}]. Use contract.setEvaluationOptions({${k}: ${manifestOptions[k]}}) to evaluate contract state.` + ); + } } } if (errors.length) {