From e22d94948552b18fff8a79bb54355dfcbc63fb7e Mon Sep 17 00:00:00 2001 From: ppedziwiatr Date: Tue, 25 Apr 2023 10:52:36 +0200 Subject: [PATCH] fix: extract VRF to a plugin #248 --- .github/workflows/tests.yml | 4 +- crates/pst/tests/contract.spec.ts | 3 +- package.json | 7 ++- src/__tests__/integration/basic/vrf.test.ts | 16 +++-- src/contract/HandlerBasedContract.ts | 9 ++- src/core/Warp.ts | 8 +++ src/core/WarpPlugin.ts | 11 +++- .../ArweaveGatewayBundledInteractionLoader.ts | 37 +++++++----- .../impl/ArweaveGatewayInteractionsLoader.ts | 26 ++++---- .../modules/impl/DefaultStateEvaluator.ts | 59 ++++++------------- src/core/modules/impl/TagsParser.ts | 6 ++ src/utils/vrf.ts | 20 ------- yarn.lock | 20 ++++++- 13 files changed, 126 insertions(+), 100 deletions(-) delete mode 100644 src/utils/vrf.ts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 168626e..197507f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,10 +23,10 @@ jobs: run: yarn test:integration:internal-writes - name: Run integration tests [wasm] run: yarn test:integration:wasm - - name: Run regression tests - run: yarn test:regression - name: Test example rust contracts run: yarn build:test:wasm + - name: Run regression tests + run: yarn test:regression - name: Trigger integration tests if: github.ref_name == 'main' run: > diff --git a/crates/pst/tests/contract.spec.ts b/crates/pst/tests/contract.spec.ts index 140cb4e..86902bd 100644 --- a/crates/pst/tests/contract.spec.ts +++ b/crates/pst/tests/contract.spec.ts @@ -21,6 +21,7 @@ import path from 'path'; import { PstContract } from '../contract/definition/bindings/ts/PstContract'; import { State } from '../contract/definition/bindings/ts/ContractState'; import { TheAnswerExtension } from './the-answer-plugin'; +import { VRFPlugin } from 'warp-contracts-plugin-vrf'; 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', '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); arweaveWrapper = new ArweaveWrapper(warp); diff --git a/package.json b/package.json index 66574d2..8ba0f93 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "warp-contracts", - "version": "1.4.2", + "version": "1.4.3-beta.0", "description": "An implementation of the SmartWeave smart contract protocol.", "types": "./lib/types/index.d.ts", "main": "./lib/cjs/index.js", @@ -78,12 +78,10 @@ }, "homepage": "https://github.com/warp-contracts/warp#readme", "dependencies": { - "@idena/vrf-js": "^1.0.1", "archiver": "^5.3.0", "arweave": "^1.12.4", "async-mutex": "^0.4.0", "bignumber.js": "9.1.1", - "elliptic": "^6.5.4", "events": "3.3.0", "fast-copy": "^3.0.0", "level": "^8.0.0", @@ -95,6 +93,7 @@ "warp-wasm-metering": "1.0.1" }, "devDependencies": { + "@idena/vrf-js": "^1.0.1", "@types/cheerio": "^0.22.30", "@types/jest": "^28.1.6", "@types/node": "^18.0.6", @@ -103,6 +102,7 @@ "arlocal": "1.1.42", "cheerio": "^1.0.0-rc.10", "colors": "^1.4.0", + "elliptic": "^6.5.4", "esbuild": "0.17.5", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", @@ -119,6 +119,7 @@ "typescript": "^4.9.5", "warp-contracts-plugin-deploy": "^1.0.3", "warp-contracts-plugin-vm2": "1.0.0", + "warp-contracts-plugin-vrf": "^1.0.3", "ws": "^8.11.0" }, "resolutions": { diff --git a/src/__tests__/integration/basic/vrf.test.ts b/src/__tests__/integration/basic/vrf.test.ts index cb6ffdf..8734761 100644 --- a/src/__tests__/integration/basic/vrf.test.ts +++ b/src/__tests__/integration/basic/vrf.test.ts @@ -17,6 +17,7 @@ import { LoggerFactory } from '../../../logging/LoggerFactory'; import { ArweaveGatewayInteractionsLoader } from '../../../core/modules/impl/ArweaveGatewayInteractionsLoader'; import { LexicographicalInteractionsSorter } from '../../../core/modules/impl/LexicographicalInteractionsSorter'; import { DeployPlugin } from 'warp-contracts-plugin-deploy'; +import { VRFPlugin } from "warp-contracts-plugin-vrf"; const EC = new elliptic.ec('secp256k1'); const key = EC.genKeyPair(); @@ -65,7 +66,8 @@ describe('Testing the Profit Sharing Token', () => { .useArweaveGateway() .setInteractionsLoader(loader) .build() - .use(new DeployPlugin()); + .use(new DeployPlugin()) + .use(new VRFPlugin()); ({ jwk: wallet, address: walletAddress } = await warp.generateWallet()); 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 () => { - 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({ 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 () => { - 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({ 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 () => { - 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({ wallet, diff --git a/src/contract/HandlerBasedContract.ts b/src/contract/HandlerBasedContract.ts index f9d264e..863fb60 100644 --- a/src/contract/HandlerBasedContract.ts +++ b/src/contract/HandlerBasedContract.ts @@ -31,7 +31,6 @@ import { } from './Contract'; import { ArTransfer, ArWallet, emptyTransfer, Tags } from './deploy/CreateContract'; import { InnerWritesEvaluator } from './InnerWritesEvaluator'; -import { generateMockVrf } from '../utils/vrf'; import { CustomSignature, Signature } from './Signature'; import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator'; import { WarpFetchWrapper } from '../core/WarpFetchWrapper'; @@ -40,6 +39,7 @@ import { TransactionStatusResponse } from '../utils/types/arweave-types'; import { InteractionState } from './states/InteractionState'; import { ContractInteractionState } from './states/ContractInteractionState'; import { Crypto } from 'warp-isomorphic'; +import { VrfPluginFunctions } from '../core/WarpPlugin'; /** * An implementation of {@link Contract} that is backwards compatible with current style @@ -688,7 +688,12 @@ export class HandlerBasedContract implements Contract { dummyTx.sortKey = await this._sorter.createSortKey(dummyTx.block.id, dummyTx.id, dummyTx.block.height, true); dummyTx.strict = strict; if (vrf) { - dummyTx.vrf = generateMockVrf(dummyTx.sortKey, arweave); + const vrfPlugin = this.warp.maybeLoadPlugin('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( diff --git a/src/core/Warp.ts b/src/core/Warp.ts index 5d38c9c..6119c10 100644 --- a/src/core/Warp.ts +++ b/src/core/Warp.ts @@ -174,6 +174,14 @@ export class Warp { return this.plugins.get(type) as WarpPlugin; } + maybeLoadPlugin(type: WarpPluginType): WarpPlugin | null { + if (!this.hasPlugin(type)) { + return null; + } + + return this.plugins.get(type) as WarpPlugin; + } + // Close cache connection async close(): Promise { return Promise.all([ diff --git a/src/core/WarpPlugin.ts b/src/core/WarpPlugin.ts index 82f5ab0..e5adf4a 100644 --- a/src/core/WarpPlugin.ts +++ b/src/core/WarpPlugin.ts @@ -1,3 +1,6 @@ +import Arweave from 'arweave'; +import { VrfData } from '../legacy/gqlResult'; + export const knownWarpPluginsPartial = [`^smartweave-extension-`] as const; export const knownWarpPlugins = [ 'evm-signature-verification', @@ -7,7 +10,8 @@ export const knownWarpPlugins = [ 'fetch-options', 'deploy', 'contract-blacklist', - 'vm2' + 'vm2', + 'vrf' ] as const; type WarpPluginPartialType = `smartweave-extension-${string}`; export type WarpKnownPluginType = (typeof knownWarpPlugins)[number]; @@ -18,3 +22,8 @@ export interface WarpPlugin { process(input: T): R; } + +export type VrfPluginFunctions = { + generateMockVrf(sortKey: string): VrfData; + verify(vrf: VrfData, sortKey: string): boolean; +}; diff --git a/src/core/modules/impl/ArweaveGatewayBundledInteractionLoader.ts b/src/core/modules/impl/ArweaveGatewayBundledInteractionLoader.ts index 84c7daf..74c8559 100644 --- a/src/core/modules/impl/ArweaveGatewayBundledInteractionLoader.ts +++ b/src/core/modules/impl/ArweaveGatewayBundledInteractionLoader.ts @@ -9,15 +9,16 @@ import { InteractionsSorter } from '../InteractionsSorter'; import { EvaluationOptions } from '../StateEvaluator'; import { LexicographicalInteractionsSorter } from './LexicographicalInteractionsSorter'; import { Warp, WarpEnvironment } from '../../Warp'; -import { generateMockVrf } from '../../../utils/vrf'; import { Tag } from 'utils/types/arweave-types'; import { ArweaveGQLTxsFetcher } from './ArweaveGQLTxsFetcher'; import { WarpTags, WARP_TAGS } from '../../KnownTags'; import { safeParseInt } from '../../../utils/utils'; +import { VrfPluginFunctions, WarpPlugin } from '../../WarpPlugin'; +import { TagsParser } from './TagsParser'; const MAX_REQUEST = 100; -// SortKey.blockHeight is blockheight -// at which interaction was send to bundler +// SortKey.blockHeight is block height +// at which interaction was sent to bundler // it can be actually finalized in later block // we assume that this maximal "delay" const EMPIRIC_BUNDLR_FINALITY_TIME = 100; @@ -44,7 +45,9 @@ export class ArweaveGatewayBundledInteractionLoader implements InteractionsLoade private arweaveFetcher: ArweaveGQLTxsFetcher; private arweaveWrapper: ArweaveWrapper; + private _warp: Warp; private readonly sorter: InteractionsSorter; + private readonly tagsParser = new TagsParser(); constructor(protected readonly arweave: Arweave, private readonly environment: WarpEnvironment) { this.sorter = new LexicographicalInteractionsSorter(arweave); @@ -108,12 +111,13 @@ export class ArweaveGatewayBundledInteractionLoader implements InteractionsLoade const sortedInteractions = await this.sorter.sort(interactions); const isLocalOrTestnetEnv = this.environment === 'local' || this.environment === 'testnet'; + const vrfPlugin = this._warp.maybeLoadPlugin('vrf'); return sortedInteractions .filter((interaction) => this.isNewerThenSortKeyBlockHeight(interaction)) .filter((interaction) => this.isSortKeyInBounds(fromSortKey, toSortKey, 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(({ node: interaction }) => interaction); } @@ -218,14 +222,18 @@ export class ArweaveGatewayBundledInteractionLoader implements InteractionsLoade return interactions; } - private maybeAddMockVrf(isLocalOrTestnetEnv: boolean, interaction: GQLEdgeInterface): GQLEdgeInterface { + private maybeAddMockVrf( + isLocalOrTestnetEnv: boolean, + interaction: GQLEdgeInterface, + vrfPlugin?: WarpPlugin + ): GQLEdgeInterface { if (isLocalOrTestnetEnv) { - if ( - interaction.node.tags.some((t) => { - return t.name == WARP_TAGS.REQUEST_VRF && t.value === 'true'; - }) - ) { - interaction.node.vrf = generateMockVrf(interaction.node.sortKey, this.arweave); + if (this.tagsParser.hasVrfTag(interaction.node)) { + if (vrfPlugin) { + 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!'); + } } } return interaction; @@ -238,11 +246,7 @@ export class ArweaveGatewayBundledInteractionLoader implements InteractionsLoade const sendToBundlerBlockHeight = Number.parseInt(blockHeightSortKey); const finalizedBlockHeight = Number(interaction.node.block.height); const blockHeightDiff = finalizedBlockHeight - sendToBundlerBlockHeight; - if (blockHeightDiff < 0) { - return false; - } - - return true; + return blockHeightDiff >= 0; } return true; } @@ -263,5 +267,6 @@ export class ArweaveGatewayBundledInteractionLoader implements InteractionsLoade set warp(warp: Warp) { this.arweaveWrapper = new ArweaveWrapper(warp); this.arweaveFetcher = new ArweaveGQLTxsFetcher(warp); + this._warp = warp; } } diff --git a/src/core/modules/impl/ArweaveGatewayInteractionsLoader.ts b/src/core/modules/impl/ArweaveGatewayInteractionsLoader.ts index 0e38ea0..e4aedcf 100644 --- a/src/core/modules/impl/ArweaveGatewayInteractionsLoader.ts +++ b/src/core/modules/impl/ArweaveGatewayInteractionsLoader.ts @@ -8,8 +8,9 @@ import { InteractionsSorter } from '../InteractionsSorter'; import { EvaluationOptions } from '../StateEvaluator'; import { LexicographicalInteractionsSorter } from './LexicographicalInteractionsSorter'; import { Warp, WarpEnvironment } from '../../Warp'; -import { generateMockVrf } from '../../../utils/vrf'; import { ArweaveGQLTxsFetcher, ArweaveTransactionQuery } from './ArweaveGQLTxsFetcher'; +import { VrfPluginFunctions } from '../../WarpPlugin'; +import { TagsParser } from './TagsParser'; const MAX_REQUEST = 100; @@ -22,6 +23,8 @@ export class ArweaveGatewayInteractionsLoader implements InteractionsLoader { private readonly sorter: InteractionsSorter; private arweaveTransactionQuery: ArweaveGQLTxsFetcher; + private _warp: Warp; + private readonly tagsParser = new TagsParser(); constructor(protected readonly arweave: Arweave, private readonly environment: WarpEnvironment) { this.sorter = new LexicographicalInteractionsSorter(arweave); @@ -76,9 +79,9 @@ export class ArweaveGatewayInteractionsLoader implements InteractionsLoader { }, first: MAX_REQUEST }; - const innerWritesInteractions = await ( - await this.arweaveTransactionQuery.transactions(innerWritesVariables) - ).filter(bundledTxsFilter); + const innerWritesInteractions = (await this.arweaveTransactionQuery.transactions(innerWritesVariables)).filter( + bundledTxsFilter + ); this.logger.debug('Inner writes interactions length:', innerWritesInteractions.length); interactions = interactions.concat(innerWritesInteractions); @@ -116,15 +119,17 @@ export class ArweaveGatewayInteractionsLoader implements InteractionsLoader { }); const isLocalOrTestnetEnv = this.environment === 'local' || this.environment === 'testnet'; + const vrfPlugin = this._warp.maybeLoadPlugin('vrf'); + return sortedInteractions.map((i) => { const interaction = i.node; if (isLocalOrTestnetEnv) { - if ( - interaction.tags.some((t) => { - return t.name == WARP_TAGS.REQUEST_VRF && t.value === 'true'; - }) - ) { - interaction.vrf = generateMockVrf(interaction.sortKey, this.arweave); + if (this.tagsParser.hasVrfTag(interaction)) { + if (vrfPlugin) { + interaction.vrf = vrfPlugin.process().generateMockVrf(interaction.sortKey); + } else { + this.logger.warn('Cannot generate mock vrf for interaction - no "warp-contracts-plugin-vrf" attached!'); + } } } @@ -142,5 +147,6 @@ export class ArweaveGatewayInteractionsLoader implements InteractionsLoader { set warp(warp: Warp) { this.arweaveTransactionQuery = new ArweaveGQLTxsFetcher(warp); + this._warp = warp; } } diff --git a/src/core/modules/impl/DefaultStateEvaluator.ts b/src/core/modules/impl/DefaultStateEvaluator.ts index 62a1610..fe4cf47 100644 --- a/src/core/modules/impl/DefaultStateEvaluator.ts +++ b/src/core/modules/impl/DefaultStateEvaluator.ts @@ -1,20 +1,24 @@ import Arweave from 'arweave'; -import { ProofHoHash } from '@idena/vrf-js'; -import elliptic from 'elliptic'; import { SortKeyCache, SortKeyCacheResult } from '../../../cache/SortKeyCache'; import { InteractionCall } from '../../ContractCallRecord'; import { ExecutionContext } from '../../../core/ExecutionContext'; import { ExecutionContextModifier } from '../../../core/ExecutionContextModifier'; -import { GQLNodeInterface, GQLTagInterface, VrfData } from '../../../legacy/gqlResult'; +import { GQLNodeInterface, GQLTagInterface } from '../../../legacy/gqlResult'; import { Benchmark } from '../../../logging/Benchmark'; import { LoggerFactory } from '../../../logging/LoggerFactory'; import { indent } from '../../../utils/utils'; import { EvalStateResult, StateEvaluator } from '../StateEvaluator'; import { ContractInteraction, HandlerApi, InteractionResult } from './HandlerExecutorFactory'; 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 @@ -71,21 +75,11 @@ export abstract class DefaultStateEvaluator implements StateEvaluator { const missingInteractionsLength = missingInteractions.length; - const evmSignatureVerificationPlugin = warp.hasPlugin('evm-signature-verification') - ? warp.loadPlugin>('evm-signature-verification') - : null; - - const progressPlugin = warp.hasPlugin('evaluation-progress') - ? warp.loadPlugin< - { - contractTxId: string; - currentInteraction: number; - allInteractions: number; - lastInteractionProcessingTime: string; - }, - void - >('evaluation-progress') - : null; + const evmSignatureVerificationPlugin = warp.maybeLoadPlugin>( + 'evm-signature-verification' + ); + const progressPlugin = warp.maybeLoadPlugin('evaluation-progress'); + const vrfPlugin = warp.maybeLoadPlugin('vrf'); let shouldBreakAfterEvolve = false; @@ -103,8 +97,12 @@ export abstract class DefaultStateEvaluator implements StateEvaluator { currentSortKey = missingInteraction.sortKey; if (missingInteraction.vrf) { - if (!this.verifyVrf(missingInteraction.vrf, missingInteraction.sortKey, this.arweave)) { - throw new Error('Vrf verification failed.'); + 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.'); + } } } @@ -315,25 +313,6 @@ export abstract class DefaultStateEvaluator implements StateEvaluator { 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( result: InteractionResult, currentTx: GQLNodeInterface, diff --git a/src/core/modules/impl/TagsParser.ts b/src/core/modules/impl/TagsParser.ts index c48303b..753acb1 100644 --- a/src/core/modules/impl/TagsParser.ts +++ b/src/core/modules/impl/TagsParser.ts @@ -112,4 +112,10 @@ export class TagsParser { return false; } + + hasVrfTag(interaction: GQLNodeInterface) { + return interaction.tags.some((t) => { + return t.name == WARP_TAGS.REQUEST_VRF && t.value === 'true'; + }); + } } diff --git a/src/utils/vrf.ts b/src/utils/vrf.ts deleted file mode 100644 index 9c3b9c5..0000000 --- a/src/utils/vrf.ts +++ /dev/null @@ -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 - }; -} diff --git a/yarn.lock b/yarn.lock index aff6aab..5596dc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2365,7 +2365,17 @@ arweave-stream-tx@^1.1.0: dependencies: 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" resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.0.tgz#046d1b74efe5b157ad85279473f3729e8c7a47a9" integrity sha512-pir1s0Lg7MzAxmFsPqht1SGOq8Hoj1fhf44/8PVjBd6T8l6JRrFN23UvKp7N+MXBYM5SqrLYB8KrgvxAaczGFg== @@ -7479,6 +7489,14 @@ warp-contracts-plugin-vm2@1.0.0: bignumber.js "^9.1.1" 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: version "1.0.0" resolved "https://registry.yarnpkg.com/warp-isomorphic/-/warp-isomorphic-1.0.0.tgz#dccccfc046bc6ac77919f8be1024ced1385c70ea"