Skip to content

Commit

Permalink
original src tx id
Browse files Browse the repository at this point in the history
  • Loading branch information
Tadeuchi committed Sep 19, 2023
1 parent b123ee0 commit cc54182
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/contract/HandlerBasedContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ export class HandlerBasedContract<State> implements Contract<State> {

// we're calling this to verify whether proper env is used for this contract
// (e.g. test env for test contract)
await this.warp.definitionLoader.load(this._contractTxId);
await this.warp.definitionLoader.verifyEnv(this._contractTxId);

const effectiveTags = options?.tags || [];
const effectiveTransfer = options?.transfer || emptyTransfer;
Expand Down
2 changes: 2 additions & 0 deletions src/core/ContractDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class SrcCache {
export class ContractCache<State> {
txId: string;
srcTxId: string;
originalSrcTxId: string;
initState: State;
minFee: string;
owner: string;
Expand All @@ -47,6 +48,7 @@ export class ContractCache<State> {
constructor(value: ContractDefinition<State>) {
this.txId = value.txId;
this.srcTxId = value.srcTxId;
this.originalSrcTxId = value.originalSrcTxId;
this.initState = value.initState;
this.manifest = value.manifest;
this.minFee = value.minFee;
Expand Down
5 changes: 5 additions & 0 deletions src/core/modules/DefinitionLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ export interface DefinitionLoader extends GwTypeAware, WarpAware {
getCache(): BasicSortKeyCache<ContractCache<unknown>>;

getSrcCache(): BasicSortKeyCache<SrcCache>;

/**
* @throws {InvalidEnvError}
*/
verifyEnv(contractTxId: string): Promise<void>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { GW_TYPE } from '../InteractionsLoader';
import { ArweaveGQLTxsFetcher } from './ArweaveGQLTxsFetcher';
import { WasmSrc } from './wasm/WasmSrc';
import Arweave from 'arweave';
import { EnvVerifier } from './verify/EnvVerifier';

function getTagValue(tags: GQLTagInterface[], tagName: string, orDefault = undefined) {
const tag = tags.find(({ name }) => name === tagName);
Expand All @@ -37,9 +38,8 @@ export class ArweaveGatewayBundledContractDefinitionLoader implements Definition
this.logger.debug('Contract tx fetch time', benchmark.elapsed());
const owner = contractTx.owner.address;

const contractSrcTxId = evolvedSrcTxId
? evolvedSrcTxId
: getTagValue(contractTx.tags, SMART_WEAVE_TAGS.CONTRACT_SRC_TX_ID);
const originalSrcTxId = getTagValue(contractTx.tags, SMART_WEAVE_TAGS.CONTRACT_SRC_TX_ID);
const contractSrcTxId = evolvedSrcTxId ? evolvedSrcTxId : originalSrcTxId;
const testnet = getTagValue(contractTx.tags, WARP_TAGS.WARP_TESTNET) || null;

if (testnet && this.env !== 'testnet') {
Expand Down Expand Up @@ -67,6 +67,7 @@ export class ArweaveGatewayBundledContractDefinitionLoader implements Definition
const contractDefinition: ContractDefinition<State> = {
txId: contractTxId,
srcTxId: contractSrcTxId,
originalSrcTxId,
src,
srcBinary,
srcWasmLang,
Expand All @@ -86,6 +87,11 @@ export class ArweaveGatewayBundledContractDefinitionLoader implements Definition
return contractDefinition;
}

async verifyEnv(contractTxId: string): Promise<void> {
const cd = await this.load(contractTxId);
new EnvVerifier(this.env).verify(cd);
}

async fetchContractTx(contractTxId: string): Promise<GQLTransaction | null> {
const txUsingId = await this.arweaveTransactions.transaction(contractTxId);
if (txUsingId == null) {
Expand Down
12 changes: 9 additions & 3 deletions src/core/modules/impl/ContractDefinitionLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { WasmSrc } from './wasm/WasmSrc';
import { Warp, WarpEnvironment } from '../../Warp';
import { Transaction } from '../../../utils/types/arweave-types';
import { BasicSortKeyCache } from '../../../cache/BasicSortKeyCache';
import { EnvVerifier } from './verify/EnvVerifier';

export class ContractDefinitionLoader implements DefinitionLoader {
private readonly logger = LoggerFactory.INST.create('ContractDefinitionLoader');
Expand Down Expand Up @@ -45,9 +46,8 @@ export class ContractDefinitionLoader implements DefinitionLoader {
this.logger.debug('Contract tx and owner', benchmark.elapsed());
benchmark.reset();

const contractSrcTxId = forcedSrcTxId
? forcedSrcTxId
: this.tagsParser.getTag(contractTx, SMART_WEAVE_TAGS.CONTRACT_SRC_TX_ID);
const originalSrcTxId = this.tagsParser.getTag(contractTx, SMART_WEAVE_TAGS.CONTRACT_SRC_TX_ID);
const contractSrcTxId = forcedSrcTxId ? forcedSrcTxId : originalSrcTxId;
const testnet = this.tagsParser.getTag(contractTx, WARP_TAGS.WARP_TESTNET) || null;
if (testnet && this.env !== 'testnet') {
throw new Error('Trying to use testnet contract in a non-testnet env. Use the "forTestnet" factory method.');
Expand Down Expand Up @@ -77,6 +77,7 @@ export class ContractDefinitionLoader implements DefinitionLoader {
txId: contractTxId,
srcTxId: contractSrcTxId,
src,
originalSrcTxId,
srcBinary,
srcWasmLang,
initState,
Expand All @@ -91,6 +92,11 @@ export class ContractDefinitionLoader implements DefinitionLoader {
};
}

async verifyEnv(contractTxId: string): Promise<void> {
const cd = await this.load(contractTxId);
new EnvVerifier(this.env).verify(cd);
}

async loadContractSource(contractSrcTxId: string): Promise<ContractSource> {
const benchmark = Benchmark.measure();

Expand Down
33 changes: 22 additions & 11 deletions src/core/modules/impl/WarpGatewayContractDefinitionLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { CacheKey, SortKeyCacheResult } from '../../../cache/SortKeyCache';
import { Transaction } from '../../../utils/types/arweave-types';
import { getJsonResponse, stripTrailingSlash } from '../../../utils/utils';
import { BasicSortKeyCache } from '../../../cache/BasicSortKeyCache';
import { EnvVerifier } from './verify/EnvVerifier';

/**
* An extension to {@link ContractDefinitionLoader} that makes use of
Expand All @@ -29,6 +30,7 @@ export class WarpGatewayContractDefinitionLoader implements DefinitionLoader {
private contractDefinitionLoader: ContractDefinitionLoader;
private arweaveWrapper: ArweaveWrapper;
private readonly tagsParser: TagsParser;
private readonly envVerifier: EnvVerifier;
private _warp: Warp;

constructor(
Expand All @@ -39,6 +41,7 @@ export class WarpGatewayContractDefinitionLoader implements DefinitionLoader {
) {
this.contractDefinitionLoader = new ContractDefinitionLoader(arweave, env);
this.tagsParser = new TagsParser();
this.envVerifier = new EnvVerifier(this.env);
}

async load<State>(contractTxId: string, evolvedSrcTxId?: string): Promise<ContractDefinition<State>> {
Expand All @@ -51,13 +54,13 @@ export class WarpGatewayContractDefinitionLoader implements DefinitionLoader {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
result.srcBinary = Buffer.from((result.srcBinary as any).data);
}
this.verifyEnv(result);
this.envVerifier.verify(result);
return result;
}
const benchmark = Benchmark.measure();
const contract = await this.doLoad<State>(contractTxId, evolvedSrcTxId);
this.rLogger.info(`Contract definition loaded in: ${benchmark.elapsed()}`);
this.verifyEnv(contract);
this.envVerifier.verify(contract);

await this.putToCache(contractTxId, contract, evolvedSrcTxId);

Expand Down Expand Up @@ -119,29 +122,37 @@ export class WarpGatewayContractDefinitionLoader implements DefinitionLoader {
return this.srcCache;
}

private verifyEnv(def: ContractDefinition<unknown>): 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.');
}
async verifyEnv(contractTxId: string): Promise<void> {
this.envVerifier.verify(await this.getFromCache(contractTxId));
}

// Gets ContractDefinition and ContractSource from two caches and returns a combined structure
private async getFromCache<State>(contractTxId: string, srcTxId?: string): Promise<ContractDefinition<State> | null> {
const contract = (await this.definitionCache.get(new CacheKey(contractTxId, 'cd'))) as SortKeyCacheResult<
ContractCache<State>
>;

if (!contract) {
return null;
}

const src = await this.srcCache.get(new CacheKey(srcTxId || contract.cachedValue.srcTxId, 'src'));
// If not originalSrcTxId set update cache
const contractCache = contract.cachedValue;
if (!contractCache.originalSrcTxId) {
return null;
}

// If no evolved src tx id provided we assume that the original tx is needed and it the definition loaded should be updated
if (!srcTxId && contractCache.srcTxId != contractCache.originalSrcTxId) {
return null;
}

const effectiveSrcTxId = srcTxId || contractCache.srcTxId;
const src = await this.srcCache.get(new CacheKey(effectiveSrcTxId, 'src'));
if (!src) {
return null;
}
return { ...contract.cachedValue, ...src.cachedValue };
return { ...contractCache, ...src.cachedValue, srcTxId: effectiveSrcTxId };
}

// Divides ContractDefinition into entries in two caches to avoid duplicates
Expand Down
23 changes: 23 additions & 0 deletions src/core/modules/impl/verify/EnvVerifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ContractDefinition } from '../../../ContractDefinition';
import { WarpEnvironment } from '../../../Warp';

export class InvalidEnvError extends Error {}

export class EnvVerifier {
private readonly env: WarpEnvironment;

constructor(env: WarpEnvironment) {
this.env = env;
}

public verify(def: ContractDefinition<unknown>): void {
if (def.testnet && this.env !== 'testnet') {
throw new InvalidEnvError(
'Trying to use testnet contract in a non-testnet env. Use the "forTestnet" factory method.'
);
}
if (!def.testnet && this.env === 'testnet') {
throw new InvalidEnvError('Trying to use non-testnet contract in a testnet env.');
}
}
}

0 comments on commit cc54182

Please sign in to comment.