performance issue with FCP and LkfzZvdl_vfjRXZOPjnov18cGnnK3aDKj0qSQCgkCX8 contract #19
This commit is contained in:
committed by
Piotr Pędziwiatr
parent
8d8a09761c
commit
b627336a06
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -11,3 +11,5 @@ jobs:
|
|||||||
run: yarn test:unit
|
run: yarn test:unit
|
||||||
- name: Run integration tests
|
- name: Run integration tests
|
||||||
run: yarn test:integration
|
run: yarn test:integration
|
||||||
|
- name: Run regression tests
|
||||||
|
run: yarn test:regression
|
||||||
|
|||||||
1055
new_state_fix.json
Normal file
1055
new_state_fix.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,11 @@ describe.each(chunked)('.suite %#', (contracts: string[]) => {
|
|||||||
const result = await readContract(arweave, contractTxId);
|
const result = await readContract(arweave, contractTxId);
|
||||||
const resultString = JSON.stringify(result).trim();
|
const resultString = JSON.stringify(result).trim();
|
||||||
|
|
||||||
const result2 = await smartWeave.contract(contractTxId).readState();
|
const result2 = await smartWeave.contract(contractTxId)
|
||||||
|
.setEvaluationOptions({
|
||||||
|
fcpOptimization: true
|
||||||
|
})
|
||||||
|
.readState();
|
||||||
const result2String = JSON.stringify(result2.state).trim();
|
const result2String = JSON.stringify(result2.state).trim();
|
||||||
|
|
||||||
expect(result2String).toEqual(resultString);
|
expect(result2String).toEqual(resultString);
|
||||||
|
|||||||
@@ -108,5 +108,6 @@
|
|||||||
"Qa9SzAuwJR6xZp3UiKzokKEoRnt_utJKjFjTaSR85Xw",
|
"Qa9SzAuwJR6xZp3UiKzokKEoRnt_utJKjFjTaSR85Xw",
|
||||||
"38TR3D8BxlPTc89NOW67IkQQUPR8jDLaJNdYv-4wWfM",
|
"38TR3D8BxlPTc89NOW67IkQQUPR8jDLaJNdYv-4wWfM",
|
||||||
"yWDo0H85PVimIpHM86qEP8BzXHIvyIfQE7NeVgTbhxs",
|
"yWDo0H85PVimIpHM86qEP8BzXHIvyIfQE7NeVgTbhxs",
|
||||||
"OrO8n453N6bx921wtsEs-0OCImBLCItNU5oSbFKlFuU"
|
"OrO8n453N6bx921wtsEs-0OCImBLCItNU5oSbFKlFuU",
|
||||||
|
"-q2dbbzO7Gh0mh80Qh5dVTXfH9AC6XNPqawuNGtjzus"
|
||||||
]
|
]
|
||||||
|
|||||||
1
src/cache/impl/MemBlockHeightCache.ts
vendored
1
src/cache/impl/MemBlockHeightCache.ts
vendored
@@ -49,7 +49,6 @@ export class MemBlockHeightSwCache<V = any> implements BlockHeightSwCache<V> {
|
|||||||
if (!(await this.contains(cacheKey))) {
|
if (!(await this.contains(cacheKey))) {
|
||||||
this.storage[cacheKey] = new Map();
|
this.storage[cacheKey] = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.storage[cacheKey].set(blockHeight, deepCopy(value));
|
this.storage[cacheKey].set(blockHeight, deepCopy(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,10 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
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.debug('Contract src txId', executionContext.contractDefinition.srcTxId);
|
this.logger.info('Execution Context', {
|
||||||
|
blockHeight: executionContext.blockHeight,
|
||||||
|
srcTxId: executionContext.contractDefinition.srcTxId
|
||||||
|
});
|
||||||
this.logger.debug('context', benchmark.elapsed());
|
this.logger.debug('context', benchmark.elapsed());
|
||||||
benchmark.reset();
|
benchmark.reset();
|
||||||
const result = await stateEvaluator.eval(executionContext, currentTx || []);
|
const result = await stateEvaluator.eval(executionContext, currentTx || []);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ExecutionContext, GQLNodeInterface } from '@smartweave';
|
import { ExecutionContext, GQLEdgeInterface, GQLNodeInterface } from '@smartweave';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementors of this class are responsible for evaluating contract's state
|
* Implementors of this class are responsible for evaluating contract's state
|
||||||
@@ -30,6 +30,8 @@ export class DefaultEvaluationOptions implements EvaluationOptions {
|
|||||||
ignoreExceptions = true;
|
ignoreExceptions = true;
|
||||||
|
|
||||||
waitForConfirmation = false;
|
waitForConfirmation = false;
|
||||||
|
|
||||||
|
fcpOptimization = 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.
|
||||||
@@ -40,4 +42,7 @@ export interface EvaluationOptions {
|
|||||||
// allow to wait for confirmation of the interaction transaction - this way
|
// allow to wait for confirmation of the interaction transaction - this way
|
||||||
// you will know, when the new interaction is effectively available on the network
|
// you will know, when the new interaction is effectively available on the network
|
||||||
waitForConfirmation: boolean;
|
waitForConfirmation: boolean;
|
||||||
|
|
||||||
|
// experimental optimization for contracts that utilize the Foreign Call Protocol
|
||||||
|
fcpOptimization: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Benchmark,
|
Benchmark,
|
||||||
ContractInteraction,
|
ContractInteraction,
|
||||||
|
deepCopy,
|
||||||
EvalStateResult,
|
EvalStateResult,
|
||||||
ExecutionContext,
|
ExecutionContext,
|
||||||
ExecutionContextModifier,
|
ExecutionContextModifier,
|
||||||
@@ -10,6 +11,7 @@ import {
|
|||||||
HandlerApi,
|
HandlerApi,
|
||||||
InteractionResult,
|
InteractionResult,
|
||||||
LoggerFactory,
|
LoggerFactory,
|
||||||
|
MemCache,
|
||||||
StateEvaluator,
|
StateEvaluator,
|
||||||
TagsParser
|
TagsParser
|
||||||
} from '@smartweave';
|
} from '@smartweave';
|
||||||
@@ -19,6 +21,8 @@ import Arweave from 'arweave';
|
|||||||
export class DefaultStateEvaluator implements StateEvaluator {
|
export class DefaultStateEvaluator implements StateEvaluator {
|
||||||
private readonly logger = LoggerFactory.INST.create('DefaultStateEvaluator');
|
private readonly logger = LoggerFactory.INST.create('DefaultStateEvaluator');
|
||||||
|
|
||||||
|
private readonly transactionStateCache: MemCache<EvalStateResult<unknown>> = new MemCache();
|
||||||
|
|
||||||
private readonly tagsParser = new TagsParser();
|
private readonly tagsParser = new TagsParser();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -45,10 +49,10 @@ export class DefaultStateEvaluator implements StateEvaluator {
|
|||||||
currentTx: { interactionTxId: string; contractTxId: string }[]
|
currentTx: { interactionTxId: string; contractTxId: string }[]
|
||||||
): Promise<EvalStateResult<State>> {
|
): Promise<EvalStateResult<State>> {
|
||||||
const stateEvaluationBenchmark = Benchmark.measure();
|
const stateEvaluationBenchmark = Benchmark.measure();
|
||||||
const evaluationOptions = executionContext.evaluationOptions;
|
const { ignoreExceptions } = executionContext.evaluationOptions;
|
||||||
|
|
||||||
let currentState = baseState.state;
|
let currentState = baseState.state;
|
||||||
const validity = JSON.parse(JSON.stringify(baseState.validity));
|
let validity = deepCopy(baseState.validity);
|
||||||
|
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
`Evaluating state for ${executionContext.contractDefinition.txId} [${missingInteractions.length} non-cached of ${executionContext.sortedInteractions.length} all]`
|
`Evaluating state for ${executionContext.contractDefinition.txId} [${missingInteractions.length} non-cached of ${executionContext.sortedInteractions.length} all]`
|
||||||
@@ -64,67 +68,78 @@ export class DefaultStateEvaluator implements StateEvaluator {
|
|||||||
this.logger.trace('Init state', JSON.stringify(baseState.state));
|
this.logger.trace('Init state', JSON.stringify(baseState.state));
|
||||||
|
|
||||||
for (const missingInteraction of missingInteractions) {
|
for (const missingInteraction of missingInteractions) {
|
||||||
this.logger.debug(
|
|
||||||
`${missingInteraction.node.id}: ${missingInteractions.indexOf(missingInteraction) + 1}/${
|
|
||||||
missingInteractions.length
|
|
||||||
} [of all:${executionContext.sortedInteractions.length}]`
|
|
||||||
);
|
|
||||||
const singleInteractionBenchmark = Benchmark.measure();
|
|
||||||
const currentInteraction: GQLNodeInterface = missingInteraction.node;
|
const currentInteraction: GQLNodeInterface = missingInteraction.node;
|
||||||
|
|
||||||
const inputTag = this.tagsParser.getInputTag(missingInteraction, executionContext.contractDefinition.txId);
|
this.logger.debug(
|
||||||
if (!inputTag) {
|
`[${executionContext.contractDefinition.txId}][${missingInteraction.node.id}]: ${
|
||||||
this.logger.error(`Skipping tx with missing or invalid Input tag - ${currentInteraction.id}`);
|
missingInteractions.indexOf(missingInteraction) + 1
|
||||||
continue;
|
}/${missingInteractions.length} [of all:${executionContext.sortedInteractions.length}]`
|
||||||
}
|
|
||||||
|
|
||||||
const input = this.parseInput(inputTag);
|
|
||||||
if (!input) {
|
|
||||||
this.logger.error(`Skipping tx with missing or invalid Input tag - ${currentInteraction.id}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const interaction: ContractInteraction<unknown> = {
|
|
||||||
input,
|
|
||||||
caller: currentInteraction.owner.address
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await executionContext.handler.handle(
|
|
||||||
executionContext,
|
|
||||||
currentState,
|
|
||||||
interaction,
|
|
||||||
currentInteraction,
|
|
||||||
currentTx
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logResult<State>(result, currentInteraction, executionContext);
|
const state = await this.onNextIteration(currentInteraction, executionContext);
|
||||||
|
if (state !== null) {
|
||||||
|
this.logger.debug('Found in cache');
|
||||||
|
currentState = state.state;
|
||||||
|
validity = state.validity;
|
||||||
|
} else {
|
||||||
|
const singleInteractionBenchmark = Benchmark.measure();
|
||||||
|
|
||||||
if (result.type === 'exception' && evaluationOptions.ignoreExceptions !== true) {
|
const inputTag = this.tagsParser.getInputTag(missingInteraction, executionContext.contractDefinition.txId);
|
||||||
throw new Error(`Exception while processing ${JSON.stringify(interaction)}:\n${result.result}`);
|
if (!inputTag) {
|
||||||
}
|
this.logger.error(`Skipping tx with missing or invalid Input tag - ${currentInteraction.id}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
validity[currentInteraction.id] = result.type === 'ok';
|
const input = this.parseInput(inputTag);
|
||||||
|
if (!input) {
|
||||||
|
this.logger.error(`Skipping tx with missing or invalid Input tag - ${currentInteraction.id}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
currentState = result.state;
|
const interaction: ContractInteraction<unknown> = {
|
||||||
|
input,
|
||||||
|
caller: currentInteraction.owner.address
|
||||||
|
};
|
||||||
|
|
||||||
// I'm really NOT a fan of this "modify" feature, but I don't have idea how to better
|
const result = await executionContext.handler.handle(
|
||||||
// implement the "evolve" feature
|
executionContext,
|
||||||
for (const { modify } of this.executionContextModifiers) {
|
currentState,
|
||||||
|
interaction,
|
||||||
|
currentInteraction,
|
||||||
|
currentTx
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logResult<State>(result, currentInteraction, executionContext);
|
||||||
|
|
||||||
|
if (result.type === 'exception' && ignoreExceptions !== true) {
|
||||||
|
throw new Error(`Exception while processing ${JSON.stringify(interaction)}:\n${result.result}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.type === 'exception') {
|
||||||
|
this.logger.error('Credit:', (currentState as any).credit);
|
||||||
|
}
|
||||||
|
|
||||||
|
validity[currentInteraction.id] = result.type === 'ok';
|
||||||
// strangely - state is for some reason modified for some contracts (eg. YLVpmhSq5JmLltfg6R-5fL04rIRPrlSU22f6RQ6VyYE)
|
// strangely - state is for some reason modified for some contracts (eg. YLVpmhSq5JmLltfg6R-5fL04rIRPrlSU22f6RQ6VyYE)
|
||||||
// when calling any async (even simple timeout) function here...
|
// when calling any async (even simple timeout) function here...
|
||||||
// that's a dumb workaround for this issue
|
// that's (ie. deepCopy) a dumb workaround for this issue
|
||||||
// see https://github.com/ArweaveTeam/SmartWeave/pull/92 for more details
|
// see https://github.com/ArweaveTeam/SmartWeave/pull/92 for more details
|
||||||
const stateCopy = JSON.parse(JSON.stringify(currentState));
|
currentState = deepCopy(result.state);
|
||||||
executionContext = await modify<State>(currentState, executionContext);
|
|
||||||
currentState = stateCopy;
|
this.logger.debug('Interaction evaluation', singleInteractionBenchmark.elapsed());
|
||||||
}
|
}
|
||||||
this.logger.debug('Interaction evaluation', singleInteractionBenchmark.elapsed());
|
|
||||||
|
|
||||||
await this.onStateUpdate<State>(
|
await this.onStateUpdate<State>(
|
||||||
currentInteraction,
|
currentInteraction,
|
||||||
executionContext,
|
executionContext,
|
||||||
new EvalStateResult(currentState, validity)
|
new EvalStateResult(currentState, validity)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// I'm really NOT a fan of this "modify" feature, but I don't have idea how to better
|
||||||
|
// implement the "evolve" feature
|
||||||
|
for (const { modify } of this.executionContextModifiers) {
|
||||||
|
executionContext = await modify<State>(currentState, executionContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.logger.debug('State evaluation total:', stateEvaluationBenchmark.elapsed());
|
this.logger.debug('State evaluation total:', stateEvaluationBenchmark.elapsed());
|
||||||
return new EvalStateResult<State>(currentState, validity);
|
return new EvalStateResult<State>(currentState, validity);
|
||||||
@@ -136,10 +151,16 @@ export class DefaultStateEvaluator implements StateEvaluator {
|
|||||||
executionContext: ExecutionContext<State, HandlerApi<State>>
|
executionContext: ExecutionContext<State, HandlerApi<State>>
|
||||||
) {
|
) {
|
||||||
if (result.type === 'exception') {
|
if (result.type === 'exception') {
|
||||||
this.logger.error(`Executing of interaction: [${executionContext.contractDefinition.srcTxId} -> ${currentTx.id}] threw exception:`, `${result.errorMessage}`);
|
this.logger.error(
|
||||||
|
`Executing of interaction: [${executionContext.contractDefinition.srcTxId} -> ${currentTx.id}] threw exception:`,
|
||||||
|
`${result.errorMessage}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (result.type === 'error') {
|
if (result.type === 'error') {
|
||||||
this.logger.warn(`Executing of interaction: [${executionContext.contractDefinition.srcTxId} -> ${currentTx.id}] returned error:`, result.errorMessage);
|
this.logger.warn(
|
||||||
|
`Executing of interaction: [${executionContext.contractDefinition.srcTxId} -> ${currentTx.id}] returned error:`,
|
||||||
|
result.errorMessage
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +178,25 @@ export class DefaultStateEvaluator implements StateEvaluator {
|
|||||||
executionContext: ExecutionContext<State, unknown>,
|
executionContext: ExecutionContext<State, unknown>,
|
||||||
state: EvalStateResult<State>
|
state: EvalStateResult<State>
|
||||||
) {
|
) {
|
||||||
// noop
|
if (executionContext.evaluationOptions.fcpOptimization) {
|
||||||
|
this.transactionStateCache.put(
|
||||||
|
`${executionContext.contractDefinition.txId}|${currentInteraction.id}`,
|
||||||
|
deepCopy(state)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onNextIteration<State>(
|
||||||
|
currentInteraction: GQLNodeInterface,
|
||||||
|
executionContext: ExecutionContext<State>
|
||||||
|
): Promise<EvalStateResult<State>> {
|
||||||
|
const cacheKey = `${executionContext.contractDefinition.txId}|${currentInteraction.id}`;
|
||||||
|
const cachedState = this.transactionStateCache.get(cacheKey);
|
||||||
|
|
||||||
|
if (cachedState == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return deepCopy(cachedState as EvalStateResult<State>);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export class HandlerExecutorFactory implements ExecutorFactory<HandlerApi<unknow
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
const self = this;
|
const self = this;
|
||||||
|
const contractLogger = LoggerFactory.INST.create('Contract');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async handle<Input, Result>(
|
async handle<Input, Result>(
|
||||||
@@ -59,11 +60,10 @@ export class HandlerExecutorFactory implements ExecutorFactory<HandlerApi<unknow
|
|||||||
currentTx: { interactionTxId: string; contractTxId: string }[]
|
currentTx: { interactionTxId: string; contractTxId: string }[]
|
||||||
): Promise<InteractionResult<State, Result>> {
|
): Promise<InteractionResult<State, Result>> {
|
||||||
try {
|
try {
|
||||||
const contractLogger = LoggerFactory.INST.create('Contract');
|
|
||||||
const handler = contractFunction(swGlobal, BigNumber, clarity, contractLogger) as HandlerFunction<State, Input, Result>;
|
const handler = contractFunction(swGlobal, BigNumber, clarity, contractLogger) as HandlerFunction<State, Input, Result>;
|
||||||
const stateCopy = JSON.parse(JSON.stringify(state));
|
const stateCopy = JSON.parse(JSON.stringify(state));
|
||||||
swGlobal._activeTx = interactionTx;
|
swGlobal._activeTx = interactionTx;
|
||||||
self.logger.debug(`SmartWeave.contract.id:`, swGlobal.contract.id);
|
self.logger.trace(`SmartWeave.contract.id:`, swGlobal.contract.id);
|
||||||
|
|
||||||
self.assignReadContractState<Input, State>(swGlobal, contractDefinition, executionContext, currentTx);
|
self.assignReadContractState<Input, State>(swGlobal, contractDefinition, executionContext, currentTx);
|
||||||
self.assignViewContractState<Input, State>(swGlobal, contractDefinition, executionContext);
|
self.assignViewContractState<Input, State>(swGlobal, contractDefinition, executionContext);
|
||||||
@@ -117,7 +117,9 @@ export class HandlerExecutorFactory implements ExecutorFactory<HandlerApi<unknow
|
|||||||
to: contractTxId,
|
to: contractTxId,
|
||||||
input
|
input
|
||||||
});
|
});
|
||||||
const childContract = executionContext.smartweave.contract(contractTxId, executionContext.contract);
|
const childContract = executionContext.smartweave
|
||||||
|
.contract(contractTxId, executionContext.contract)
|
||||||
|
.setEvaluationOptions(executionContext.evaluationOptions);
|
||||||
|
|
||||||
return await childContract.viewStateForTx(input, swGlobal._activeTx);
|
return await childContract.viewStateForTx(input, swGlobal._activeTx);
|
||||||
};
|
};
|
||||||
@@ -130,12 +132,16 @@ export class HandlerExecutorFactory implements ExecutorFactory<HandlerApi<unknow
|
|||||||
currentTx: { interactionTxId: string; contractTxId: string }[]
|
currentTx: { interactionTxId: string; contractTxId: string }[]
|
||||||
) {
|
) {
|
||||||
swGlobal.contracts.readContractState = async (contractTxId: string, height?: number, returnValidity?: boolean) => {
|
swGlobal.contracts.readContractState = async (contractTxId: string, height?: number, returnValidity?: boolean) => {
|
||||||
|
const requestedHeight = height || swGlobal.block.height;
|
||||||
this.logger.debug('swGlobal.readContractState call:', {
|
this.logger.debug('swGlobal.readContractState call:', {
|
||||||
from: contractDefinition.txId,
|
from: contractDefinition.txId,
|
||||||
to: contractTxId
|
to: contractTxId,
|
||||||
|
height: requestedHeight,
|
||||||
|
transaction: swGlobal.transaction.id
|
||||||
});
|
});
|
||||||
const requestedHeight = height || swGlobal.block.height;
|
const childContract = executionContext.smartweave
|
||||||
const childContract = executionContext.smartweave.contract(contractTxId, executionContext.contract);
|
.contract(contractTxId, executionContext.contract)
|
||||||
|
.setEvaluationOptions(executionContext.evaluationOptions);
|
||||||
|
|
||||||
const stateWithValidity = await childContract.readState(requestedHeight, [
|
const stateWithValidity = await childContract.readState(requestedHeight, [
|
||||||
...(currentTx || []),
|
...(currentTx || []),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BlockHeightCacheResult, BlockHeightKey, BlockHeightSwCache } from '@smartweave/cache';
|
import { BlockHeightCacheResult, BlockHeightKey, BlockHeightSwCache, MemCache } from '@smartweave/cache';
|
||||||
import {
|
import {
|
||||||
DefaultStateEvaluator,
|
DefaultStateEvaluator,
|
||||||
EvalStateResult,
|
EvalStateResult,
|
||||||
@@ -7,8 +7,9 @@ import {
|
|||||||
HandlerApi
|
HandlerApi
|
||||||
} from '@smartweave/core';
|
} from '@smartweave/core';
|
||||||
import Arweave from 'arweave';
|
import Arweave from 'arweave';
|
||||||
import { GQLNodeInterface } from '@smartweave/legacy';
|
import { GQLEdgeInterface, GQLNodeInterface } from '@smartweave/legacy';
|
||||||
import { Benchmark, LoggerFactory } from '@smartweave/logging';
|
import { Benchmark, LoggerFactory } from '@smartweave/logging';
|
||||||
|
import { deepCopy } from '@smartweave/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of DefaultStateEvaluator that adds caching capabilities
|
* An implementation of DefaultStateEvaluator that adds caching capabilities
|
||||||
@@ -77,6 +78,7 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
|||||||
const index = missingInteractions.findIndex((tx) => tx.node.id === entry.interactionTxId);
|
const index = missingInteractions.findIndex((tx) => tx.node.id === entry.interactionTxId);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.cLogger.debug('Inf. Loop fix - removing interaction', {
|
this.cLogger.debug('Inf. Loop fix - removing interaction', {
|
||||||
|
height: missingInteractions[index].node.block.height,
|
||||||
contractTxId: entry.contractTxId,
|
contractTxId: entry.contractTxId,
|
||||||
interactionTxId: entry.interactionTxId
|
interactionTxId: entry.interactionTxId
|
||||||
});
|
});
|
||||||
@@ -87,7 +89,7 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
|||||||
|
|
||||||
// if cache is up-to date - return immediately to speed-up the whole process
|
// if cache is up-to date - return immediately to speed-up the whole process
|
||||||
if (missingInteractions.length === 0 && cachedState) {
|
if (missingInteractions.length === 0 && cachedState) {
|
||||||
this.cLogger.debug(`State up to requested height [${requestedBlockHeight}] fully cached!`);
|
this.cLogger.fatal(`State up to requested height [${requestedBlockHeight}] fully cached!`);
|
||||||
return cachedState.cachedValue;
|
return cachedState.cachedValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,6 +112,8 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
|||||||
executionContext: ExecutionContext<State>,
|
executionContext: ExecutionContext<State>,
|
||||||
state: EvalStateResult<State>
|
state: EvalStateResult<State>
|
||||||
) {
|
) {
|
||||||
|
await super.onStateUpdate(currentInteraction, executionContext, state);
|
||||||
|
|
||||||
await this.cache.put(
|
await this.cache.put(
|
||||||
new BlockHeightKey(executionContext.contractDefinition.txId, currentInteraction.block.height),
|
new BlockHeightKey(executionContext.contractDefinition.txId, currentInteraction.block.height),
|
||||||
state
|
state
|
||||||
|
|||||||
Reference in New Issue
Block a user