refactor: client api refactor
This commit is contained in:
@@ -8,19 +8,19 @@ const logger = LoggerFactory.INST.create(__filename);
|
||||
/**
|
||||
* An implementation of ExecutorFactory that adds caching capabilities
|
||||
*/
|
||||
export class CacheableExecutorFactory<State, Api> implements ExecutorFactory<State, Api> {
|
||||
export class CacheableExecutorFactory<Api> implements ExecutorFactory<Api> {
|
||||
constructor(
|
||||
arweave: Arweave,
|
||||
private readonly baseImplementation: ExecutorFactory<State, Api>,
|
||||
private readonly baseImplementation: ExecutorFactory<Api>,
|
||||
private readonly cache: SwCache<string, Api>
|
||||
) {}
|
||||
|
||||
async create(contractDefinition: ContractDefinition<State>): Promise<Api> {
|
||||
async create<State>(contractDefinition: ContractDefinition<State>): Promise<Api> {
|
||||
// warn: do not cache on the contractDefinition.srcTxId. This might look like a good optimisation
|
||||
// (as many contracts share the same source code), but unfortunately this is causing issues
|
||||
// with the same SwGlobal object being cached for all contracts with the same source code
|
||||
// (eg. SwGlobal.contract.id field - which of course should have different value for contracts
|
||||
// with the same source).
|
||||
// (eg. SwGlobal.contract.id field - which of course should have different value for different contracts
|
||||
// that share the same source).
|
||||
const cacheKey = contractDefinition.txId;
|
||||
if (!this.cache.contains(cacheKey)) {
|
||||
logger.debug('Updating executor factory cache');
|
||||
|
||||
@@ -15,17 +15,17 @@ const logger = LoggerFactory.INST.create(__filename);
|
||||
/**
|
||||
* An implementation of DefaultStateEvaluator that adds caching capabilities
|
||||
*/
|
||||
export class CacheableStateEvaluator<State> extends DefaultStateEvaluator<State> {
|
||||
export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
||||
constructor(
|
||||
arweave: Arweave,
|
||||
private readonly cache: BlockHeightSwCache<EvalStateResult<State>>,
|
||||
executionContextModifiers: ExecutionContextModifier<State>[] = []
|
||||
private readonly cache: BlockHeightSwCache<EvalStateResult<unknown>>,
|
||||
executionContextModifiers: ExecutionContextModifier[] = []
|
||||
) {
|
||||
super(arweave, executionContextModifiers);
|
||||
}
|
||||
|
||||
async eval(
|
||||
executionContext: ExecutionContext<State, any>,
|
||||
async eval<State, Api>(
|
||||
executionContext: ExecutionContext<State>,
|
||||
currentTx: { interactionTxId: string; contractTxId: string }[]
|
||||
): Promise<EvalStateResult<State>> {
|
||||
const requestedBlockHeight = executionContext.blockHeight;
|
||||
@@ -42,7 +42,10 @@ export class CacheableStateEvaluator<State> extends DefaultStateEvaluator<State>
|
||||
// if there was anything to cache...
|
||||
if (sortedInteractionsUpToBlock.length > 0) {
|
||||
// get latest available cache for the requested block height
|
||||
cachedState = this.cache.getLessOrEqual(executionContext.contractDefinition.txId, requestedBlockHeight);
|
||||
cachedState = this.cache.getLessOrEqual(
|
||||
executionContext.contractDefinition.txId,
|
||||
requestedBlockHeight
|
||||
) as BlockHeightCacheResult<EvalStateResult<State>>;
|
||||
|
||||
if (cachedState != null) {
|
||||
logger.debug(`Cached state for ${executionContext.contractDefinition.txId}`, {
|
||||
@@ -101,9 +104,9 @@ export class CacheableStateEvaluator<State> extends DefaultStateEvaluator<State>
|
||||
);
|
||||
}
|
||||
|
||||
onStateUpdate(
|
||||
onStateUpdate<State>(
|
||||
currentInteraction: GQLNodeInterface,
|
||||
executionContext: ExecutionContext<State, HandlerApi<State>>,
|
||||
executionContext: ExecutionContext<State>,
|
||||
state: EvalStateResult<State>
|
||||
) {
|
||||
this.cache.put(
|
||||
|
||||
@@ -7,15 +7,15 @@ import { ContractDefinition, ExecutorFactory } from '@smartweave/core';
|
||||
*
|
||||
* Not meant to be used in production env! ;-)
|
||||
*/
|
||||
export class DebuggableExecutorFactory<State, Api> implements ExecutorFactory<State, Api> {
|
||||
export class DebuggableExecutorFactory<Api> implements ExecutorFactory<Api> {
|
||||
constructor(
|
||||
private readonly baseImplementation: ExecutorFactory<State, Api>,
|
||||
private readonly baseImplementation: ExecutorFactory<Api>,
|
||||
private readonly sourceCode: { [key: string]: string }
|
||||
) {
|
||||
// contract source code before default "normalization"
|
||||
}
|
||||
|
||||
async create(contractDefinition: ContractDefinition<State>): Promise<Api> {
|
||||
async create<State>(contractDefinition: ContractDefinition<State>): Promise<Api> {
|
||||
if (Object.prototype.hasOwnProperty.call(this.sourceCode, contractDefinition.txId)) {
|
||||
contractDefinition = {
|
||||
...contractDefinition,
|
||||
|
||||
@@ -3,13 +3,14 @@ import {
|
||||
ExecutionContext,
|
||||
ExecutionContextModifier,
|
||||
ExecutorFactory,
|
||||
HandlerApi,
|
||||
LoggerFactory,
|
||||
SmartWeaveError,
|
||||
SmartWeaveErrorType
|
||||
} from '@smartweave';
|
||||
|
||||
export interface EvolveCompatibleState {
|
||||
settings: never[]; // some..erm..settings?
|
||||
settings: any[]; // some..erm..settings?
|
||||
canEvolve: boolean; // whether contract is allowed to evolve. seems to default to true..
|
||||
evolve: string; // the transaction id of the Arweave transaction with the updated source code. odd naming convention..
|
||||
}
|
||||
@@ -34,21 +35,32 @@ without the need of hard-coding contract's txId in the client's source code.
|
||||
This also makes it easier to audit given contract - as you keep all its versions in one place.
|
||||
*/
|
||||
|
||||
export class Evolve<State extends EvolveCompatibleState, Api> implements ExecutionContextModifier<State> {
|
||||
function isEvolveCompatible(state: any): state is EvolveCompatibleState {
|
||||
const settings =
|
||||
state.settings && isIterable(state.settings) ? new Map<string, any>(state.settings) : new Map<string, any>();
|
||||
|
||||
return state.evolve !== undefined || settings.has('evolve');
|
||||
}
|
||||
|
||||
export class Evolve implements ExecutionContextModifier {
|
||||
constructor(
|
||||
private readonly definitionLoader: DefinitionLoader<State>,
|
||||
private readonly executorFactory: ExecutorFactory<State, Api>
|
||||
private readonly definitionLoader: DefinitionLoader,
|
||||
private readonly executorFactory: ExecutorFactory<HandlerApi<unknown>>
|
||||
) {
|
||||
this.modify = this.modify.bind(this);
|
||||
}
|
||||
|
||||
async modify(state: State, executionContext: ExecutionContext<State, Api>): Promise<ExecutionContext<State, Api>> {
|
||||
async modify<State>(state: State, executionContext: ExecutionContext<State>): Promise<ExecutionContext<State>> {
|
||||
const contractTxId = executionContext.contractDefinition.txId;
|
||||
logger.debug(`trying to evolve for: ${contractTxId}`);
|
||||
if (!isEvolveCompatible(state)) {
|
||||
logger.verbose('State is not evolve compatible');
|
||||
return executionContext;
|
||||
}
|
||||
const currentSrcTxId = executionContext.contractDefinition.srcTxId;
|
||||
|
||||
const settings =
|
||||
state.settings && isIterable(state.settings) ? new Map<string, never>(state.settings) : new Map<string, never>();
|
||||
state.settings && isIterable(state.settings) ? new Map<string, any>(state.settings) : new Map<string, any>();
|
||||
|
||||
// note: from my understanding - this variable holds the id of the transaction with updated source code.
|
||||
const evolve: string = state.evolve || settings.get('evolve');
|
||||
@@ -68,9 +80,9 @@ export class Evolve<State extends EvolveCompatibleState, Api> implements Executi
|
||||
if (currentSrcTxId !== evolve) {
|
||||
try {
|
||||
// note: that's really nasty IMO - loading original contract definition, but forcing different sourceTxId...
|
||||
logger.info(`Evolving to: ${evolve}`);
|
||||
const newContractDefinition = await this.definitionLoader.load(contractTxId, evolve);
|
||||
const newHandler = await this.executorFactory.create(newContractDefinition);
|
||||
logger.info('Evolving to: ', evolve);
|
||||
const newContractDefinition = await this.definitionLoader.load<State>(contractTxId, evolve);
|
||||
const newHandler = await this.executorFactory.create<State>(newContractDefinition);
|
||||
|
||||
const modifiedContext = {
|
||||
...executionContext,
|
||||
|
||||
Reference in New Issue
Block a user