feat: Allow to enable Vrf in testing environments #222
This commit is contained in:
@@ -138,11 +138,43 @@ describe('Testing the Profit Sharing Token', () => {
|
||||
await expect(pst.readState()).rejects.toThrow('Vrf verification failed.');
|
||||
useWrongProof.pop();
|
||||
});
|
||||
|
||||
it('should allow to test VRF on a standard forLocal Warp instance', async () => {
|
||||
const localWarp = WarpFactory.forLocal(1823);
|
||||
|
||||
const { contractTxId: vrfContractTxId } = await localWarp.createContract.deploy({
|
||||
wallet,
|
||||
initState: JSON.stringify(initialState),
|
||||
src: contractSrc
|
||||
});
|
||||
|
||||
await mineBlock(localWarp);
|
||||
const vrfContract = localWarp.contract(vrfContractTxId).connect(wallet);
|
||||
await vrfContract.writeInteraction(
|
||||
{
|
||||
function: 'vrf'
|
||||
},
|
||||
{ vrf: true }
|
||||
);
|
||||
const result = await vrfContract.readState();
|
||||
const lastTxId = Object.keys(result.cachedValue.validity).pop();
|
||||
const vrf = (result.cachedValue.state as any).vrf[lastTxId];
|
||||
|
||||
expect(vrf).not.toBeUndefined();
|
||||
expect(vrf['random_6_1'] == vrf['random_6_2']).toBe(true);
|
||||
expect(vrf['random_6_2'] == vrf['random_6_3']).toBe(true);
|
||||
expect(vrf['random_12_1'] == vrf['random_12_2']).toBe(true);
|
||||
expect(vrf['random_12_2'] == vrf['random_12_3']).toBe(true);
|
||||
expect(vrf['random_46_1'] == vrf['random_46_2']).toBe(true);
|
||||
expect(vrf['random_46_2'] == vrf['random_46_3']).toBe(true);
|
||||
expect(vrf['random_99_1'] == vrf['random_99_2']).toBe(true);
|
||||
expect(vrf['random_99_2'] == vrf['random_99_3']).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
class VrfDecorator extends ArweaveGatewayInteractionsLoader {
|
||||
constructor(protected readonly arweave: Arweave) {
|
||||
super(arweave);
|
||||
super(arweave, 'local');
|
||||
}
|
||||
|
||||
async load(
|
||||
|
||||
@@ -209,7 +209,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
if (!this.signer) {
|
||||
throw new Error("Wallet not connected. Use 'connect' method first.");
|
||||
}
|
||||
const { arweave, interactionsLoader } = this.warp;
|
||||
const { arweave, interactionsLoader, environment } = this.warp;
|
||||
|
||||
const effectiveTags = options?.tags || [];
|
||||
const effectiveTransfer = options?.transfer || emptyTransfer;
|
||||
@@ -228,7 +228,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
throw new Error('Ar Transfers are not allowed for bundled interactions');
|
||||
}
|
||||
|
||||
if (effectiveVrf && !bundleInteraction) {
|
||||
if (effectiveVrf && !bundleInteraction && environment === 'mainnet') {
|
||||
throw new Error('Vrf generation is only available for bundle interaction');
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
effectiveTransfer,
|
||||
effectiveStrict,
|
||||
false,
|
||||
false,
|
||||
effectiveVrf && environment !== 'mainnet',
|
||||
effectiveReward
|
||||
);
|
||||
const response = await arweave.transactions.post(interactionTx);
|
||||
|
||||
@@ -73,7 +73,9 @@ export class WarpBuilder {
|
||||
|
||||
public useArweaveGateway(): WarpBuilder {
|
||||
this._definitionLoader = new ContractDefinitionLoader(this._arweave, new MemCache());
|
||||
this._interactionsLoader = new CacheableInteractionsLoader(new ArweaveGatewayInteractionsLoader(this._arweave));
|
||||
this._interactionsLoader = new CacheableInteractionsLoader(
|
||||
new ArweaveGatewayInteractionsLoader(this._arweave, this._environment)
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,14 @@ import {
|
||||
import { Benchmark } from '../../../logging/Benchmark';
|
||||
import { LoggerFactory } from '../../../logging/LoggerFactory';
|
||||
import { ArweaveWrapper } from '../../../utils/ArweaveWrapper';
|
||||
import { sleep } from '../../../utils/utils';
|
||||
import { bufToBn, sleep } from '../../../utils/utils';
|
||||
import { GW_TYPE, InteractionsLoader } from '../InteractionsLoader';
|
||||
import { InteractionsSorter } from '../InteractionsSorter';
|
||||
import { EvaluationOptions } from '../StateEvaluator';
|
||||
import { LexicographicalInteractionsSorter } from './LexicographicalInteractionsSorter';
|
||||
import { WarpEnvironment } from '../../Warp';
|
||||
import elliptic from 'elliptic';
|
||||
import { Evaluate } from '@idena/vrf-js';
|
||||
|
||||
const MAX_REQUEST = 100;
|
||||
|
||||
@@ -38,6 +41,10 @@ export function bundledTxsFilter(tx: GQLEdgeInterface) {
|
||||
return !tx.node.parent?.id && !tx.node.bundledIn?.id;
|
||||
}
|
||||
|
||||
const EC = new elliptic.ec('secp256k1');
|
||||
const key = EC.genKeyPair();
|
||||
const pubKeyS = key.getPublic(true, 'hex');
|
||||
|
||||
export class ArweaveGatewayInteractionsLoader implements InteractionsLoader {
|
||||
private readonly logger = LoggerFactory.INST.create('ArweaveGatewayInteractionsLoader');
|
||||
|
||||
@@ -75,7 +82,7 @@ export class ArweaveGatewayInteractionsLoader implements InteractionsLoader {
|
||||
private readonly arweaveWrapper: ArweaveWrapper;
|
||||
private readonly sorter: InteractionsSorter;
|
||||
|
||||
constructor(protected readonly arweave: Arweave) {
|
||||
constructor(protected readonly arweave: Arweave, private readonly environment: WarpEnvironment) {
|
||||
this.arweaveWrapper = new ArweaveWrapper(arweave);
|
||||
this.sorter = new LexicographicalInteractionsSorter(arweave);
|
||||
}
|
||||
@@ -163,7 +170,28 @@ export class ArweaveGatewayInteractionsLoader implements InteractionsLoader {
|
||||
time: loadingBenchmark.elapsed()
|
||||
});
|
||||
|
||||
return sortedInteractions.map((i) => i.node);
|
||||
const isLocalOrTestnetEnv = this.environment === 'local' || this.environment === 'testnet';
|
||||
return sortedInteractions.map((i) => {
|
||||
const interaction = i.node;
|
||||
if (isLocalOrTestnetEnv) {
|
||||
if (
|
||||
interaction.tags.some((t) => {
|
||||
return t.name == SmartWeaveTags.REQUEST_VRF && t.value === 'true';
|
||||
})
|
||||
) {
|
||||
const data = this.arweave.utils.stringToBuffer(interaction.sortKey);
|
||||
const [index, proof] = Evaluate(key.getPrivate().toArray(), data);
|
||||
interaction.vrf = {
|
||||
index: this.arweave.utils.bufferTob64Url(index),
|
||||
proof: this.arweave.utils.bufferTob64Url(proof),
|
||||
bigint: bufToBn(index).toString(),
|
||||
pubkey: pubKeyS
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return interaction;
|
||||
});
|
||||
}
|
||||
|
||||
private async loadPages(variables: GqlReqVariables) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable */
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import copy from 'fast-copy';
|
||||
import { Buffer } from 'redstone-isomorphic';
|
||||
|
||||
export const sleep = (ms: number): Promise<void> => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
@@ -59,3 +60,18 @@ export function stripTrailingSlash(str: string) {
|
||||
export function indent(callDepth: number) {
|
||||
return ''.padEnd(callDepth * 2, ' ');
|
||||
}
|
||||
|
||||
export function bufToBn(buf: Buffer) {
|
||||
const hex = [];
|
||||
const u8 = Uint8Array.from(buf);
|
||||
|
||||
u8.forEach(function (i) {
|
||||
let h = i.toString(16);
|
||||
if (h.length % 2) {
|
||||
h = '0' + h;
|
||||
}
|
||||
hex.push(h);
|
||||
});
|
||||
|
||||
return BigInt('0x' + hex.join(''));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user