fix: extract VRF to a plugin #248
This commit is contained in:
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@@ -23,10 +23,10 @@ jobs:
|
|||||||
run: yarn test:integration:internal-writes
|
run: yarn test:integration:internal-writes
|
||||||
- name: Run integration tests [wasm]
|
- name: Run integration tests [wasm]
|
||||||
run: yarn test:integration:wasm
|
run: yarn test:integration:wasm
|
||||||
- name: Run regression tests
|
|
||||||
run: yarn test:regression
|
|
||||||
- name: Test example rust contracts
|
- name: Test example rust contracts
|
||||||
run: yarn build:test:wasm
|
run: yarn build:test:wasm
|
||||||
|
- name: Run regression tests
|
||||||
|
run: yarn test:regression
|
||||||
- name: Trigger integration tests
|
- name: Trigger integration tests
|
||||||
if: github.ref_name == 'main'
|
if: github.ref_name == 'main'
|
||||||
run: >
|
run: >
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import path from 'path';
|
|||||||
import { PstContract } from '../contract/definition/bindings/ts/PstContract';
|
import { PstContract } from '../contract/definition/bindings/ts/PstContract';
|
||||||
import { State } from '../contract/definition/bindings/ts/ContractState';
|
import { State } from '../contract/definition/bindings/ts/ContractState';
|
||||||
import { TheAnswerExtension } from './the-answer-plugin';
|
import { TheAnswerExtension } from './the-answer-plugin';
|
||||||
|
import { VRFPlugin } from 'warp-contracts-plugin-vrf';
|
||||||
|
|
||||||
jest.setTimeout(30000);
|
jest.setTimeout(30000);
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ describe('Testing the Rust WASM Profit Sharing Token', () => {
|
|||||||
LoggerFactory.INST.logLevel('debug', 'WASM:Rust');
|
LoggerFactory.INST.logLevel('debug', 'WASM:Rust');
|
||||||
//LoggerFactory.INST.logLevel('debug', 'WasmContractHandlerApi');
|
//LoggerFactory.INST.logLevel('debug', 'WasmContractHandlerApi');
|
||||||
|
|
||||||
warp = WarpFactory.forLocal(1820).use(new DeployPlugin()).use(new TheAnswerExtension());
|
warp = WarpFactory.forLocal(1820).use(new DeployPlugin()).use(new TheAnswerExtension()).use(new VRFPlugin());
|
||||||
({ arweave } = warp);
|
({ arweave } = warp);
|
||||||
arweaveWrapper = new ArweaveWrapper(warp);
|
arweaveWrapper = new ArweaveWrapper(warp);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "warp-contracts",
|
"name": "warp-contracts",
|
||||||
"version": "1.4.2",
|
"version": "1.4.3-beta.0",
|
||||||
"description": "An implementation of the SmartWeave smart contract protocol.",
|
"description": "An implementation of the SmartWeave smart contract protocol.",
|
||||||
"types": "./lib/types/index.d.ts",
|
"types": "./lib/types/index.d.ts",
|
||||||
"main": "./lib/cjs/index.js",
|
"main": "./lib/cjs/index.js",
|
||||||
@@ -78,12 +78,10 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/warp-contracts/warp#readme",
|
"homepage": "https://github.com/warp-contracts/warp#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@idena/vrf-js": "^1.0.1",
|
|
||||||
"archiver": "^5.3.0",
|
"archiver": "^5.3.0",
|
||||||
"arweave": "^1.12.4",
|
"arweave": "^1.12.4",
|
||||||
"async-mutex": "^0.4.0",
|
"async-mutex": "^0.4.0",
|
||||||
"bignumber.js": "9.1.1",
|
"bignumber.js": "9.1.1",
|
||||||
"elliptic": "^6.5.4",
|
|
||||||
"events": "3.3.0",
|
"events": "3.3.0",
|
||||||
"fast-copy": "^3.0.0",
|
"fast-copy": "^3.0.0",
|
||||||
"level": "^8.0.0",
|
"level": "^8.0.0",
|
||||||
@@ -95,6 +93,7 @@
|
|||||||
"warp-wasm-metering": "1.0.1"
|
"warp-wasm-metering": "1.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@idena/vrf-js": "^1.0.1",
|
||||||
"@types/cheerio": "^0.22.30",
|
"@types/cheerio": "^0.22.30",
|
||||||
"@types/jest": "^28.1.6",
|
"@types/jest": "^28.1.6",
|
||||||
"@types/node": "^18.0.6",
|
"@types/node": "^18.0.6",
|
||||||
@@ -103,6 +102,7 @@
|
|||||||
"arlocal": "1.1.42",
|
"arlocal": "1.1.42",
|
||||||
"cheerio": "^1.0.0-rc.10",
|
"cheerio": "^1.0.0-rc.10",
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
|
"elliptic": "^6.5.4",
|
||||||
"esbuild": "0.17.5",
|
"esbuild": "0.17.5",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
@@ -119,6 +119,7 @@
|
|||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"warp-contracts-plugin-deploy": "^1.0.3",
|
"warp-contracts-plugin-deploy": "^1.0.3",
|
||||||
"warp-contracts-plugin-vm2": "1.0.0",
|
"warp-contracts-plugin-vm2": "1.0.0",
|
||||||
|
"warp-contracts-plugin-vrf": "^1.0.3",
|
||||||
"ws": "^8.11.0"
|
"ws": "^8.11.0"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { LoggerFactory } from '../../../logging/LoggerFactory';
|
|||||||
import { ArweaveGatewayInteractionsLoader } from '../../../core/modules/impl/ArweaveGatewayInteractionsLoader';
|
import { ArweaveGatewayInteractionsLoader } from '../../../core/modules/impl/ArweaveGatewayInteractionsLoader';
|
||||||
import { LexicographicalInteractionsSorter } from '../../../core/modules/impl/LexicographicalInteractionsSorter';
|
import { LexicographicalInteractionsSorter } from '../../../core/modules/impl/LexicographicalInteractionsSorter';
|
||||||
import { DeployPlugin } from 'warp-contracts-plugin-deploy';
|
import { DeployPlugin } from 'warp-contracts-plugin-deploy';
|
||||||
|
import { VRFPlugin } from "warp-contracts-plugin-vrf";
|
||||||
|
|
||||||
const EC = new elliptic.ec('secp256k1');
|
const EC = new elliptic.ec('secp256k1');
|
||||||
const key = EC.genKeyPair();
|
const key = EC.genKeyPair();
|
||||||
@@ -65,7 +66,8 @@ describe('Testing the Profit Sharing Token', () => {
|
|||||||
.useArweaveGateway()
|
.useArweaveGateway()
|
||||||
.setInteractionsLoader(loader)
|
.setInteractionsLoader(loader)
|
||||||
.build()
|
.build()
|
||||||
.use(new DeployPlugin());
|
.use(new DeployPlugin())
|
||||||
|
.use(new VRFPlugin());
|
||||||
|
|
||||||
({ jwk: wallet, address: walletAddress } = await warp.generateWallet());
|
({ jwk: wallet, address: walletAddress } = await warp.generateWallet());
|
||||||
walletAddress = await arweave.wallets.jwkToAddress(wallet);
|
walletAddress = await arweave.wallets.jwkToAddress(wallet);
|
||||||
@@ -142,7 +144,9 @@ describe('Testing the Profit Sharing Token', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should allow to test VRF on a standard forLocal Warp instance', async () => {
|
it('should allow to test VRF on a standard forLocal Warp instance', async () => {
|
||||||
const localWarp = WarpFactory.forLocal(1823).use(new DeployPlugin());
|
const localWarp = WarpFactory.forLocal(1823)
|
||||||
|
.use(new DeployPlugin())
|
||||||
|
.use(new VRFPlugin());
|
||||||
|
|
||||||
const { contractTxId: vrfContractTxId } = await localWarp.deploy({
|
const { contractTxId: vrfContractTxId } = await localWarp.deploy({
|
||||||
wallet,
|
wallet,
|
||||||
@@ -174,7 +178,9 @@ describe('Testing the Profit Sharing Token', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should allow to test VRF on a standard forLocal Warp instance in strict mode', async () => {
|
it('should allow to test VRF on a standard forLocal Warp instance in strict mode', async () => {
|
||||||
const localWarp = WarpFactory.forLocal(1823).use(new DeployPlugin());
|
const localWarp = WarpFactory.forLocal(1823)
|
||||||
|
.use(new DeployPlugin())
|
||||||
|
.use(new VRFPlugin());
|
||||||
|
|
||||||
const { contractTxId: vrfContractTxId } = await localWarp.deploy({
|
const { contractTxId: vrfContractTxId } = await localWarp.deploy({
|
||||||
wallet,
|
wallet,
|
||||||
@@ -206,7 +212,9 @@ describe('Testing the Profit Sharing Token', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should allow to test VRF on a standard forLocal Warp instance with internal writes', async () => {
|
it('should allow to test VRF on a standard forLocal Warp instance with internal writes', async () => {
|
||||||
const localWarp = WarpFactory.forLocal(1823).use(new DeployPlugin());
|
const localWarp = WarpFactory.forLocal(1823)
|
||||||
|
.use(new DeployPlugin())
|
||||||
|
.use(new VRFPlugin());
|
||||||
|
|
||||||
const { contractTxId: vrfContractTxId } = await localWarp.deploy({
|
const { contractTxId: vrfContractTxId } = await localWarp.deploy({
|
||||||
wallet,
|
wallet,
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import {
|
|||||||
} from './Contract';
|
} from './Contract';
|
||||||
import { ArTransfer, ArWallet, emptyTransfer, Tags } from './deploy/CreateContract';
|
import { ArTransfer, ArWallet, emptyTransfer, Tags } from './deploy/CreateContract';
|
||||||
import { InnerWritesEvaluator } from './InnerWritesEvaluator';
|
import { InnerWritesEvaluator } from './InnerWritesEvaluator';
|
||||||
import { generateMockVrf } from '../utils/vrf';
|
|
||||||
import { CustomSignature, Signature } from './Signature';
|
import { CustomSignature, Signature } from './Signature';
|
||||||
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
|
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
|
||||||
import { WarpFetchWrapper } from '../core/WarpFetchWrapper';
|
import { WarpFetchWrapper } from '../core/WarpFetchWrapper';
|
||||||
@@ -40,6 +39,7 @@ import { TransactionStatusResponse } from '../utils/types/arweave-types';
|
|||||||
import { InteractionState } from './states/InteractionState';
|
import { InteractionState } from './states/InteractionState';
|
||||||
import { ContractInteractionState } from './states/ContractInteractionState';
|
import { ContractInteractionState } from './states/ContractInteractionState';
|
||||||
import { Crypto } from 'warp-isomorphic';
|
import { Crypto } from 'warp-isomorphic';
|
||||||
|
import { VrfPluginFunctions } from '../core/WarpPlugin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of {@link Contract} that is backwards compatible with current style
|
* An implementation of {@link Contract} that is backwards compatible with current style
|
||||||
@@ -688,7 +688,12 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
dummyTx.sortKey = await this._sorter.createSortKey(dummyTx.block.id, dummyTx.id, dummyTx.block.height, true);
|
dummyTx.sortKey = await this._sorter.createSortKey(dummyTx.block.id, dummyTx.id, dummyTx.block.height, true);
|
||||||
dummyTx.strict = strict;
|
dummyTx.strict = strict;
|
||||||
if (vrf) {
|
if (vrf) {
|
||||||
dummyTx.vrf = generateMockVrf(dummyTx.sortKey, arweave);
|
const vrfPlugin = this.warp.maybeLoadPlugin<void, VrfPluginFunctions>('vrf');
|
||||||
|
if (vrfPlugin) {
|
||||||
|
dummyTx.vrf = vrfPlugin.process().generateMockVrf(dummyTx.sortKey);
|
||||||
|
} else {
|
||||||
|
this.logger.warn('Cannot generate mock vrf for interaction - no "warp-contracts-plugin-vrf" attached!');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleResult = await this.evalInteraction<Input, View>(
|
const handleResult = await this.evalInteraction<Input, View>(
|
||||||
|
|||||||
@@ -174,6 +174,14 @@ export class Warp {
|
|||||||
return this.plugins.get(type) as WarpPlugin<P, Q>;
|
return this.plugins.get(type) as WarpPlugin<P, Q>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maybeLoadPlugin<P, Q>(type: WarpPluginType): WarpPlugin<P, Q> | null {
|
||||||
|
if (!this.hasPlugin(type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.plugins.get(type) as WarpPlugin<P, Q>;
|
||||||
|
}
|
||||||
|
|
||||||
// Close cache connection
|
// Close cache connection
|
||||||
async close(): Promise<void> {
|
async close(): Promise<void> {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import Arweave from 'arweave';
|
||||||
|
import { VrfData } from '../legacy/gqlResult';
|
||||||
|
|
||||||
export const knownWarpPluginsPartial = [`^smartweave-extension-`] as const;
|
export const knownWarpPluginsPartial = [`^smartweave-extension-`] as const;
|
||||||
export const knownWarpPlugins = [
|
export const knownWarpPlugins = [
|
||||||
'evm-signature-verification',
|
'evm-signature-verification',
|
||||||
@@ -7,7 +10,8 @@ export const knownWarpPlugins = [
|
|||||||
'fetch-options',
|
'fetch-options',
|
||||||
'deploy',
|
'deploy',
|
||||||
'contract-blacklist',
|
'contract-blacklist',
|
||||||
'vm2'
|
'vm2',
|
||||||
|
'vrf'
|
||||||
] as const;
|
] as const;
|
||||||
type WarpPluginPartialType = `smartweave-extension-${string}`;
|
type WarpPluginPartialType = `smartweave-extension-${string}`;
|
||||||
export type WarpKnownPluginType = (typeof knownWarpPlugins)[number];
|
export type WarpKnownPluginType = (typeof knownWarpPlugins)[number];
|
||||||
@@ -18,3 +22,8 @@ export interface WarpPlugin<T, R> {
|
|||||||
|
|
||||||
process(input: T): R;
|
process(input: T): R;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type VrfPluginFunctions = {
|
||||||
|
generateMockVrf(sortKey: string): VrfData;
|
||||||
|
verify(vrf: VrfData, sortKey: string): boolean;
|
||||||
|
};
|
||||||
|
|||||||
@@ -9,15 +9,16 @@ import { InteractionsSorter } from '../InteractionsSorter';
|
|||||||
import { EvaluationOptions } from '../StateEvaluator';
|
import { EvaluationOptions } from '../StateEvaluator';
|
||||||
import { LexicographicalInteractionsSorter } from './LexicographicalInteractionsSorter';
|
import { LexicographicalInteractionsSorter } from './LexicographicalInteractionsSorter';
|
||||||
import { Warp, WarpEnvironment } from '../../Warp';
|
import { Warp, WarpEnvironment } from '../../Warp';
|
||||||
import { generateMockVrf } from '../../../utils/vrf';
|
|
||||||
import { Tag } from 'utils/types/arweave-types';
|
import { Tag } from 'utils/types/arweave-types';
|
||||||
import { ArweaveGQLTxsFetcher } from './ArweaveGQLTxsFetcher';
|
import { ArweaveGQLTxsFetcher } from './ArweaveGQLTxsFetcher';
|
||||||
import { WarpTags, WARP_TAGS } from '../../KnownTags';
|
import { WarpTags, WARP_TAGS } from '../../KnownTags';
|
||||||
import { safeParseInt } from '../../../utils/utils';
|
import { safeParseInt } from '../../../utils/utils';
|
||||||
|
import { VrfPluginFunctions, WarpPlugin } from '../../WarpPlugin';
|
||||||
|
import { TagsParser } from './TagsParser';
|
||||||
|
|
||||||
const MAX_REQUEST = 100;
|
const MAX_REQUEST = 100;
|
||||||
// SortKey.blockHeight is block height
|
// SortKey.blockHeight is block height
|
||||||
// at which interaction was send to bundler
|
// at which interaction was sent to bundler
|
||||||
// it can be actually finalized in later block
|
// it can be actually finalized in later block
|
||||||
// we assume that this maximal "delay"
|
// we assume that this maximal "delay"
|
||||||
const EMPIRIC_BUNDLR_FINALITY_TIME = 100;
|
const EMPIRIC_BUNDLR_FINALITY_TIME = 100;
|
||||||
@@ -44,7 +45,9 @@ export class ArweaveGatewayBundledInteractionLoader implements InteractionsLoade
|
|||||||
|
|
||||||
private arweaveFetcher: ArweaveGQLTxsFetcher;
|
private arweaveFetcher: ArweaveGQLTxsFetcher;
|
||||||
private arweaveWrapper: ArweaveWrapper;
|
private arweaveWrapper: ArweaveWrapper;
|
||||||
|
private _warp: Warp;
|
||||||
private readonly sorter: InteractionsSorter;
|
private readonly sorter: InteractionsSorter;
|
||||||
|
private readonly tagsParser = new TagsParser();
|
||||||
|
|
||||||
constructor(protected readonly arweave: Arweave, private readonly environment: WarpEnvironment) {
|
constructor(protected readonly arweave: Arweave, private readonly environment: WarpEnvironment) {
|
||||||
this.sorter = new LexicographicalInteractionsSorter(arweave);
|
this.sorter = new LexicographicalInteractionsSorter(arweave);
|
||||||
@@ -108,12 +111,13 @@ export class ArweaveGatewayBundledInteractionLoader implements InteractionsLoade
|
|||||||
|
|
||||||
const sortedInteractions = await this.sorter.sort(interactions);
|
const sortedInteractions = await this.sorter.sort(interactions);
|
||||||
const isLocalOrTestnetEnv = this.environment === 'local' || this.environment === 'testnet';
|
const isLocalOrTestnetEnv = this.environment === 'local' || this.environment === 'testnet';
|
||||||
|
const vrfPlugin = this._warp.maybeLoadPlugin<void, VrfPluginFunctions>('vrf');
|
||||||
|
|
||||||
return sortedInteractions
|
return sortedInteractions
|
||||||
.filter((interaction) => this.isNewerThenSortKeyBlockHeight(interaction))
|
.filter((interaction) => this.isNewerThenSortKeyBlockHeight(interaction))
|
||||||
.filter((interaction) => this.isSortKeyInBounds(fromSortKey, toSortKey, interaction))
|
.filter((interaction) => this.isSortKeyInBounds(fromSortKey, toSortKey, interaction))
|
||||||
.map((interaction) => this.attachSequencerDataToInteraction(interaction))
|
.map((interaction) => this.attachSequencerDataToInteraction(interaction))
|
||||||
.map((interaction) => this.maybeAddMockVrf(isLocalOrTestnetEnv, interaction))
|
.map((interaction) => this.maybeAddMockVrf(isLocalOrTestnetEnv, interaction, vrfPlugin))
|
||||||
.map((interaction, index, allInteractions) => this.verifySortKeyIntegrity(interaction, index, allInteractions))
|
.map((interaction, index, allInteractions) => this.verifySortKeyIntegrity(interaction, index, allInteractions))
|
||||||
.map(({ node: interaction }) => interaction);
|
.map(({ node: interaction }) => interaction);
|
||||||
}
|
}
|
||||||
@@ -218,14 +222,18 @@ export class ArweaveGatewayBundledInteractionLoader implements InteractionsLoade
|
|||||||
return interactions;
|
return interactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private maybeAddMockVrf(isLocalOrTestnetEnv: boolean, interaction: GQLEdgeInterface): GQLEdgeInterface {
|
private maybeAddMockVrf(
|
||||||
|
isLocalOrTestnetEnv: boolean,
|
||||||
|
interaction: GQLEdgeInterface,
|
||||||
|
vrfPlugin?: WarpPlugin<void, VrfPluginFunctions>
|
||||||
|
): GQLEdgeInterface {
|
||||||
if (isLocalOrTestnetEnv) {
|
if (isLocalOrTestnetEnv) {
|
||||||
if (
|
if (this.tagsParser.hasVrfTag(interaction.node)) {
|
||||||
interaction.node.tags.some((t) => {
|
if (vrfPlugin) {
|
||||||
return t.name == WARP_TAGS.REQUEST_VRF && t.value === 'true';
|
interaction.node.vrf = vrfPlugin.process().generateMockVrf(interaction.node.sortKey);
|
||||||
})
|
} else {
|
||||||
) {
|
this.logger.warn('Cannot generate mock vrf for interaction - no "warp-contracts-plugin-vrf" attached!');
|
||||||
interaction.node.vrf = generateMockVrf(interaction.node.sortKey, this.arweave);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return interaction;
|
return interaction;
|
||||||
@@ -238,11 +246,7 @@ export class ArweaveGatewayBundledInteractionLoader implements InteractionsLoade
|
|||||||
const sendToBundlerBlockHeight = Number.parseInt(blockHeightSortKey);
|
const sendToBundlerBlockHeight = Number.parseInt(blockHeightSortKey);
|
||||||
const finalizedBlockHeight = Number(interaction.node.block.height);
|
const finalizedBlockHeight = Number(interaction.node.block.height);
|
||||||
const blockHeightDiff = finalizedBlockHeight - sendToBundlerBlockHeight;
|
const blockHeightDiff = finalizedBlockHeight - sendToBundlerBlockHeight;
|
||||||
if (blockHeightDiff < 0) {
|
return blockHeightDiff >= 0;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -263,5 +267,6 @@ export class ArweaveGatewayBundledInteractionLoader implements InteractionsLoade
|
|||||||
set warp(warp: Warp) {
|
set warp(warp: Warp) {
|
||||||
this.arweaveWrapper = new ArweaveWrapper(warp);
|
this.arweaveWrapper = new ArweaveWrapper(warp);
|
||||||
this.arweaveFetcher = new ArweaveGQLTxsFetcher(warp);
|
this.arweaveFetcher = new ArweaveGQLTxsFetcher(warp);
|
||||||
|
this._warp = warp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import { InteractionsSorter } from '../InteractionsSorter';
|
|||||||
import { EvaluationOptions } from '../StateEvaluator';
|
import { EvaluationOptions } from '../StateEvaluator';
|
||||||
import { LexicographicalInteractionsSorter } from './LexicographicalInteractionsSorter';
|
import { LexicographicalInteractionsSorter } from './LexicographicalInteractionsSorter';
|
||||||
import { Warp, WarpEnvironment } from '../../Warp';
|
import { Warp, WarpEnvironment } from '../../Warp';
|
||||||
import { generateMockVrf } from '../../../utils/vrf';
|
|
||||||
import { ArweaveGQLTxsFetcher, ArweaveTransactionQuery } from './ArweaveGQLTxsFetcher';
|
import { ArweaveGQLTxsFetcher, ArweaveTransactionQuery } from './ArweaveGQLTxsFetcher';
|
||||||
|
import { VrfPluginFunctions } from '../../WarpPlugin';
|
||||||
|
import { TagsParser } from './TagsParser';
|
||||||
|
|
||||||
const MAX_REQUEST = 100;
|
const MAX_REQUEST = 100;
|
||||||
|
|
||||||
@@ -22,6 +23,8 @@ export class ArweaveGatewayInteractionsLoader implements InteractionsLoader {
|
|||||||
|
|
||||||
private readonly sorter: InteractionsSorter;
|
private readonly sorter: InteractionsSorter;
|
||||||
private arweaveTransactionQuery: ArweaveGQLTxsFetcher;
|
private arweaveTransactionQuery: ArweaveGQLTxsFetcher;
|
||||||
|
private _warp: Warp;
|
||||||
|
private readonly tagsParser = new TagsParser();
|
||||||
|
|
||||||
constructor(protected readonly arweave: Arweave, private readonly environment: WarpEnvironment) {
|
constructor(protected readonly arweave: Arweave, private readonly environment: WarpEnvironment) {
|
||||||
this.sorter = new LexicographicalInteractionsSorter(arweave);
|
this.sorter = new LexicographicalInteractionsSorter(arweave);
|
||||||
@@ -76,9 +79,9 @@ export class ArweaveGatewayInteractionsLoader implements InteractionsLoader {
|
|||||||
},
|
},
|
||||||
first: MAX_REQUEST
|
first: MAX_REQUEST
|
||||||
};
|
};
|
||||||
const innerWritesInteractions = await (
|
const innerWritesInteractions = (await this.arweaveTransactionQuery.transactions(innerWritesVariables)).filter(
|
||||||
await this.arweaveTransactionQuery.transactions(innerWritesVariables)
|
bundledTxsFilter
|
||||||
).filter(bundledTxsFilter);
|
);
|
||||||
|
|
||||||
this.logger.debug('Inner writes interactions length:', innerWritesInteractions.length);
|
this.logger.debug('Inner writes interactions length:', innerWritesInteractions.length);
|
||||||
interactions = interactions.concat(innerWritesInteractions);
|
interactions = interactions.concat(innerWritesInteractions);
|
||||||
@@ -116,15 +119,17 @@ export class ArweaveGatewayInteractionsLoader implements InteractionsLoader {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const isLocalOrTestnetEnv = this.environment === 'local' || this.environment === 'testnet';
|
const isLocalOrTestnetEnv = this.environment === 'local' || this.environment === 'testnet';
|
||||||
|
const vrfPlugin = this._warp.maybeLoadPlugin<void, VrfPluginFunctions>('vrf');
|
||||||
|
|
||||||
return sortedInteractions.map((i) => {
|
return sortedInteractions.map((i) => {
|
||||||
const interaction = i.node;
|
const interaction = i.node;
|
||||||
if (isLocalOrTestnetEnv) {
|
if (isLocalOrTestnetEnv) {
|
||||||
if (
|
if (this.tagsParser.hasVrfTag(interaction)) {
|
||||||
interaction.tags.some((t) => {
|
if (vrfPlugin) {
|
||||||
return t.name == WARP_TAGS.REQUEST_VRF && t.value === 'true';
|
interaction.vrf = vrfPlugin.process().generateMockVrf(interaction.sortKey);
|
||||||
})
|
} else {
|
||||||
) {
|
this.logger.warn('Cannot generate mock vrf for interaction - no "warp-contracts-plugin-vrf" attached!');
|
||||||
interaction.vrf = generateMockVrf(interaction.sortKey, this.arweave);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,5 +147,6 @@ export class ArweaveGatewayInteractionsLoader implements InteractionsLoader {
|
|||||||
|
|
||||||
set warp(warp: Warp) {
|
set warp(warp: Warp) {
|
||||||
this.arweaveTransactionQuery = new ArweaveGQLTxsFetcher(warp);
|
this.arweaveTransactionQuery = new ArweaveGQLTxsFetcher(warp);
|
||||||
|
this._warp = warp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
import Arweave from 'arweave';
|
import Arweave from 'arweave';
|
||||||
|
|
||||||
import { ProofHoHash } from '@idena/vrf-js';
|
|
||||||
import elliptic from 'elliptic';
|
|
||||||
import { SortKeyCache, SortKeyCacheResult } from '../../../cache/SortKeyCache';
|
import { SortKeyCache, SortKeyCacheResult } from '../../../cache/SortKeyCache';
|
||||||
import { InteractionCall } from '../../ContractCallRecord';
|
import { InteractionCall } from '../../ContractCallRecord';
|
||||||
import { ExecutionContext } from '../../../core/ExecutionContext';
|
import { ExecutionContext } from '../../../core/ExecutionContext';
|
||||||
import { ExecutionContextModifier } from '../../../core/ExecutionContextModifier';
|
import { ExecutionContextModifier } from '../../../core/ExecutionContextModifier';
|
||||||
import { GQLNodeInterface, GQLTagInterface, VrfData } from '../../../legacy/gqlResult';
|
import { GQLNodeInterface, GQLTagInterface } from '../../../legacy/gqlResult';
|
||||||
import { Benchmark } from '../../../logging/Benchmark';
|
import { Benchmark } from '../../../logging/Benchmark';
|
||||||
import { LoggerFactory } from '../../../logging/LoggerFactory';
|
import { LoggerFactory } from '../../../logging/LoggerFactory';
|
||||||
import { indent } from '../../../utils/utils';
|
import { indent } from '../../../utils/utils';
|
||||||
import { EvalStateResult, StateEvaluator } from '../StateEvaluator';
|
import { EvalStateResult, StateEvaluator } from '../StateEvaluator';
|
||||||
import { ContractInteraction, HandlerApi, InteractionResult } from './HandlerExecutorFactory';
|
import { ContractInteraction, HandlerApi, InteractionResult } from './HandlerExecutorFactory';
|
||||||
import { TagsParser } from './TagsParser';
|
import { TagsParser } from './TagsParser';
|
||||||
|
import { VrfPluginFunctions } from '../../WarpPlugin';
|
||||||
|
|
||||||
const EC = new elliptic.ec('secp256k1');
|
type EvaluationProgressInput = {
|
||||||
|
contractTxId: string;
|
||||||
|
currentInteraction: number;
|
||||||
|
allInteractions: number;
|
||||||
|
lastInteractionProcessingTime: string;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class contains the base functionality of evaluating the contracts state - according
|
* This class contains the base functionality of evaluating the contracts state - according
|
||||||
@@ -71,21 +75,11 @@ export abstract class DefaultStateEvaluator implements StateEvaluator {
|
|||||||
|
|
||||||
const missingInteractionsLength = missingInteractions.length;
|
const missingInteractionsLength = missingInteractions.length;
|
||||||
|
|
||||||
const evmSignatureVerificationPlugin = warp.hasPlugin('evm-signature-verification')
|
const evmSignatureVerificationPlugin = warp.maybeLoadPlugin<GQLNodeInterface, Promise<boolean>>(
|
||||||
? warp.loadPlugin<GQLNodeInterface, Promise<boolean>>('evm-signature-verification')
|
'evm-signature-verification'
|
||||||
: null;
|
);
|
||||||
|
const progressPlugin = warp.maybeLoadPlugin<EvaluationProgressInput, void>('evaluation-progress');
|
||||||
const progressPlugin = warp.hasPlugin('evaluation-progress')
|
const vrfPlugin = warp.maybeLoadPlugin<void, VrfPluginFunctions>('vrf');
|
||||||
? warp.loadPlugin<
|
|
||||||
{
|
|
||||||
contractTxId: string;
|
|
||||||
currentInteraction: number;
|
|
||||||
allInteractions: number;
|
|
||||||
lastInteractionProcessingTime: string;
|
|
||||||
},
|
|
||||||
void
|
|
||||||
>('evaluation-progress')
|
|
||||||
: null;
|
|
||||||
|
|
||||||
let shouldBreakAfterEvolve = false;
|
let shouldBreakAfterEvolve = false;
|
||||||
|
|
||||||
@@ -103,10 +97,14 @@ export abstract class DefaultStateEvaluator implements StateEvaluator {
|
|||||||
currentSortKey = missingInteraction.sortKey;
|
currentSortKey = missingInteraction.sortKey;
|
||||||
|
|
||||||
if (missingInteraction.vrf) {
|
if (missingInteraction.vrf) {
|
||||||
if (!this.verifyVrf(missingInteraction.vrf, missingInteraction.sortKey, this.arweave)) {
|
if (!vrfPlugin) {
|
||||||
|
this.logger.warn('Cannot verify vrf for interaction - no "warp-contracts-plugin-vrf" attached!');
|
||||||
|
} else {
|
||||||
|
if (!vrfPlugin.process().verify(missingInteraction.vrf, missingInteraction.sortKey)) {
|
||||||
throw new Error('Vrf verification failed.');
|
throw new Error('Vrf verification failed.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (evmSignatureVerificationPlugin && this.tagsParser.isEvmSigned(missingInteraction)) {
|
if (evmSignatureVerificationPlugin && this.tagsParser.isEvmSigned(missingInteraction)) {
|
||||||
try {
|
try {
|
||||||
@@ -315,25 +313,6 @@ export abstract class DefaultStateEvaluator implements StateEvaluator {
|
|||||||
return new SortKeyCacheResult(currentSortKey, evalStateResult);
|
return new SortKeyCacheResult(currentSortKey, evalStateResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
private verifyVrf(vrf: VrfData, sortKey: string, arweave: Arweave): boolean {
|
|
||||||
const keys = EC.keyFromPublic(vrf.pubkey, 'hex');
|
|
||||||
|
|
||||||
let hash;
|
|
||||||
try {
|
|
||||||
// ProofHoHash throws its own 'invalid vrf' exception
|
|
||||||
hash = ProofHoHash(
|
|
||||||
keys.getPublic(),
|
|
||||||
arweave.utils.stringToBuffer(sortKey),
|
|
||||||
arweave.utils.b64UrlToBuffer(vrf.proof)
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
} catch (e: any) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arweave.utils.bufferTob64Url(hash) == vrf.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
private logResult<State>(
|
private logResult<State>(
|
||||||
result: InteractionResult<State, unknown>,
|
result: InteractionResult<State, unknown>,
|
||||||
currentTx: GQLNodeInterface,
|
currentTx: GQLNodeInterface,
|
||||||
|
|||||||
@@ -112,4 +112,10 @@ export class TagsParser {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasVrfTag(interaction: GQLNodeInterface) {
|
||||||
|
return interaction.tags.some((t) => {
|
||||||
|
return t.name == WARP_TAGS.REQUEST_VRF && t.value === 'true';
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import elliptic from 'elliptic';
|
|
||||||
import Arweave from 'arweave';
|
|
||||||
import { Evaluate } from '@idena/vrf-js';
|
|
||||||
import { bufToBn } from './utils';
|
|
||||||
import { VrfData } from '../legacy/gqlResult';
|
|
||||||
|
|
||||||
const EC = new elliptic.ec('secp256k1');
|
|
||||||
const key = EC.genKeyPair();
|
|
||||||
const pubKeyS = key.getPublic(true, 'hex');
|
|
||||||
|
|
||||||
export function generateMockVrf(sortKey: string, arweave: Arweave): VrfData {
|
|
||||||
const data = arweave.utils.stringToBuffer(sortKey);
|
|
||||||
const [index, proof] = Evaluate(key.getPrivate().toArray(), data);
|
|
||||||
return {
|
|
||||||
index: arweave.utils.bufferTob64Url(index),
|
|
||||||
proof: arweave.utils.bufferTob64Url(proof),
|
|
||||||
bigint: bufToBn(index).toString(),
|
|
||||||
pubkey: pubKeyS
|
|
||||||
};
|
|
||||||
}
|
|
||||||
20
yarn.lock
20
yarn.lock
@@ -2365,7 +2365,17 @@ arweave-stream-tx@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
exponential-backoff "^3.1.0"
|
exponential-backoff "^3.1.0"
|
||||||
|
|
||||||
arweave@^1.10.13, arweave@^1.10.23, arweave@^1.10.5, arweave@^1.11.4, arweave@^1.12.4:
|
arweave@1.13.7:
|
||||||
|
version "1.13.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.7.tgz#cda8534c833baec372a7052c61f53b4e39a886d7"
|
||||||
|
integrity sha512-Hv+x2bSI6UyBHpuVbUDMMpMje1ETfpJWj52kKfz44O0IqDRi/LukOkkDUptup1p6OT6KP1/DdpnUnsNHoskFeA==
|
||||||
|
dependencies:
|
||||||
|
arconnect "^0.4.2"
|
||||||
|
asn1.js "^5.4.1"
|
||||||
|
base64-js "^1.5.1"
|
||||||
|
bignumber.js "^9.0.2"
|
||||||
|
|
||||||
|
arweave@^1.10.13, arweave@^1.10.23, arweave@^1.10.5, arweave@^1.11.4:
|
||||||
version "1.13.0"
|
version "1.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.0.tgz#046d1b74efe5b157ad85279473f3729e8c7a47a9"
|
resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.0.tgz#046d1b74efe5b157ad85279473f3729e8c7a47a9"
|
||||||
integrity sha512-pir1s0Lg7MzAxmFsPqht1SGOq8Hoj1fhf44/8PVjBd6T8l6JRrFN23UvKp7N+MXBYM5SqrLYB8KrgvxAaczGFg==
|
integrity sha512-pir1s0Lg7MzAxmFsPqht1SGOq8Hoj1fhf44/8PVjBd6T8l6JRrFN23UvKp7N+MXBYM5SqrLYB8KrgvxAaczGFg==
|
||||||
@@ -7479,6 +7489,14 @@ warp-contracts-plugin-vm2@1.0.0:
|
|||||||
bignumber.js "^9.1.1"
|
bignumber.js "^9.1.1"
|
||||||
vm2 "^3.9.16"
|
vm2 "^3.9.16"
|
||||||
|
|
||||||
|
warp-contracts-plugin-vrf@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/warp-contracts-plugin-vrf/-/warp-contracts-plugin-vrf-1.0.3.tgz#84077e00b02778fe181c50ed4c2bf987d72c6ffe"
|
||||||
|
integrity sha512-QIrlN/HQ4FfaqiPl38auHtJYnvC15ID3GWzsEIukzHHdcKgskuzskc0q0fCCI63q74so++9EKbQ+0k4s1+XXtw==
|
||||||
|
dependencies:
|
||||||
|
"@idena/vrf-js" "^1.0.1"
|
||||||
|
elliptic "^6.5.4"
|
||||||
|
|
||||||
warp-isomorphic@1.0.0:
|
warp-isomorphic@1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/warp-isomorphic/-/warp-isomorphic-1.0.0.tgz#dccccfc046bc6ac77919f8be1024ced1385c70ea"
|
resolved "https://registry.yarnpkg.com/warp-isomorphic/-/warp-isomorphic-1.0.0.tgz#dccccfc046bc6ac77919f8be1024ced1385c70ea"
|
||||||
|
|||||||
Reference in New Issue
Block a user