feat: maxCallDepth implemented

This commit is contained in:
ppedziwiatr
2021-10-27 16:46:04 +02:00
committed by Piotr Pędziwiatr
parent aeb8dc0be6
commit d63ab29129
7 changed files with 183 additions and 78 deletions

View File

@@ -119,7 +119,7 @@ describe('Testing internal writes', () => {
contractA = smartweave contractA = smartweave
.contract(contractATxId) .contract(contractATxId)
.setEvaluationOptions({ .setEvaluationOptions({
internalWrites: true internalWrites: true,
}) })
.connect(wallet); .connect(wallet);
contractB = smartweave contractB = smartweave
@@ -271,6 +271,56 @@ describe('Testing internal writes', () => {
}); });
}); });
describe('with different maxDepths', () => {
beforeEach(async () => {
await deployContracts();
});
it('should properly evaluate contractC state for maxDepth = 3', async () => {
contractC.setEvaluationOptions({
maxCallDepth: 3
});
await contractB.writeInteraction({ function: 'add' });
await contractB.writeInteraction({ function: 'add' });
await contractC.writeInteraction({ function: 'add' });
await mine();
await contractA.writeInteraction({
function: 'writeInDepth',
contractId1: contractBTxId,
contractId2: contractCTxId,
amount: 10
});
await mine();
expect((await contractC.readState()).state.counter).toEqual(231);
expect((await contractC.readState()).state.counter).toEqual(231);
});
it('should throw when evaluating ContractC state for maxDepth = 2', async () => {
contractC.setEvaluationOptions({
maxCallDepth: 2,
ignoreExceptions: false
});
await contractB.writeInteraction({ function: 'add' });
await contractB.writeInteraction({ function: 'add' });
await contractC.writeInteraction({ function: 'add' });
await mine();
await contractA.writeInteraction({
function: 'writeInDepth',
contractId1: contractBTxId,
contractId2: contractCTxId,
amount: 10
});
await mine();
await expect(contractC.readState()).rejects.toThrow(/(.)*Error: Max call depth(.*)/);
});
});
async function mine() { async function mine() {
await arweave.api.get('mine'); await arweave.api.get('mine');
} }

View File

@@ -115,4 +115,10 @@ export interface Contract<State = unknown> {
getNetworkInfo(): NetworkInfoInterface; getNetworkInfo(): NetworkInfoInterface;
getRootBlockHeight(): number | null; getRootBlockHeight(): number | null;
parent(): Contract | null;
callDepth(): number;
evaluationOptions(): EvaluationOptions;
} }

View File

@@ -37,19 +37,21 @@ import { NetworkInfoInterface } from 'arweave/node/network';
export class HandlerBasedContract<State> implements Contract<State> { export class HandlerBasedContract<State> implements Contract<State> {
private readonly logger = LoggerFactory.INST.create('HandlerBasedContract'); private readonly logger = LoggerFactory.INST.create('HandlerBasedContract');
private callStack: ContractCallStack; private _callStack: ContractCallStack;
private evaluationOptions: EvaluationOptions = new DefaultEvaluationOptions(); private _evaluationOptions: EvaluationOptions = new DefaultEvaluationOptions();
/** /**
* current Arweave networkInfo that will be used for all operations of the SmartWeave protocol. * current Arweave networkInfo that will be used for all operations of the SmartWeave protocol.
* Only the 'root' contract call should read this data from Arweave - all the inner calls ("child" contracts) * Only the 'root' contract call should read this data from Arweave - all the inner calls ("child" contracts)
* should reuse this data from the parent ("calling") contract. * should reuse this data from the parent ("calling") contract.
*/ */
private networkInfo?: NetworkInfoInterface = null; private _networkInfo?: NetworkInfoInterface = null;
private rootBlockHeight: number = null; private _rootBlockHeight: number = null;
private readonly innerWritesEvaluator = new InnerWritesEvaluator(); private readonly _innerWritesEvaluator = new InnerWritesEvaluator();
private readonly _callDepth: number;
/** /**
* wallet connected to this contract * wallet connected to this contract
@@ -57,39 +59,56 @@ export class HandlerBasedContract<State> implements Contract<State> {
protected wallet?: ArWallet; protected wallet?: ArWallet;
constructor( constructor(
readonly contractTxId: string, private readonly _contractTxId: string,
protected readonly smartweave: SmartWeave, protected readonly smartweave: SmartWeave,
private readonly callingContract: Contract = null, private readonly _parentContract: Contract = null,
private readonly callingInteraction: GQLNodeInterface = null private readonly _callingInteraction: GQLNodeInterface = null
) { ) {
this.waitForConfirmation = this.waitForConfirmation.bind(this); this.waitForConfirmation = this.waitForConfirmation.bind(this);
if (callingContract != null) { if (_parentContract != null) {
this.networkInfo = callingContract.getNetworkInfo(); this._networkInfo = _parentContract.getNetworkInfo();
this.rootBlockHeight = callingContract.getRootBlockHeight(); this._rootBlockHeight = _parentContract.getRootBlockHeight();
this._evaluationOptions = _parentContract.evaluationOptions();
this._callDepth = _parentContract.callDepth() + 1;
const interaction: InteractionCall = _parentContract.getCallStack().getInteraction(_callingInteraction.id);
console.log('Call depth', {
callDepth: this._callDepth,
max: this._evaluationOptions.maxCallDepth,
options: this._evaluationOptions
});
if (this._callDepth > this._evaluationOptions.maxCallDepth) {
throw Error(
`Max call depth of ${this._evaluationOptions.maxCallDepth} has been exceeded for interaction ${JSON.stringify(
interaction.interactionInput
)}`
);
}
// sanity-check... // sanity-check...
if (this.networkInfo == null) { if (this._networkInfo == null) {
throw Error('Calling contract should have the network info already set!'); throw Error('Calling contract should have the network info already set!');
} }
this.logger.debug('Calling interaction id', callingInteraction.id); this.logger.debug('Calling interaction id', _callingInteraction.id);
const interaction: InteractionCall = callingContract.getCallStack().getInteraction(callingInteraction.id); const callStack = new ContractCallStack(_contractTxId, this._callDepth);
const callStack = new ContractCallStack(contractTxId); interaction.interactionInput.foreignContractCalls.set(_contractTxId, callStack);
interaction.interactionInput.foreignContractCalls.set(contractTxId, callStack); this._callStack = callStack;
this.callStack = callStack;
} else { } else {
this.callStack = new ContractCallStack(contractTxId); this._callDepth = 0;
this._callStack = new ContractCallStack(_contractTxId, 0);
} }
} }
async readState(blockHeight?: number, currentTx?: CurrentTx[]): Promise<EvalStateResult<State>> { async readState(blockHeight?: number, currentTx?: CurrentTx[]): Promise<EvalStateResult<State>> {
this.logger.info('Read state for', { this.logger.info('Read state for', {
contractTxId: this.contractTxId, contractTxId: this._contractTxId,
currentTx currentTx
}); });
this.maybeResetRootContract(blockHeight); this.maybeResetRootContract(blockHeight);
const { stateEvaluator } = this.smartweave; const { stateEvaluator } = this.smartweave;
const benchmark = Benchmark.measure(); const benchmark = Benchmark.measure();
const executionContext = await this.createExecutionContext(this.contractTxId, blockHeight); const executionContext = await this.createExecutionContext(this._contractTxId, blockHeight);
this.logger.info('Execution Context', { this.logger.info('Execution Context', {
blockHeight: executionContext.blockHeight, blockHeight: executionContext.blockHeight,
srcTxId: executionContext.contractDefinition?.srcTxId, srcTxId: executionContext.contractDefinition?.srcTxId,
@@ -109,7 +128,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
tags: Tags = [], tags: Tags = [],
transfer: ArTransfer = emptyTransfer transfer: ArTransfer = emptyTransfer
): Promise<InteractionResult<State, View>> { ): Promise<InteractionResult<State, View>> {
this.logger.info('View state for', this.contractTxId); this.logger.info('View state for', this._contractTxId);
return await this.callContract<Input, View>(input, blockHeight, tags, transfer); return await this.callContract<Input, View>(input, blockHeight, tags, transfer);
} }
@@ -117,12 +136,12 @@ export class HandlerBasedContract<State> implements Contract<State> {
input: Input, input: Input,
interactionTx: GQLNodeInterface interactionTx: GQLNodeInterface
): Promise<InteractionResult<State, View>> { ): Promise<InteractionResult<State, View>> {
this.logger.info(`View state for ${this.contractTxId}`, interactionTx); this.logger.info(`View state for ${this._contractTxId}`, interactionTx);
return await this.callContractForTx<Input, View>(input, interactionTx); return await this.callContractForTx<Input, View>(input, interactionTx);
} }
async dryWrite<Input>(input: Input, tags?: Tags, transfer?: ArTransfer): Promise<InteractionResult<State, unknown>> { async dryWrite<Input>(input: Input, tags?: Tags, transfer?: ArTransfer): Promise<InteractionResult<State, unknown>> {
this.logger.info('Dry-write for', this.contractTxId); this.logger.info('Dry-write for', this._contractTxId);
return await this.callContract<Input>(input, undefined, tags, transfer); return await this.callContract<Input>(input, undefined, tags, transfer);
} }
@@ -131,8 +150,8 @@ export class HandlerBasedContract<State> implements Contract<State> {
transaction: GQLNodeInterface, transaction: GQLNodeInterface,
currentTx?: CurrentTx[] currentTx?: CurrentTx[]
): Promise<InteractionResult<State, unknown>> { ): Promise<InteractionResult<State, unknown>> {
this.logger.info(`Dry-write from transaction ${transaction.id} for ${this.contractTxId}`); this.logger.info(`Dry-write from transaction ${transaction.id} for ${this._contractTxId}`);
return await this.callContractForTx<Input>(input, transaction, true, currentTx || []); return await this.callContractForTx<Input>(input, transaction, currentTx || []);
} }
async writeInteraction<Input>( async writeInteraction<Input>(
@@ -146,10 +165,10 @@ export class HandlerBasedContract<State> implements Contract<State> {
} }
const { arweave } = this.smartweave; const { arweave } = this.smartweave;
if (this.evaluationOptions.internalWrites) { if (this._evaluationOptions.internalWrites) {
await this.callContract(input, undefined, tags, transfer); await this.callContract(input, undefined, tags, transfer);
const callStack: ContractCallStack = this.getCallStack(); const callStack: ContractCallStack = this.getCallStack();
const innerWrites = this.innerWritesEvaluator.eval(callStack); const innerWrites = this._innerWritesEvaluator.eval(callStack);
this.logger.debug('Input', input); this.logger.debug('Input', input);
this.logger.debug('Callstack', callStack.print()); this.logger.debug('Callstack', callStack.print());
@@ -166,7 +185,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
const interactionTx = await createTx( const interactionTx = await createTx(
this.smartweave.arweave, this.smartweave.arweave,
this.wallet, this.wallet,
this.contractTxId, this._contractTxId,
input, input,
tags, tags,
transfer.target, transfer.target,
@@ -180,7 +199,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
return null; return null;
} }
if (this.evaluationOptions.waitForConfirmation) { if (this._evaluationOptions.waitForConfirmation) {
this.logger.info('Waiting for confirmation of', interactionTx.id); this.logger.info('Waiting for confirmation of', interactionTx.id);
const benchmark = Benchmark.measure(); const benchmark = Benchmark.measure();
await this.waitForConfirmation(interactionTx.id); await this.waitForConfirmation(interactionTx.id);
@@ -190,15 +209,15 @@ export class HandlerBasedContract<State> implements Contract<State> {
} }
txId(): string { txId(): string {
return this.contractTxId; return this._contractTxId;
} }
getCallStack(): ContractCallStack { getCallStack(): ContractCallStack {
return this.callStack; return this._callStack;
} }
getNetworkInfo(): NetworkInfoInterface { getNetworkInfo(): NetworkInfoInterface {
return this.networkInfo; return this._networkInfo;
} }
connect(wallet: ArWallet): Contract<State> { connect(wallet: ArWallet): Contract<State> {
@@ -207,15 +226,15 @@ export class HandlerBasedContract<State> implements Contract<State> {
} }
setEvaluationOptions(options: Partial<EvaluationOptions>): Contract<State> { setEvaluationOptions(options: Partial<EvaluationOptions>): Contract<State> {
this.evaluationOptions = { this._evaluationOptions = {
...this.evaluationOptions, ...this._evaluationOptions,
...options ...options
}; };
return this; return this;
} }
getRootBlockHeight(): number { getRootBlockHeight(): number {
return this.rootBlockHeight; return this._rootBlockHeight;
} }
private async waitForConfirmation(transactionId: string): Promise<TransactionStatusResponse> { private async waitForConfirmation(transactionId: string): Promise<TransactionStatusResponse> {
@@ -245,10 +264,10 @@ export class HandlerBasedContract<State> implements Contract<State> {
const benchmark = Benchmark.measure(); const benchmark = Benchmark.measure();
// if this is a "root" call (ie. original call from SmartWeave's client) // if this is a "root" call (ie. original call from SmartWeave's client)
if (this.callingContract == null) { if (this._parentContract == null) {
this.logger.debug('Reading network info for root call'); this.logger.debug('Reading network info for root call');
currentNetworkInfo = await arweave.network.getInfo(); currentNetworkInfo = await arweave.network.getInfo();
this.networkInfo = currentNetworkInfo; this._networkInfo = currentNetworkInfo;
} else { } else {
// if that's a call from within contract's source code // if that's a call from within contract's source code
this.logger.debug('Reusing network info from the calling contract'); this.logger.debug('Reusing network info from the calling contract');
@@ -258,7 +277,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
// call to contract (from contract's source code) was loading network info independently // call to contract (from contract's source code) was loading network info independently
// if the contract was evaluating for many minutes/hours, this could effectively lead to reading // if the contract was evaluating for many minutes/hours, this could effectively lead to reading
// state on different block heights... // state on different block heights...
currentNetworkInfo = (this.callingContract as HandlerBasedContract<State>).networkInfo; currentNetworkInfo = (this._parentContract as HandlerBasedContract<State>)._networkInfo;
} }
if (blockHeight == null) { if (blockHeight == null) {
@@ -292,8 +311,8 @@ export class HandlerBasedContract<State> implements Contract<State> {
interactionsLoader.load( interactionsLoader.load(
contractTxId, contractTxId,
cachedBlockHeight + 1, cachedBlockHeight + 1,
this.rootBlockHeight || this.networkInfo.height, this._rootBlockHeight || this._networkInfo.height,
this.evaluationOptions this._evaluationOptions
) )
]); ]);
this.logger.debug('contract and interactions load', benchmark.elapsed()); this.logger.debug('contract and interactions load', benchmark.elapsed());
@@ -315,7 +334,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
handler, handler,
smartweave: this.smartweave, smartweave: this.smartweave,
contract: this, contract: this,
evaluationOptions: this.evaluationOptions, evaluationOptions: this._evaluationOptions,
currentNetworkInfo, currentNetworkInfo,
cachedState cachedState
}; };
@@ -345,7 +364,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
if (cachedBlockHeight != blockHeight) { if (cachedBlockHeight != blockHeight) {
[contractDefinition, interactions] = await Promise.all([ [contractDefinition, interactions] = await Promise.all([
definitionLoader.load<State>(contractTxId), definitionLoader.load<State>(contractTxId),
await interactionsLoader.load(contractTxId, 0, blockHeight, this.evaluationOptions) await interactionsLoader.load(contractTxId, 0, blockHeight, this._evaluationOptions)
]); ]);
sortedInteractions = await interactionsSorter.sort(interactions); sortedInteractions = await interactionsSorter.sort(interactions);
} else { } else {
@@ -364,18 +383,18 @@ export class HandlerBasedContract<State> implements Contract<State> {
handler, handler,
smartweave: this.smartweave, smartweave: this.smartweave,
contract: this, contract: this,
evaluationOptions: this.evaluationOptions, evaluationOptions: this._evaluationOptions,
caller, caller,
cachedState cachedState
}; };
} }
private maybeResetRootContract(blockHeight?: number) { private maybeResetRootContract(blockHeight?: number) {
if (this.callingContract == null) { if (this._parentContract == null) {
this.logger.debug('Clearing network info and call stack for the root contract'); this.logger.debug('Clearing network info and call stack for the root contract');
this.networkInfo = null; this._networkInfo = null;
this.callStack = new ContractCallStack(this.txId()); this._callStack = new ContractCallStack(this.txId(), 0);
this.rootBlockHeight = blockHeight; this._rootBlockHeight = blockHeight;
} }
} }
@@ -392,7 +411,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
} }
const { arweave, stateEvaluator } = this.smartweave; const { arweave, stateEvaluator } = this.smartweave;
// create execution context // create execution context
let executionContext = await this.createExecutionContext(this.contractTxId, blockHeight, true); let executionContext = await this.createExecutionContext(this._contractTxId, blockHeight, true);
// add block data to execution context // add block data to execution context
if (!executionContext.currentBlockData) { if (!executionContext.currentBlockData) {
@@ -427,7 +446,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
const tx = await createTx( const tx = await createTx(
arweave, arweave,
this.wallet, this.wallet,
this.contractTxId, this._contractTxId,
input, input,
tags, tags,
transfer.target, transfer.target,
@@ -458,22 +477,21 @@ export class HandlerBasedContract<State> implements Contract<State> {
private async callContractForTx<Input, View = unknown>( private async callContractForTx<Input, View = unknown>(
input: Input, input: Input,
interactionTx: GQLNodeInterface, interactionTx: GQLNodeInterface,
dryWrite = false,
currentTx?: CurrentTx[] currentTx?: CurrentTx[]
): Promise<InteractionResult<State, View>> { ): Promise<InteractionResult<State, View>> {
this.maybeResetRootContract(); this.maybeResetRootContract();
const executionContext = await this.createExecutionContextFromTx(this.contractTxId, interactionTx); const executionContext = await this.createExecutionContextFromTx(this._contractTxId, interactionTx);
const evalStateResult = await this.smartweave.stateEvaluator.eval<State>(executionContext, currentTx); const evalStateResult = await this.smartweave.stateEvaluator.eval<State>(executionContext, currentTx);
this.logger.debug('callContractForTx - evalStateResult', { this.logger.debug('callContractForTx - evalStateResult', {
result: evalStateResult.state, result: evalStateResult.state,
txId: this.contractTxId txId: this._contractTxId
}); });
const interaction: ContractInteraction<Input> = { const interaction: ContractInteraction<Input> = {
input, input,
caller: this.callingContract.txId() //executionContext.caller caller: this._parentContract.txId() //executionContext.caller
}; };
const interactionData: InteractionData<Input> = { const interactionData: InteractionData<Input> = {
@@ -482,16 +500,15 @@ export class HandlerBasedContract<State> implements Contract<State> {
currentTx currentTx
}; };
return await this.evalInteraction(interactionData, executionContext, evalStateResult, dryWrite); return await this.evalInteraction(interactionData, executionContext, evalStateResult);
} }
private async evalInteraction<Input, View = unknown>( private async evalInteraction<Input, View = unknown>(
interactionData: InteractionData<Input>, interactionData: InteractionData<Input>,
executionContext: ExecutionContext<State, HandlerApi<State>>, executionContext: ExecutionContext<State, HandlerApi<State>>,
evalStateResult: EvalStateResult<State>, evalStateResult: EvalStateResult<State>
dryWrite = false
) { ) {
const interactionCall: InteractionCall = this.getCallStack().addInteractionData(interactionData, dryWrite); const interactionCall: InteractionCall = this.getCallStack().addInteractionData(interactionData);
const benchmark = Benchmark.measure(); const benchmark = Benchmark.measure();
const result = await executionContext.handler.handle<Input, View>( const result = await executionContext.handler.handle<Input, View>(
@@ -503,7 +520,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
interactionCall.update({ interactionCall.update({
cacheHit: false, cacheHit: false,
intermediaryCacheHit: false, intermediaryCacheHit: false,
outputState: this.evaluationOptions.stackTrace.saveState ? result.state : undefined, outputState: this._evaluationOptions.stackTrace.saveState ? result.state : undefined,
executionTime: benchmark.elapsed(true) as number, executionTime: benchmark.elapsed(true) as number,
valid: result.type === 'ok', valid: result.type === 'ok',
errorMessage: result.errorMessage errorMessage: result.errorMessage
@@ -511,4 +528,16 @@ export class HandlerBasedContract<State> implements Contract<State> {
return result; return result;
} }
parent(): Contract | null {
return this._parentContract;
}
callDepth(): number {
return this._callDepth;
}
evaluationOptions(): EvaluationOptions {
return this._evaluationOptions;
}
} }

View File

@@ -3,9 +3,9 @@ import { InteractionData, mapReplacer } from '@smartweave';
export class ContractCallStack { export class ContractCallStack {
readonly interactions: Map<string, InteractionCall> = new Map(); readonly interactions: Map<string, InteractionCall> = new Map();
constructor(public readonly contractTxId: string, public readonly label: string = '') {} constructor(public readonly contractTxId: string, public readonly depth: number, public readonly label: string = '') {}
addInteractionData(interactionData: InteractionData<any>, dryWrite = false): InteractionCall { addInteractionData(interactionData: InteractionData<any>): InteractionCall {
const { interaction, interactionTx } = interactionData; const { interaction, interactionTx } = interactionData;
const interactionCall = InteractionCall.create( const interactionCall = InteractionCall.create(

View File

@@ -72,11 +72,14 @@ export class DefaultEvaluationOptions implements EvaluationOptions {
updateCacheForEachInteraction = true; updateCacheForEachInteraction = true;
internalWrites = false;
maxCallDepth = 7; // your lucky number...
stackTrace = { stackTrace = {
saveState: false saveState: false
}; };
internalWrites: false;
} }
// an interface for the contract EvaluationOptions - can be used to change the behaviour of some of the features. // an interface for the contract EvaluationOptions - can be used to change the behaviour of some of the features.
@@ -96,13 +99,22 @@ export interface EvaluationOptions {
// and caches it maybe more suitable to cache only after state has been fully evaluated) // and caches it maybe more suitable to cache only after state has been fully evaluated)
updateCacheForEachInteraction: boolean; updateCacheForEachInteraction: boolean;
// a new, experimental enhancement of the protocol that allows for interactWrites from
// smart contract's source code.
internalWrites: boolean;
// maximum call depth between contracts
// eg. ContractA calls ContractB,
// then ContractB calls ContractC,
// then ContractC calls ContractD
// - call depth = 3
// this is added as a protection from "stackoverflow" errors
maxCallDepth: number;
// a set of options that control the behaviour of the stack trace generator // a set of options that control the behaviour of the stack trace generator
stackTrace: { stackTrace: {
// whether output state should be saved for each interaction in the stack trace (may result in huuuuge json files!) // whether output state should be saved for each interaction in the stack trace (may result in huuuuge json files!)
saveState: boolean; saveState: boolean;
}; };
// a new, experimental enhancement of the protocol that allows for interactWrites from
// smart contract's source code.
internalWrites: boolean;
} }

View File

@@ -106,9 +106,11 @@ export class ContractHandlerApi<State> implements HandlerApi<State> {
input input
}); });
const calleeContract = executionContext.smartweave const calleeContract = executionContext.smartweave.contract(
.contract(contractTxId, executionContext.contract, this.swGlobal._activeTx) contractTxId,
.setEvaluationOptions(executionContext.evaluationOptions); executionContext.contract,
this.swGlobal._activeTx
);
const result = await calleeContract.dryWriteFromTx<Input>(input, this.swGlobal._activeTx, [ const result = await calleeContract.dryWriteFromTx<Input>(input, this.swGlobal._activeTx, [
...(currentTx || []), ...(currentTx || []),
@@ -139,9 +141,11 @@ export class ContractHandlerApi<State> implements HandlerApi<State> {
to: contractTxId, to: contractTxId,
input input
}); });
const childContract = executionContext.smartweave const childContract = executionContext.smartweave.contract(
.contract(contractTxId, executionContext.contract, this.swGlobal._activeTx) contractTxId,
.setEvaluationOptions(executionContext.evaluationOptions); executionContext.contract,
this.swGlobal._activeTx
);
return await childContract.viewStateForTx(input, this.swGlobal._activeTx); return await childContract.viewStateForTx(input, this.swGlobal._activeTx);
}; };
@@ -167,9 +171,11 @@ export class ContractHandlerApi<State> implements HandlerApi<State> {
}); });
const { stateEvaluator } = executionContext.smartweave; const { stateEvaluator } = executionContext.smartweave;
const childContract = executionContext.smartweave const childContract = executionContext.smartweave.contract(
.contract(contractTxId, executionContext.contract, interactionTx) contractTxId,
.setEvaluationOptions(executionContext.evaluationOptions); executionContext.contract,
interactionTx
);
await stateEvaluator.onContractCall(interactionTx, executionContext, currentResult); await stateEvaluator.onContractCall(interactionTx, executionContext, currentResult);

View File

@@ -91,9 +91,11 @@ export class DefaultStateEvaluator implements StateEvaluator {
.getCallStack() .getCallStack()
.addInteractionData({ interaction: null, interactionTx, currentTx }); .addInteractionData({ interaction: null, interactionTx, currentTx });
const writingContract = executionContext.smartweave const writingContract = executionContext.smartweave.contract(
.contract(writingContractTxId, executionContext.contract, interactionTx) writingContractTxId,
.setEvaluationOptions(executionContext.evaluationOptions); executionContext.contract,
interactionTx
);
this.logger.debug('Reading state of the calling contract', interactionTx.block.height); this.logger.debug('Reading state of the calling contract', interactionTx.block.height);
await writingContract.readState(interactionTx.block.height, [ await writingContract.readState(interactionTx.block.height, [
@@ -162,7 +164,7 @@ export class DefaultStateEvaluator implements StateEvaluator {
this.logResult<State>(result, interactionTx, executionContext); this.logResult<State>(result, interactionTx, executionContext);
if (result.type === 'exception' && ignoreExceptions !== true) { if (result.type === 'exception' && ignoreExceptions !== true) {
throw new Error(`Exception while processing ${JSON.stringify(interaction)}:\n${result.result}`); throw new Error(`Exception while processing ${JSON.stringify(interaction)}:\n${result.errorMessage}`);
} }
validity[interactionTx.id] = result.type === 'ok'; validity[interactionTx.id] = result.type === 'ok';