From de4f62edbaa1b9ffc6a0d89682df1c47f333ba8b Mon Sep 17 00:00:00 2001 From: Tadeuchi Date: Wed, 10 Jan 2024 14:19:24 +0100 Subject: [PATCH] caching contracts loading from arweave --- .../basic/contract-loading.test.ts | 9 +- src/__tests__/integration/basic/vrf.test.ts | 2 +- ...weave-sequenced-interaction-loader.test.ts | 36 +---- src/core/Warp.ts | 4 +- src/core/WarpBuilder.ts | 40 +++--- src/core/WarpFactory.ts | 2 +- src/core/modules/DefinitionLoader.ts | 2 + ....ts => ArweaveContractDefinitionLoader.ts} | 30 +--- .../impl/CacheableContractDefinitionLoader.ts | 130 ++++++++++++++++++ .../WarpGatewayContractDefinitionLoader.ts | 103 ++------------ src/index.ts | 2 +- tools/contract-testnet.js | 4 +- yarn.lock | 128 ++++++++++++++++- 13 files changed, 300 insertions(+), 192 deletions(-) rename src/core/modules/impl/{ContractDefinitionLoader.ts => ArweaveContractDefinitionLoader.ts} (85%) create mode 100644 src/core/modules/impl/CacheableContractDefinitionLoader.ts diff --git a/src/__tests__/integration/basic/contract-loading.test.ts b/src/__tests__/integration/basic/contract-loading.test.ts index b52d431a..da36707f 100644 --- a/src/__tests__/integration/basic/contract-loading.test.ts +++ b/src/__tests__/integration/basic/contract-loading.test.ts @@ -12,8 +12,8 @@ import { LoggerFactory } from '../../../logging/LoggerFactory'; import { WarpGatewayContractDefinitionLoader } from '../../../core/modules/impl/WarpGatewayContractDefinitionLoader'; import { DefaultEvaluationOptions } from '../../../core/modules/StateEvaluator'; import { LexicographicalInteractionsSorter } from '../../../core/modules/impl/LexicographicalInteractionsSorter'; -import { LevelDbCache } from '../../../cache/impl/LevelDbCache'; import { DeployPlugin } from 'warp-contracts-plugin-deploy'; +import { CacheableContractDefinitionLoader } from "../../../core/modules/impl/CacheableContractDefinitionLoader"; interface ExampleContractState { counter: number; @@ -22,7 +22,7 @@ interface ExampleContractState { describe('Testing WarpGatewayContractDefinitionLoader', () => { let contractSrc: string; let wallet: JWKInterface; - let loader: WarpGatewayContractDefinitionLoader; + let loader: CacheableContractDefinitionLoader; const evalOptions = new DefaultEvaluationOptions(); let sorter: LexicographicalInteractionsSorter; @@ -44,10 +44,7 @@ describe('Testing WarpGatewayContractDefinitionLoader', () => { const { arweave } = warp; - const contractCache = new LevelDbCache({ ...defaultCacheOptions, inMemory: true }); - const srcCache = new LevelDbCache({ ...defaultCacheOptions, inMemory: true }); - - loader = new WarpGatewayContractDefinitionLoader(arweave, contractCache, srcCache, 'local'); + loader = new CacheableContractDefinitionLoader(new WarpGatewayContractDefinitionLoader(arweave, 'local'), 'local', { ...defaultCacheOptions, inMemory: true }); loader.warp = warp; sorter = new LexicographicalInteractionsSorter(arweave); diff --git a/src/__tests__/integration/basic/vrf.test.ts b/src/__tests__/integration/basic/vrf.test.ts index 83218d1c..88ba6710 100644 --- a/src/__tests__/integration/basic/vrf.test.ts +++ b/src/__tests__/integration/basic/vrf.test.ts @@ -63,7 +63,7 @@ describe('Testing the Profit Sharing Token', () => { }, 'local' ) - .useArweaveGateway() + .useArweaveGateway(defaultCacheOptions) .setInteractionsLoader(loader) .build() .use(new DeployPlugin()) diff --git a/src/__tests__/regression/arweave-sequenced-interaction-loader.test.ts b/src/__tests__/regression/arweave-sequenced-interaction-loader.test.ts index 23f25316..80167e1d 100644 --- a/src/__tests__/regression/arweave-sequenced-interaction-loader.test.ts +++ b/src/__tests__/regression/arweave-sequenced-interaction-loader.test.ts @@ -2,13 +2,12 @@ import { ArweaveGatewayBundledContractDefinitionLoader } from '../../core/module import { ArweaveGatewayBundledInteractionLoader } from '../../core/modules/impl/ArweaveGatewayBundledInteractionLoader'; import { SourceType, WarpGatewayInteractionsLoader } from '../../core/modules/impl/WarpGatewayInteractionsLoader'; import { EvaluationOptions } from '../../core/modules/StateEvaluator'; -import { WarpFactory } from '../../core/WarpFactory'; +import { defaultCacheOptions, WarpFactory } from "../../core/WarpFactory"; import { LoggerFactory } from '../../logging/LoggerFactory'; import { WarpGatewayContractDefinitionLoader } from '../../core/modules/impl/WarpGatewayContractDefinitionLoader'; -import { LevelDbCache } from '../../cache/impl/LevelDbCache'; -import { ContractCache, SrcCache } from '../../core/ContractDefinition'; import stringify from 'safe-stable-stringify'; import Arweave from 'arweave/node/common'; +import { CacheableContractDefinitionLoader } from "../../core/modules/impl/CacheableContractDefinitionLoader"; const EXAMPLE_CONTRACT_TX_ID = 'T8Fakv0Sol6ALQ4Mt6FTxEJVDJWT-HDUmcI3qIA49U4'; const EXAMPLE_CONTRACT_SRC_TX_ID = 'QEIweYIpdMSer_E33VreYzmuTIx33FQ4Sq32XJqlLQw'; @@ -33,23 +32,7 @@ describe('Arweave Gateway interaction loader', () => { it('should load contract definition', async () => { const warp = WarpFactory.forMainnet(); - const contractsCache = new LevelDbCache>({ - inMemory: true, - dbLocation: '' - }); - - // Separate cache for sources to minimize duplicates - const sourceCache = new LevelDbCache({ - inMemory: true, - dbLocation: '' - }); - - const wrLoader = new WarpGatewayContractDefinitionLoader( - warp.arweave, - contractsCache, - sourceCache, - warp.environment - ); + const wrLoader = new CacheableContractDefinitionLoader(new WarpGatewayContractDefinitionLoader(warp.arweave, 'local'), 'local', { ...defaultCacheOptions, inMemory: true }) wrLoader.warp = warp; const arLoader = new ArweaveGatewayBundledContractDefinitionLoader(warp.environment); @@ -100,21 +83,10 @@ describe('Arweave Gateway interaction loader', () => { }); it('warp interaction loader and arweave interaction loader evaluates to same state', async () => { - const contractsCache = new LevelDbCache>({ - inMemory: true, - dbLocation: '' - }); - - // Separate cache for sources to minimize duplicates - const sourceCache = new LevelDbCache({ - inMemory: true, - dbLocation: '' - }); - const arweave = Arweave.init({ host: 'arweave.net', port: 443, protocol: 'https' }); const arLoader = new ArweaveGatewayBundledInteractionLoader(arweave, 'mainnet'); - const wrLoader = new WarpGatewayContractDefinitionLoader(arweave, contractsCache, sourceCache, 'mainnet'); + const wrLoader = new CacheableContractDefinitionLoader(new WarpGatewayContractDefinitionLoader(arweave, 'local'), 'local', { ...defaultCacheOptions, inMemory: true }); const withArLoader = WarpFactory.custom(arweave, { inMemory: true, dbLocation: '' }, 'mainnet') .setInteractionsLoader(arLoader) .setDefinitionLoader(wrLoader) diff --git a/src/core/Warp.ts b/src/core/Warp.ts index 441568bd..93bb8f46 100644 --- a/src/core/Warp.ts +++ b/src/core/Warp.ts @@ -12,7 +12,7 @@ import { HandlerBasedContract } from '../contract/HandlerBasedContract'; import { PstContract } from '../contract/PstContract'; import { PstContractImpl } from '../contract/PstContractImpl'; import { Testing, Wallet } from '../contract/testing/Testing'; -import { DefinitionLoader } from './modules/DefinitionLoader'; +import { CacheableDefinitionLoader } from './modules/DefinitionLoader'; import { ExecutorFactory } from './modules/ExecutorFactory'; import { HandlerApi } from './modules/impl/HandlerExecutorFactory'; import { InteractionsLoader } from './modules/InteractionsLoader'; @@ -72,7 +72,7 @@ export class Warp { constructor( readonly arweave: Arweave, - readonly definitionLoader: DefinitionLoader, + readonly definitionLoader: CacheableDefinitionLoader, readonly interactionsLoader: InteractionsLoader, readonly executorFactory: ExecutorFactory>, readonly stateEvaluator: StateEvaluator, diff --git a/src/core/WarpBuilder.ts b/src/core/WarpBuilder.ts index 821412c2..a7973d39 100644 --- a/src/core/WarpBuilder.ts +++ b/src/core/WarpBuilder.ts @@ -1,10 +1,10 @@ import Arweave from 'arweave'; import { DebuggableExecutorFactory } from '../plugins/DebuggableExecutorFactor'; -import { DefinitionLoader } from './modules/DefinitionLoader'; +import { CacheableDefinitionLoader } from './modules/DefinitionLoader'; import { ExecutorFactory } from './modules/ExecutorFactory'; import { ArweaveGatewayInteractionsLoader } from './modules/impl/ArweaveGatewayInteractionsLoader'; import { CacheableInteractionsLoader } from './modules/impl/CacheableInteractionsLoader'; -import { ContractDefinitionLoader } from './modules/impl/ContractDefinitionLoader'; +import { ArweaveContractDefinitionLoader } from './modules/impl/ArweaveContractDefinitionLoader'; import { HandlerApi } from './modules/impl/HandlerExecutorFactory'; import { WarpGatewayContractDefinitionLoader } from './modules/impl/WarpGatewayContractDefinitionLoader'; import { WarpGatewayInteractionsLoader } from './modules/impl/WarpGatewayInteractionsLoader'; @@ -12,12 +12,11 @@ import { InteractionsLoader } from './modules/InteractionsLoader'; import { StateEvaluator, EvalStateResult } from './modules/StateEvaluator'; import { WarpEnvironment, Warp } from './Warp'; import { CacheOptions, GatewayOptions } from './WarpFactory'; -import { LevelDbCache } from '../cache/impl/LevelDbCache'; -import { ContractCache, SrcCache } from './ContractDefinition'; import { BasicSortKeyCache } from '../cache/BasicSortKeyCache'; +import { CacheableContractDefinitionLoader } from './modules/impl/CacheableContractDefinitionLoader'; export class WarpBuilder { - private _definitionLoader?: DefinitionLoader; + private _definitionLoader?: CacheableDefinitionLoader; private _interactionsLoader?: InteractionsLoader; private _executorFactory?: ExecutorFactory>; private _stateEvaluator?: StateEvaluator; @@ -28,7 +27,7 @@ export class WarpBuilder { private readonly _environment: WarpEnvironment = 'custom' ) {} - public setDefinitionLoader(value: DefinitionLoader): WarpBuilder { + public setDefinitionLoader(value: CacheableDefinitionLoader): WarpBuilder { this._definitionLoader = value; return this; } @@ -61,31 +60,24 @@ export class WarpBuilder { new WarpGatewayInteractionsLoader(gatewayOptions.confirmationStatus, gatewayOptions.source) ); - const contractsCache = new LevelDbCache>({ - ...cacheOptions, - dbLocation: `${cacheOptions.dbLocation}/contracts` - }); - - // Separate cache for sources to minimize duplicates - const sourceCache = new LevelDbCache({ - ...cacheOptions, - dbLocation: `${cacheOptions.dbLocation}/source` - }); - - this._definitionLoader = new WarpGatewayContractDefinitionLoader( - this._arweave, - contractsCache, - sourceCache, - this._environment + this._definitionLoader = new CacheableContractDefinitionLoader( + new WarpGatewayContractDefinitionLoader(this._arweave, this._environment), + this._environment, + cacheOptions ); return this; } - public useArweaveGateway(): WarpBuilder { - this._definitionLoader = new ContractDefinitionLoader(this._arweave, this._environment); + public useArweaveGateway(cacheOptions: CacheOptions): WarpBuilder { this._interactionsLoader = new CacheableInteractionsLoader( new ArweaveGatewayInteractionsLoader(this._arweave, this._environment) ); + + this._definitionLoader = new CacheableContractDefinitionLoader( + new ArweaveContractDefinitionLoader(this._arweave, this._environment), + this._environment, + cacheOptions + ); return this; } diff --git a/src/core/WarpFactory.ts b/src/core/WarpFactory.ts index 3dd63434..320138bf 100644 --- a/src/core/WarpFactory.ts +++ b/src/core/WarpFactory.ts @@ -134,7 +134,7 @@ export class WarpFactory { cacheOptions: CacheOptions = defaultCacheOptions, environment: WarpEnvironment ): Warp { - return this.custom(arweave, cacheOptions, environment).useArweaveGateway().build(); + return this.custom(arweave, cacheOptions, environment).useArweaveGateway(cacheOptions).build(); } private static customWarpGw( diff --git a/src/core/modules/DefinitionLoader.ts b/src/core/modules/DefinitionLoader.ts index 86b2bcce..59ed4904 100644 --- a/src/core/modules/DefinitionLoader.ts +++ b/src/core/modules/DefinitionLoader.ts @@ -12,7 +12,9 @@ export interface DefinitionLoader extends GwTypeAware, WarpAware { load(contractTxId: string, evolvedSrcTxId?: string): Promise>; loadContractSource(srcTxId: string): Promise; +} +export interface CacheableDefinitionLoader extends DefinitionLoader { setCache(cache: BasicSortKeyCache>): void; // Cache for storing common source code or binaries diff --git a/src/core/modules/impl/ContractDefinitionLoader.ts b/src/core/modules/impl/ArweaveContractDefinitionLoader.ts similarity index 85% rename from src/core/modules/impl/ContractDefinitionLoader.ts rename to src/core/modules/impl/ArweaveContractDefinitionLoader.ts index ea78e8dc..7aecdb7c 100644 --- a/src/core/modules/impl/ContractDefinitionLoader.ts +++ b/src/core/modules/impl/ArweaveContractDefinitionLoader.ts @@ -1,12 +1,6 @@ import Arweave from 'arweave'; import { ContractType } from '../../../contract/deploy/CreateContract'; -import { - ContractDefinition, - ContractSource, - ContractCache, - SrcCache, - SUPPORTED_SRC_CONTENT_TYPES -} from '../../../core/ContractDefinition'; +import { ContractDefinition, ContractSource, SUPPORTED_SRC_CONTENT_TYPES } from '../../../core/ContractDefinition'; import { SMART_WEAVE_TAGS, WARP_TAGS } from '../../KnownTags'; import { Benchmark } from '../../../logging/Benchmark'; import { LoggerFactory } from '../../../logging/LoggerFactory'; @@ -17,11 +11,9 @@ import { TagsParser } from './TagsParser'; import { WasmSrc } from './wasm/WasmSrc'; import { Warp, WarpEnvironment } from '../../Warp'; import { Transaction } from '../../../utils/types/arweave-types'; -import { BasicSortKeyCache } from '../../../cache/BasicSortKeyCache'; -export class ContractDefinitionLoader implements DefinitionLoader { +export class ArweaveContractDefinitionLoader implements DefinitionLoader { private readonly logger = LoggerFactory.INST.create('ContractDefinitionLoader'); - protected arweaveWrapper: ArweaveWrapper; private readonly tagsParser: TagsParser; @@ -146,24 +138,6 @@ export class ContractDefinitionLoader implements DefinitionLoader { return 'arweave'; } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - setCache(cache: BasicSortKeyCache>): void { - throw new Error('No cache implemented for this loader'); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - setSrcCache(cache: BasicSortKeyCache): void { - throw new Error('No cache implemented for this loader'); - } - - getCache(): BasicSortKeyCache> { - throw new Error('No cache implemented for this loader'); - } - - getSrcCache(): BasicSortKeyCache { - throw new Error('No cache implemented for this loader'); - } - set warp(warp: Warp) { this.arweaveWrapper = new ArweaveWrapper(warp); } diff --git a/src/core/modules/impl/CacheableContractDefinitionLoader.ts b/src/core/modules/impl/CacheableContractDefinitionLoader.ts new file mode 100644 index 00000000..af58499e --- /dev/null +++ b/src/core/modules/impl/CacheableContractDefinitionLoader.ts @@ -0,0 +1,130 @@ +import { Buffer } from 'warp-isomorphic'; +import { GW_TYPE } from '../InteractionsLoader'; +import { ContractCache, ContractDefinition, ContractSource, SrcCache } from '../../ContractDefinition'; +import { Benchmark } from '../../../logging/Benchmark'; +import { LoggerFactory } from '../../../logging/LoggerFactory'; +import { CacheableDefinitionLoader, DefinitionLoader } from '../DefinitionLoader'; +import { Warp, WarpEnvironment } from '../../Warp'; +import { CacheKey, SortKeyCacheResult } from '../../../cache/SortKeyCache'; +import { BasicSortKeyCache } from '../../../cache/BasicSortKeyCache'; +import { LevelDbCache } from '../../../cache/impl/LevelDbCache'; +import { CacheOptions } from '../../WarpFactory'; + +/** + * An implementation of {@link CacheableDefinitionLoader} that delegates loading contracts and caches the result. + */ +export class CacheableContractDefinitionLoader implements CacheableDefinitionLoader { + private readonly rLogger = LoggerFactory.INST.create('CacheableContractDefinitionLoader'); + private definitionCache: BasicSortKeyCache>; + private srcCache: BasicSortKeyCache; + + constructor( + private readonly contractDefinitionLoader: DefinitionLoader, + private readonly env: WarpEnvironment, + cacheOptions: CacheOptions + ) { + this.definitionCache = new LevelDbCache>({ + ...cacheOptions, + dbLocation: `${cacheOptions.dbLocation}/contracts` + }); + + // Separate cache for sources to minimize duplicates + this.srcCache = new LevelDbCache({ + ...cacheOptions, + dbLocation: `${cacheOptions.dbLocation}/source` + }); + } + + async load(contractTxId: string, evolvedSrcTxId?: string): Promise> { + console.log('load', contractTxId, evolvedSrcTxId); + const result = await this.getFromCache(contractTxId, evolvedSrcTxId); + if (result) { + this.rLogger.debug('Hit from cache!', contractTxId, evolvedSrcTxId); + // LevelDB serializes Buffer to an object with 'type' and 'data' fields + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (result.contractType == 'wasm' && (result.srcBinary as any).data) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + result.srcBinary = Buffer.from((result.srcBinary as any).data); + } + this.verifyEnv(result); + return result; + } + const benchmark = Benchmark.measure(); + const contract = await this.contractDefinitionLoader.load(contractTxId, evolvedSrcTxId); + + this.rLogger.info(`Contract definition loaded in: ${benchmark.elapsed()}`); + this.verifyEnv(contract); + + await this.putToCache(contractTxId, contract, evolvedSrcTxId); + + return contract; + } + + async loadContractSource(contractSrcTxId: string): Promise { + return await this.contractDefinitionLoader.loadContractSource(contractSrcTxId); + } + + type(): GW_TYPE { + return this.contractDefinitionLoader.type(); + } + + setCache(cache: BasicSortKeyCache>): void { + this.definitionCache = cache; + } + + setSrcCache(cacheSrc: BasicSortKeyCache): void { + this.srcCache = cacheSrc; + } + + getCache(): BasicSortKeyCache> { + return this.definitionCache; + } + + getSrcCache(): BasicSortKeyCache { + return this.srcCache; + } + + private verifyEnv(def: ContractDefinition): void { + if (def.testnet && this.env !== 'testnet') { + throw new Error('Trying to use testnet contract in a non-testnet env. Use the "forTestnet" factory method.'); + } + if (!def.testnet && this.env === 'testnet') { + throw new Error('Trying to use non-testnet contract in a testnet env.'); + } + } + + // Gets ContractDefinition and ContractSource from two caches and returns a combined structure + private async getFromCache(contractTxId: string, srcTxId?: string): Promise | null> { + const contract = (await this.definitionCache.get(new CacheKey(contractTxId, 'cd'))) as SortKeyCacheResult< + ContractCache + >; + + if (!contract) { + return null; + } + const effectiveSrcTxId = srcTxId || contract.cachedValue.srcTxId; + + const src = await this.srcCache.get(new CacheKey(effectiveSrcTxId, 'src')); + if (!src) { + return null; + } + return { ...contract.cachedValue, ...src.cachedValue, srcTxId: effectiveSrcTxId }; + } + + // Divides ContractDefinition into entries in two caches to avoid duplicates + private async putToCache( + contractTxId: string, + value: ContractDefinition, + srcTxId?: string + ): Promise { + const src = new SrcCache(value); + const contract = new ContractCache(value); + + await this.definitionCache.put({ key: contractTxId, sortKey: 'cd' }, contract); + await this.srcCache.put({ key: srcTxId || contract.srcTxId, sortKey: 'src' }, src); + } + + set warp(warp: Warp) { + this.contractDefinitionLoader.warp = warp; + } +} diff --git a/src/core/modules/impl/WarpGatewayContractDefinitionLoader.ts b/src/core/modules/impl/WarpGatewayContractDefinitionLoader.ts index 40c5dc13..62c1b4af 100644 --- a/src/core/modules/impl/WarpGatewayContractDefinitionLoader.ts +++ b/src/core/modules/impl/WarpGatewayContractDefinitionLoader.ts @@ -1,74 +1,42 @@ import Arweave from 'arweave'; -import { ContractDefinitionLoader } from './ContractDefinitionLoader'; +import { ArweaveContractDefinitionLoader } from './ArweaveContractDefinitionLoader'; import { Buffer } from 'warp-isomorphic'; import { GW_TYPE } from '../InteractionsLoader'; -import { ContractCache, ContractDefinition, ContractSource, SrcCache } from '../../../core/ContractDefinition'; +import { ContractDefinition, ContractSource } from '../../../core/ContractDefinition'; import { WARP_TAGS } from '../../KnownTags'; -import { Benchmark } from '../../../logging/Benchmark'; import { LoggerFactory } from '../../../logging/LoggerFactory'; import { ArweaveWrapper } from '../../../utils/ArweaveWrapper'; import { DefinitionLoader } from '../DefinitionLoader'; import { WasmSrc } from './wasm/WasmSrc'; import { Warp, WarpEnvironment } from '../../Warp'; import { TagsParser } from './TagsParser'; -import { CacheKey, SortKeyCacheResult } from '../../../cache/SortKeyCache'; import { Transaction } from '../../../utils/types/arweave-types'; import { getJsonResponse, stripTrailingSlash } from '../../../utils/utils'; -import { BasicSortKeyCache } from '../../../cache/BasicSortKeyCache'; /** - * An extension to {@link ContractDefinitionLoader} that makes use of - * Warp Gateway ({@link https://github.com/redstone-finance/redstone-sw-gateway}) + * Makes use of Warp Gateway ({@link https://github.com/redstone-finance/redstone-sw-gateway}) * to load Contract Data. * * If the contract data is not available on Warp Gateway - it fallbacks to default implementation - * in {@link ContractDefinitionLoader} - i.e. loads the definition from Arweave gateway. + * in {@link ArweaveContractDefinitionLoader} - i.e. loads the definition from Arweave gateway. */ export class WarpGatewayContractDefinitionLoader implements DefinitionLoader { private readonly rLogger = LoggerFactory.INST.create('WarpGatewayContractDefinitionLoader'); - private contractDefinitionLoader: ContractDefinitionLoader; + private contractDefinitionLoader: ArweaveContractDefinitionLoader; private arweaveWrapper: ArweaveWrapper; private readonly tagsParser: TagsParser; private _warp: Warp; - constructor( - arweave: Arweave, - private definitionCache: BasicSortKeyCache>, - private srcCache: BasicSortKeyCache, - private readonly env: WarpEnvironment - ) { - this.contractDefinitionLoader = new ContractDefinitionLoader(arweave, env); + constructor(arweave: Arweave, env: WarpEnvironment) { + this.contractDefinitionLoader = new ArweaveContractDefinitionLoader(arweave, env); this.tagsParser = new TagsParser(); } async load(contractTxId: string, evolvedSrcTxId?: string): Promise> { - const result = await this.getFromCache(contractTxId, evolvedSrcTxId); - if (result) { - this.rLogger.debug('WarpGatewayContractDefinitionLoader: Hit from cache!'); - // LevelDB serializes Buffer to an object with 'type' and 'data' fields - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if (result.contractType == 'wasm' && (result.srcBinary as any).data) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - result.srcBinary = Buffer.from((result.srcBinary as any).data); - } - this.verifyEnv(result); - return result; - } - const benchmark = Benchmark.measure(); - const contract = await this.doLoad(contractTxId, evolvedSrcTxId); - this.rLogger.info(`Contract definition loaded in: ${benchmark.elapsed()}`); - this.verifyEnv(contract); - - await this.putToCache(contractTxId, contract, evolvedSrcTxId); - - return contract; - } - - async doLoad(contractTxId: string, forcedSrcTxId?: string): Promise> { try { const baseUrl = stripTrailingSlash(this._warp.gwUrl()); const result: ContractDefinition = await getJsonResponse( - fetch(`${baseUrl}/gateway/contract?txId=${contractTxId}${forcedSrcTxId ? `&srcTxId=${forcedSrcTxId}` : ''}`) + fetch(`${baseUrl}/gateway/contract?txId=${contractTxId}${evolvedSrcTxId ? `&srcTxId=${evolvedSrcTxId}` : ''}`) ); if (result.srcBinary != null && !(result.srcBinary instanceof Buffer)) { @@ -91,7 +59,7 @@ export class WarpGatewayContractDefinitionLoader implements DefinitionLoader { return result; } catch (e) { this.rLogger.warn('Falling back to default contracts loader', e); - return await this.contractDefinitionLoader.doLoad(contractTxId, forcedSrcTxId); + return await this.contractDefinitionLoader.doLoad(contractTxId, evolvedSrcTxId); } } @@ -103,59 +71,6 @@ export class WarpGatewayContractDefinitionLoader implements DefinitionLoader { return 'warp'; } - setCache(cache: BasicSortKeyCache>): void { - this.definitionCache = cache; - } - - setSrcCache(cacheSrc: BasicSortKeyCache): void { - this.srcCache = cacheSrc; - } - - getCache(): BasicSortKeyCache> { - return this.definitionCache; - } - - getSrcCache(): BasicSortKeyCache { - return this.srcCache; - } - - private verifyEnv(def: ContractDefinition): void { - if (def.testnet && this.env !== 'testnet') { - throw new Error('Trying to use testnet contract in a non-testnet env. Use the "forTestnet" factory method.'); - } - if (!def.testnet && this.env === 'testnet') { - throw new Error('Trying to use non-testnet contract in a testnet env.'); - } - } - - // Gets ContractDefinition and ContractSource from two caches and returns a combined structure - private async getFromCache(contractTxId: string, srcTxId?: string): Promise | null> { - const contract = (await this.definitionCache.get(new CacheKey(contractTxId, 'cd'))) as SortKeyCacheResult< - ContractCache - >; - if (!contract) { - return null; - } - - const src = await this.srcCache.get(new CacheKey(srcTxId || contract.cachedValue.srcTxId, 'src')); - if (!src) { - return null; - } - return { ...contract.cachedValue, ...src.cachedValue }; - } - - // Divides ContractDefinition into entries in two caches to avoid duplicates - private async putToCache( - contractTxId: string, - value: ContractDefinition, - srcTxId?: string - ): Promise { - const src = new SrcCache(value); - const contract = new ContractCache(value); - await this.definitionCache.put({ key: contractTxId, sortKey: 'cd' }, contract); - await this.srcCache.put({ key: srcTxId || contract.srcTxId, sortKey: 'src' }, src); - } - set warp(warp: Warp) { this._warp = warp; this.arweaveWrapper = new ArweaveWrapper(warp); diff --git a/src/index.ts b/src/index.ts index d1fe5b21..522a81cd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,7 +17,7 @@ export * from './core/modules/InteractionsLoader'; export * from './core/modules/InteractionsSorter'; export * from './core/modules/StateEvaluator'; -export * from './core/modules/impl/ContractDefinitionLoader'; +export * from './core/modules/impl/ArweaveContractDefinitionLoader'; export * from './core/modules/impl/WarpGatewayContractDefinitionLoader'; export * from './core/modules/impl/ArweaveGatewayInteractionsLoader'; export * from './core/modules/impl/WarpGatewayInteractionsLoader'; diff --git a/tools/contract-testnet.js b/tools/contract-testnet.js index be825f73..7062b1bf 100644 --- a/tools/contract-testnet.js +++ b/tools/contract-testnet.js @@ -7,7 +7,7 @@ const fs = require('fs'); const path =require('path'); const {readContract} = require("smartweave"); const {WarpNodeFactory} = require("../lib/cjs/core/node/WarpNodeFactory"); -const {ContractDefinitionLoader} = require("../src"); +const {ArweaveContractDefinitionLoader} = require("../src"); const logger = LoggerFactory.INST.create('Contract'); @@ -21,7 +21,7 @@ async function main() { protocol: 'https', port: 443, }); - const loader = new ContractDefinitionLoader(arweave); + const loader = new ArweaveContractDefinitionLoader(arweave); const definition = await loader.load("contract_tx_id"); console.log(definition.srcTxId); diff --git a/yarn.lock b/yarn.lock index 9d1e2cc6..eaa71cbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1276,6 +1276,36 @@ dependencies: vary "^1.1.2" +"@lmdb/lmdb-darwin-arm64@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.7.11.tgz#b717e72f023d4215d14e4c57433c711a53c782cf" + integrity sha512-r6+vYq2vKzE+vgj/rNVRMwAevq0+ZR9IeMFIqcSga+wMtMdXQ27KqQ7uS99/yXASg29bos7yHP3yk4x6Iio0lw== + +"@lmdb/lmdb-darwin-x64@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.7.11.tgz#b42898b0742b4a82b8224b742b2d174c449cd170" + integrity sha512-jhj1aB4K8ycRL1HOQT5OtzlqOq70jxUQEWRN9Gqh3TIDN30dxXtiHi6EWF516tzw6v2+3QqhDMJh8O6DtTGG8Q== + +"@lmdb/lmdb-linux-arm64@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.7.11.tgz#a8dc8e386d27006cfccbf2a8598290b63d03a9ec" + integrity sha512-7xGEfPPbmVJWcY2Nzqo11B9Nfxs+BAsiiaY/OcT4aaTDdykKeCjvKMQJA3KXCtZ1AtiC9ljyGLi+BfUwdulY5A== + +"@lmdb/lmdb-linux-arm@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.7.11.tgz#2103f48af28336efccaac008fe882dfce33e4ac5" + integrity sha512-dHfLFVSrw/v5X5lkwp0Vl7+NFpEeEYKfMG2DpdFJnnG1RgHQZngZxCaBagFoaJGykRpd2DYF1AeuXBFrAUAXfw== + +"@lmdb/lmdb-linux-x64@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.7.11.tgz#d21ac368022a662610540f2ba8bb6ff0b96a9940" + integrity sha512-vUKI3JrREMQsXX8q0Eq5zX2FlYCKWMmLiCyyJNfZK0Uyf14RBg9VtB3ObQ41b4swYh2EWaltasWVe93Y8+KDng== + +"@lmdb/lmdb-win32-x64@2.7.11": + version "2.7.11" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.7.11.tgz#af2cb4ae6d3a92ecdeb1503b73079417525476d2" + integrity sha512-BJwkHlSUgtB+Ei52Ai32M1AOMerSlzyIGA/KC4dAGL+GGwVMdwG8HGCOA2TxP3KjhbgDPMYkv7bt/NmOmRIFng== + "@mapbox/node-pre-gyp@^1.0.0": version "1.0.11" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" @@ -1291,6 +1321,36 @@ semver "^7.3.5" tar "^6.1.11" +"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz#44d752c1a2dc113f15f781b7cc4f53a307e3fa38" + integrity sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ== + +"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz#f954f34355712212a8e06c465bc06c40852c6bb3" + integrity sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw== + +"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz#45c63037f045c2b15c44f80f0393fa24f9655367" + integrity sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg== + +"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz#35707efeafe6d22b3f373caf9e8775e8920d1399" + integrity sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA== + +"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz#091b1218b66c341f532611477ef89e83f25fae4f" + integrity sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA== + +"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz#0f164b726869f71da3c594171df5ebc1c4b0a407" + integrity sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ== + "@noble/ed25519@^1.6.1": version "1.7.3" resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123" @@ -5575,6 +5635,24 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +lmdb@2.7.11: + version "2.7.11" + resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.7.11.tgz#a24b6d36b5c7ed9889cc2d9e103fdd3f5e144d7e" + integrity sha512-x9bD4hVp7PFLUoELL8RglbNXhAMt5CYhkmss+CEau9KlNoilsTzNi9QDsPZb3KMpOGZXG6jmXhW3bBxE2XVztw== + dependencies: + msgpackr "1.8.5" + node-addon-api "^4.3.0" + node-gyp-build-optional-packages "5.0.6" + ordered-binary "^1.4.0" + weak-lru-cache "^1.2.2" + optionalDependencies: + "@lmdb/lmdb-darwin-arm64" "2.7.11" + "@lmdb/lmdb-darwin-x64" "2.7.11" + "@lmdb/lmdb-linux-arm" "2.7.11" + "@lmdb/lmdb-linux-arm64" "2.7.11" + "@lmdb/lmdb-linux-x64" "2.7.11" + "@lmdb/lmdb-win32-x64" "2.7.11" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -6012,6 +6090,27 @@ ms@2.1.3, ms@^2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msgpackr-extract@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d" + integrity sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A== + dependencies: + node-gyp-build-optional-packages "5.0.7" + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" + +msgpackr@1.8.5: + version "1.8.5" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.5.tgz#8cadfb935357680648f33699d0e833c9179dbfeb" + integrity sha512-mpPs3qqTug6ahbblkThoUY2DQdNXcm4IapwOS3Vm/87vmpzLVelvp9h3It1y9l1VPpiFLV11vfOXnmeEwiIXwg== + optionalDependencies: + msgpackr-extract "^3.0.1" + multistream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/multistream/-/multistream-4.1.0.tgz#7bf00dfd119556fbc153cff3de4c6d477909f5a8" @@ -6088,7 +6187,7 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-addon-api@^4.2.0: +node-addon-api@^4.2.0, node-addon-api@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== @@ -6105,6 +6204,16 @@ node-fetch@^2.6.12, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-gyp-build-optional-packages@5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.6.tgz#2949f5cc7dace3ac470fa2ff1a37456907120a1d" + integrity sha512-2ZJErHG4du9G3/8IWl/l9Bp5BBFy63rno5GVmjQijvTuUZKsl6g8RB4KH/x3NLcV5ZBb4GsXmAuTYr6dRml3Gw== + +node-gyp-build-optional-packages@5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3" + integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w== + node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: version "4.6.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" @@ -6296,6 +6405,11 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" +ordered-binary@^1.4.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.5.1.tgz#94ccbf14181711081ee23931db0dc3f58aaa0df6" + integrity sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A== + os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" @@ -7675,6 +7789,13 @@ warp-arbundles@^1.0.4: buffer "^6.0.3" warp-isomorphic "^1.0.7" +warp-contracts-lmdb@1.1.10: + version "1.1.10" + resolved "https://registry.yarnpkg.com/warp-contracts-lmdb/-/warp-contracts-lmdb-1.1.10.tgz#f81f85c36bc7c5afdfa7aab897187051acf7479f" + integrity sha512-Ak6/FAtzImpBtXppOwxq2SYUbn0ekLH2O2EoKU2jVicuTPEz/vVaZRd3uYiUg2sJCid9iCzoDXM6XPUc8/AsUA== + dependencies: + lmdb "2.7.11" + warp-contracts-plugin-deploy@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/warp-contracts-plugin-deploy/-/warp-contracts-plugin-deploy-1.0.8.tgz#94e9841ea3f68d09e9c1bbd483efa84a2c541901" @@ -7740,6 +7861,11 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +weak-lru-cache@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz#fdbb6741f36bae9540d12f480ce8254060dccd19" + integrity sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"