From 14a786bd073ccce255f6b87888916169e69d0e99 Mon Sep 17 00:00:00 2001 From: koonopek Date: Mon, 2 Jan 2023 11:35:13 +0100 Subject: [PATCH] fix: reading owner of tx in strict mode (#306) --- src/__tests__/integration/basic/pst.test.ts | 18 +++++++ src/contract/Contract.ts | 4 +- src/contract/HandlerBasedContract.ts | 35 ++++++------- src/contract/Signature.ts | 50 +++++++++++-------- src/contract/deploy/CreateContract.ts | 4 +- src/contract/deploy/Source.ts | 4 +- .../deploy/impl/DefaultCreateContract.ts | 4 +- src/contract/deploy/impl/SourceImpl.ts | 8 +-- src/core/Warp.ts | 4 +- 9 files changed, 78 insertions(+), 53 deletions(-) diff --git a/src/__tests__/integration/basic/pst.test.ts b/src/__tests__/integration/basic/pst.test.ts index a8a46b6..e758011 100644 --- a/src/__tests__/integration/basic/pst.test.ts +++ b/src/__tests__/integration/basic/pst.test.ts @@ -155,4 +155,22 @@ describe('Testing the Profit Sharing Token', () => { expect(result.state.balances['uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M']).toEqual(10000000 + 555 + 333); expect(result.state.balances[overwrittenCaller]).toEqual(1000 - 333); }); + + describe('when in strict mode', () => { + it('should properly extract owner from signature, using arweave wallet', async () => { + const startBalance = (await pst.currentBalance(walletAddress)).balance; + + await pst.writeInteraction( + { + function: 'transfer', + target: 'uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M', + qty: 100 + }, + { strict: true } + ) + + expect((await pst.currentBalance(walletAddress)).balance).toEqual(startBalance - 100); + }); + + }) }); diff --git a/src/contract/Contract.ts b/src/contract/Contract.ts index 96dad7b..b44e006 100644 --- a/src/contract/Contract.ts +++ b/src/contract/Contract.ts @@ -4,7 +4,7 @@ import { InteractionResult } from '../core/modules/impl/HandlerExecutorFactory'; import { EvaluationOptions, EvalStateResult } from '../core/modules/StateEvaluator'; import { GQLNodeInterface } from '../legacy/gqlResult'; import { ArTransfer, Tags, ArWallet } from './deploy/CreateContract'; -import { SignatureType } from './Signature'; +import { CustomSignature } from './Signature'; import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator'; export type CurrentTx = { interactionTxId: string; contractTxId: string }; @@ -76,7 +76,7 @@ export interface Contract { * * @param signer - either {@link ArWallet} that will be connected to this contract or custom {@link SigningFunction} */ - connect(signature: ArWallet | SignatureType): Contract; + connect(signature: ArWallet | CustomSignature): Contract; /** * Allows to set ({@link EvaluationOptions}) diff --git a/src/contract/HandlerBasedContract.ts b/src/contract/HandlerBasedContract.ts index e626128..0a688dd 100644 --- a/src/contract/HandlerBasedContract.ts +++ b/src/contract/HandlerBasedContract.ts @@ -33,7 +33,7 @@ import { import { ArTransfer, ArWallet, emptyTransfer, Tags } from './deploy/CreateContract'; import { InnerWritesEvaluator } from './InnerWritesEvaluator'; import { generateMockVrf } from '../utils/vrf'; -import { Signature, SignatureType } from './Signature'; +import { Signature, CustomSignature } from './Signature'; import { ContractDefinition } from '../core/ContractDefinition'; import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator'; import { WarpFetchWrapper } from '../core/WarpFetchWrapper'; @@ -366,13 +366,6 @@ export class HandlerBasedContract implements Contract { }); this.logger.debug('Tags with inner calls', tags); - } else { - if (strict) { - const handlerResult = await this.callContract(input, undefined, undefined, tags, transfer, strict, vrf); - if (handlerResult.type !== 'ok') { - throw Error(`Cannot create interaction: ${handlerResult.errorMessage}`); - } - } } if (vrf) { @@ -394,6 +387,14 @@ export class HandlerBasedContract implements Contract { this.warp.environment === 'testnet', reward ); + + if (!this._evaluationOptions.internalWrites && strict) { + const handlerResult = await this.callContract(input, interactionTx.owner, undefined, tags, transfer, strict, vrf); + if (handlerResult.type !== 'ok') { + throw Error(`Cannot create interaction: ${handlerResult.errorMessage}`); + } + } + return interactionTx; } @@ -405,7 +406,7 @@ export class HandlerBasedContract implements Contract { return this._callStack; } - connect(signature: ArWallet | SignatureType): Contract { + connect(signature: ArWallet | CustomSignature): Contract { this.signature = new Signature(this.warp, signature); return this; } @@ -467,14 +468,14 @@ export class HandlerBasedContract implements Contract { interactions ? Promise.resolve(interactions) : await interactionsLoader.load( - contractTxId, - cachedState?.sortKey, - // (1) we want to eagerly load dependant contract interactions and put them - // in the interactions' loader cache - // see: https://github.com/warp-contracts/warp/issues/198 - this.getToSortKey(upToSortKey), - this._evaluationOptions - ) + contractTxId, + cachedState?.sortKey, + // (1) we want to eagerly load dependant contract interactions and put them + // in the interactions' loader cache + // see: https://github.com/warp-contracts/warp/issues/198 + this.getToSortKey(upToSortKey), + this._evaluationOptions + ) ]); // (2) ...but we still need to return only interactions up to original "upToSortKey" if (cachedState?.sortKey) { diff --git a/src/contract/Signature.ts b/src/contract/Signature.ts index fc6bde6..9723668 100644 --- a/src/contract/Signature.ts +++ b/src/contract/Signature.ts @@ -2,34 +2,24 @@ import Transaction from 'arweave/node/lib/transaction'; import { Warp } from '../core/Warp'; import { ArWallet } from './deploy/CreateContract'; +export type SignatureType = 'arweave' | 'ethereum'; export type SigningFunction = (tx: Transaction) => Promise; -export type SignatureType = { signer: SigningFunction; type: 'arweave' | 'ethereum' }; +export type CustomSignature = { signer: SigningFunction; type: SignatureType }; export class Signature { - readonly signer: SigningFunction; - readonly type: 'arweave' | 'ethereum'; + signer: SigningFunction; + type: SignatureType; readonly warp: Warp; - constructor(warp: Warp, walletOrSignature: ArWallet | SignatureType) { + constructor(warp: Warp, walletOrSignature: ArWallet | CustomSignature) { this.warp = warp; - if (this.isSignatureType(walletOrSignature)) { - if ( - walletOrSignature.type !== 'arweave' && - (!(this.warp.environment == 'mainnet') || !(this.warp.interactionsLoader.type() == 'warp')) - ) { - throw new Error( - `Unable to use signing function of type: ${walletOrSignature.type} when not in mainnet environment or bundling is disabled.` - ); - } else { - this.signer = walletOrSignature.signer; - this.type = walletOrSignature.type; - } + if (this.isCustomSignature(walletOrSignature)) { + this.assertEnvForCustomSigner(walletOrSignature); + this.signer = walletOrSignature.signer; + this.type = walletOrSignature.type; } else { - this.signer = async (tx: Transaction) => { - await this.warp.arweave.transactions.sign(tx, walletOrSignature); - }; - this.type = 'arweave'; + this.assignDefaultSigner(walletOrSignature); } } @@ -39,7 +29,23 @@ export class Signature { } } - private isSignatureType(signature: ArWallet | SignatureType): signature is SignatureType { - return (signature as SignatureType).signer !== undefined; + private assignDefaultSigner(walletOrSignature) { + this.signer = async (tx: Transaction) => { + await this.warp.arweave.transactions.sign(tx, walletOrSignature); + }; + this.type = 'arweave'; + } + + private assertEnvForCustomSigner(walletOrSignature: CustomSignature) { + if (walletOrSignature.type !== 'arweave' && + (!(this.warp.environment == 'mainnet') || !(this.warp.interactionsLoader.type() == 'warp'))) { + throw new Error( + `Unable to use signing function of type: ${walletOrSignature.type} when not in mainnet environment or bundling is disabled.` + ); + } + } + + private isCustomSignature(signature: ArWallet | CustomSignature): signature is CustomSignature { + return (signature as CustomSignature).signer !== undefined; } } diff --git a/src/contract/deploy/CreateContract.ts b/src/contract/deploy/CreateContract.ts index eb56a03..5b8d972 100644 --- a/src/contract/deploy/CreateContract.ts +++ b/src/contract/deploy/CreateContract.ts @@ -1,5 +1,5 @@ import { JWKInterface } from 'arweave/node/lib/wallet'; -import { SignatureType } from '../../contract/Signature'; +import { CustomSignature } from '../../contract/Signature'; import { Source } from './Source'; import { EvaluationOptions } from '../../core/modules/StateEvaluator'; import { WarpPluginType } from '../../core/WarpPlugin'; @@ -29,7 +29,7 @@ export const BUNDLR_NODES = ['node1', 'node2'] as const; export type BundlrNodeType = typeof BUNDLR_NODES[number]; export interface CommonContractData { - wallet: ArWallet | SignatureType; + wallet: ArWallet | CustomSignature; initState: string; tags?: Tags; transfer?: ArTransfer; diff --git a/src/contract/deploy/Source.ts b/src/contract/deploy/Source.ts index 4fe3279..ec9b8ef 100644 --- a/src/contract/deploy/Source.ts +++ b/src/contract/deploy/Source.ts @@ -1,6 +1,6 @@ import { ArWallet } from './CreateContract'; import { SourceData } from './impl/SourceImpl'; -import { SignatureType } from '../../contract/Signature'; +import { CustomSignature } from '../../contract/Signature'; import Transaction from 'arweave/node/lib/transaction'; export interface Source { /** @@ -8,7 +8,7 @@ export interface Source { * @param sourceData - contract source data * @param wallet - either Arweave wallet or custom signature type */ - createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise; + createSourceTx(sourceData: SourceData, wallet: ArWallet | CustomSignature): Promise; /** * allows to save contract source diff --git a/src/contract/deploy/impl/DefaultCreateContract.ts b/src/contract/deploy/impl/DefaultCreateContract.ts index ef2a6a2..86ea8a5 100644 --- a/src/contract/deploy/impl/DefaultCreateContract.ts +++ b/src/contract/deploy/impl/DefaultCreateContract.ts @@ -1,8 +1,8 @@ /* eslint-disable */ import Arweave from 'arweave'; import Transaction from 'arweave/node/lib/transaction'; -import { Signature, SignatureType } from '../../../contract/Signature'; import { WarpFetchWrapper } from '../../../core/WarpFetchWrapper'; +import { Signature, CustomSignature } from '../../../contract/Signature'; import { SmartWeaveTags } from '../../../core/SmartWeaveTags'; import { Warp } from '../../../core/Warp'; import { WARP_GW_URL } from '../../../core/WarpFactory'; @@ -182,7 +182,7 @@ export class DefaultCreateContract implements CreateContract { } } - async createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise { + async createSourceTx(sourceData: SourceData, wallet: ArWallet | CustomSignature): Promise { return this.source.createSourceTx(sourceData, wallet); } diff --git a/src/contract/deploy/impl/SourceImpl.ts b/src/contract/deploy/impl/SourceImpl.ts index 4769833..3067a5d 100644 --- a/src/contract/deploy/impl/SourceImpl.ts +++ b/src/contract/deploy/impl/SourceImpl.ts @@ -9,7 +9,7 @@ import { LoggerFactory } from '../../../logging/LoggerFactory'; import { Source } from '../Source'; import { Buffer } from 'redstone-isomorphic'; import { Warp } from '../../../core/Warp'; -import { Signature, SignatureType } from '../../../contract/Signature'; +import { Signature, CustomSignature } from '../../../contract/Signature'; import Transaction from 'arweave/node/lib/transaction'; import { WARP_GW_URL } from '../../../core/WarpFactory'; import { TagsParser } from '../../../core/modules/impl/TagsParser'; @@ -32,9 +32,9 @@ export class SourceImpl implements Source { private readonly logger = LoggerFactory.INST.create('Source'); private signature: Signature; - constructor(private readonly warp: Warp) {} + constructor(private readonly warp: Warp) { } - async createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise { + async createSourceTx(sourceData: SourceData, wallet: ArWallet | CustomSignature): Promise { this.logger.debug('Creating new contract source'); const { src, wasmSrcCodeDir, wasmGlueCode } = sourceData; @@ -235,7 +235,7 @@ function dummyImports(moduleImports: WebAssembly.ModuleImportDescriptor[]) { if (!Object.prototype.hasOwnProperty.call(imports, moduleImport.module)) { imports[moduleImport.module] = {}; } - imports[moduleImport.module][moduleImport.name] = function () {}; + imports[moduleImport.module][moduleImport.name] = function () { }; }); return imports; diff --git a/src/core/Warp.ts b/src/core/Warp.ts index bd7e9ff..61569f8 100644 --- a/src/core/Warp.ts +++ b/src/core/Warp.ts @@ -28,7 +28,7 @@ import { } from './WarpPlugin'; import { SortKeyCache } from '../cache/SortKeyCache'; import { ContractDefinition, SrcCache } from './ContractDefinition'; -import { SignatureType } from '../contract/Signature'; +import { CustomSignature } from '../contract/Signature'; import { SourceData } from '../contract/deploy/impl/SourceImpl'; import Transaction from 'arweave/node/lib/transaction'; @@ -97,7 +97,7 @@ export class Warp { return await this.createContract.register(id, bundlrNode); } - async createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise { + async createSourceTx(sourceData: SourceData, wallet: ArWallet | CustomSignature): Promise { return await this.createContract.createSourceTx(sourceData, wallet); }