Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web3 1211 optimistic verification #23

Merged
merged 6 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions .github/workflows/ci-publish-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@ on:
jobs:
build-and-publish:
runs-on: ubuntu-latest
env:
SEED_PHRASE_1: ${{ secrets.SEED_PHRASE_1 }}
SEED_PHRASE_2: ${{ secrets.SEED_PHRASE_2 }}
SEED_PHRASE_3: ${{ secrets.SEED_PHRASE_3 }}
SEED_PHRASE_4: ${{ secrets.SEED_PHRASE_4 }}
SEED_PHRASE_5: ${{ secrets.SEED_PHRASE_5 }}
SEED_PHRASE_6: ${{ secrets.SEED_PHRASE_6 }}
SEED_PHRASE_7: ${{ secrets.SEED_PHRASE_7 }}
SEED_PHRASE_8: ${{ secrets.SEED_PHRASE_8 }}

steps:
- name: Checkout code
Expand Down Expand Up @@ -66,6 +57,7 @@ jobs:

release_notes=$(cat release_notes.md | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/"/\\"/g')
echo "release_notes=$release_notes" >> $GITHUB_ENV
echo "current_tag=$current_tag" >> $GITHUB_ENV

- name: Publish to npm
id: publish
Expand Down
3 changes: 1 addition & 2 deletions DEV_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ npm install ./path-to-package/zkverifyjs-0.2.0.tgz

1. Update `src/config/index.ts`
2. Add a new proof to src/proofTypes including processor and formatter, and add export to `src/proofTypes/index.ts`
3. Add new `SEED_PHRASE_*` environment variable to ensure parallel test runs continue to work.
4. Also note that the unit tests require an additional seed phrase (proof types / curve combo + 1)
3. Adding new `SEED_PHRASE_*` environment variables will provide more throughput for tests if they are locked waiting for one to become available from the `WalletPool`

- Search for `ADD_NEW_PROOF_TYPE` in the codebase.

Expand Down
23 changes: 20 additions & 3 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,29 @@ console.log(JSON.stringify(transactionInfo.attestationEvent)) // Attestation Eve
import { zkVerifySession, ZkVerifyEvents, TransactionStatus, VerifyTransactionInfo } from 'zkverifyjs';

async function executeVerificationTransaction(proof: unknown, publicSignals: unknown, vk: unknown) {
// Start a new zkVerifySession on our testnet (replace 'your-seed-phrase' with actual value)
// Start a new zkVerifySession on a Custom network (replace 'your-seed-phrase' with actual value)
const session = await zkVerifySession.start()
.Testnet()
.Custom('ws://my-custom-node')
.withAccount('your-seed-phrase');

// Optimistically verify the proof (requires Custom node running in unsafe mode for dryRun() call)
const { success, message } = session.optimisticVerify()
.risc0()
.execute({ proofData: {
vk: vk,
proof: proof,
publicSignals: publicSignals }
});;

if(!success) {
throw new Error("Optimistic Proof Verification Failed")
}

// Add additional dApp logic using fast response from zkVerify
// Your logic here
// Your logic here

// Execute the verification transaction
// Execute the verification transaction on zkVerify chain
const { events, transactionResult } = await session.verify().risc0()
.waitForPublishedAttestation()
.execute({ proofData: {
Expand Down
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,29 @@ console.log(JSON.stringify(transactionInfo.attestationEvent)) // Attestation Eve
import { zkVerifySession, ZkVerifyEvents, TransactionStatus, VerifyTransactionInfo } from 'zkverifyjs';

async function executeVerificationTransaction(proof: unknown, publicSignals: unknown, vk: unknown) {
// Start a new zkVerifySession on our testnet (replace 'your-seed-phrase' with actual value)
// Start a new zkVerifySession on a Custom network (replace 'your-seed-phrase' with actual value)
const session = await zkVerifySession.start()
.Testnet()
.Custom('ws://my-custom-node')
.withAccount('your-seed-phrase');

// Optimistically verify the proof (requires Custom node running in unsafe mode for dryRun() call)
const { success, message } = session.optimisticVerify()
.risc0()
.execute({ proofData: {
vk: vk,
proof: proof,
publicSignals: publicSignals }
});;

if(!success) {
throw new Error("Optimistic Proof Verification Failed")
}

// Add additional dApp logic using fast response from zkVerify
// Your logic here
// Your logic here

// Execute the verification transaction
// Execute the verification transaction on zkVerify chain
const { events, transactionResult } = await session.verify().risc0()
.waitForPublishedAttestation()
.execute({ proofData: {
Expand Down
14 changes: 12 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zkverifyjs",
"version": "0.4.0",
"version": "0.5.0",
"description": "Submit proofs to zkVerify and query proof state with ease using our npm package.",
"author": "Horizen Labs <[email protected]>",
"license": "GPL-3.0",
Expand Down Expand Up @@ -85,6 +85,7 @@
"@types/web3": "^1.2.2",
"@typescript-eslint/eslint-plugin": "^8.2.0",
"@typescript-eslint/parser": "^8.2.0",
"async-mutex": "^0.5.0",
"conventional-changelog-cli": "^5.0.0",
"eslint": "^9.9.0",
"eslint-config-prettier": "^9.1.0",
Expand Down
18 changes: 13 additions & 5 deletions src/api/account/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { setupAccount } from './index';
import { getSeedPhrase } from '../../../tests/common/utils';
import { walletPool } from '../../../tests/common/walletPool';

describe('setupAccount', () => {
beforeAll(async () => {
await cryptoWaitReady();
});

it('should return a KeyringPair when provided with a valid seed phrase', () => {
const account = setupAccount(getSeedPhrase(0));
it('should return a KeyringPair when provided with a valid seed phrase', async () => {
let wallet: string | undefined;
try {
wallet = await walletPool.acquireWallet();
const account = setupAccount(wallet);

expect(account).toBeDefined();
expect(account.publicKey).toBeDefined();
expect(account).toBeDefined();
expect(account.publicKey).toBeDefined();
} finally {
if (wallet) {
await walletPool.releaseWallet(wallet);
}
}
});

it('should throw an error with a custom message when an invalid seed phrase is provided', () => {
Expand Down
2 changes: 2 additions & 0 deletions src/api/extrinsic/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe('extrinsic utilities', () => {
proofParams.formattedVk,
proofParams.formattedProof,
proofParams.formattedPubs,
null, // TODO: Aggregate pallet (domain_id)
);
expect(extrinsic.toHex()).toBe('0x1234');
});
Expand Down Expand Up @@ -97,6 +98,7 @@ describe('extrinsic utilities', () => {
proofParams.formattedVk,
proofParams.formattedProof,
proofParams.formattedPubs,
null, // TODO: Aggregate pallet (domain_id)
);
expect(hex).toBe('0x1234');
});
Expand Down
1 change: 1 addition & 0 deletions src/api/extrinsic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const createSubmitProofExtrinsic = (
params.formattedVk,
params.formattedProof,
params.formattedPubs,
null, // TODO: Update with aggregate pallet functionality (domain_id)
);
} catch (error: unknown) {
throw new Error(formatError(error, proofType, params));
Expand Down
74 changes: 74 additions & 0 deletions src/api/optimisticVerify/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { AccountConnection, WalletConnection } from '../connection/types';
import { createSubmitProofExtrinsic } from '../extrinsic';
import { format } from '../format';
import { ProofData } from '../../types';
import { SubmittableExtrinsic } from '@polkadot/api/types';
import { FormattedProofData } from '../format/types';
import { ProofOptions } from '../../session/types';
import { VerifyInput } from '../verify/types';
import { interpretDryRunResponse } from '../../utils/helpers';
import { ApiPromise } from '@polkadot/api';

export const optimisticVerify = async (
connection: AccountConnection | WalletConnection,
proofOptions: ProofOptions,
input: VerifyInput,
): Promise<{ success: boolean; message: string }> => {
const { api } = connection;

try {
const transaction = buildTransaction(api, proofOptions, input);

const submittableExtrinsicHex = transaction.toHex();
const dryRunResult = await api.rpc.system.dryRun(submittableExtrinsicHex);
const { success, message } = await interpretDryRunResponse(
api,
dryRunResult.toHex(),
);

return { success, message };
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
success: false,
message: `Optimistic verification failed: ${errorMessage}`,
};
}
};

/**
* Builds a transaction from the provided input.
* @param api - The Polkadot.js API instance.
* @param proofOptions - Options for the proof.
* @param input - Input for the verification (proofData or extrinsic).
* @returns A SubmittableExtrinsic ready for dryRun.
* @throws If input is invalid or cannot be formatted.
*/
const buildTransaction = (
api: ApiPromise,
proofOptions: ProofOptions,
input: VerifyInput,
): SubmittableExtrinsic<'promise'> => {
if ('proofData' in input && input.proofData) {
const { proof, publicSignals, vk } = input.proofData as ProofData;
const formattedProofData: FormattedProofData = format(
proofOptions,
proof,
publicSignals,
vk,
);
return createSubmitProofExtrinsic(
api,
proofOptions.proofType,
formattedProofData,
);
}

if ('extrinsic' in input && input.extrinsic) {
return input.extrinsic;
}

throw new Error(
`Invalid input provided. Expected either 'proofData' or 'extrinsic'. Received: ${JSON.stringify(input)}`,
);
};
26 changes: 25 additions & 1 deletion src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const zkvRpc = {
description: 'Get the Merkle root and path of a stored proof',
params: [
{
name: 'attestation_id',
name: 'root_id',
type: 'u64',
},
{
Expand All @@ -106,4 +106,28 @@ export const zkvRpc = {
type: 'MerkleProof',
},
},
aggregate: {
statementPath: {
description: 'Get the Merkle root and path of a aggregate statement',
params: [
{
name: 'at',
type: 'BlockHash',
},
{
name: 'domain_id',
type: 'u32',
},
{
name: 'aggregation_id',
type: 'u64',
},
{
name: 'statement',
type: 'H256',
},
],
type: 'MerkleProof',
},
},
};
8 changes: 4 additions & 4 deletions src/proofTypes/groth16/formatter/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ export const formatG2Point = (

const formatX =
curve === 'Bls12_381'
? [x2.toString(), x1.toString()] // bls12381 uses (x2, x1)
: [x1.toString(), x2.toString()]; // bn254 uses (x1, x2)
? [x2.toString(), x1.toString()]
: [x1.toString(), x2.toString()];

const formatY =
curve === 'Bls12_381'
? [y2.toString(), y1.toString()] // bls12381 uses (y2, y1)
: [y1.toString(), y2.toString()]; // bn254 uses (y1, y2)
? [y2.toString(), y1.toString()]
: [y1.toString(), y2.toString()];

return (
formatG1Point(formatX, endianess) +
Expand Down
31 changes: 31 additions & 0 deletions src/session/builders/optimisticVerify/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ProofOptions } from '../../types';
import { VerifyInput } from '../../../api/verify/types';
import { CurveType, Library, ProofType } from '../../../config';

export type OptimisticProofMethodMap = {
[K in keyof typeof ProofType]: (
library?: Library,
curve?: CurveType,
) => OptimisticVerificationBuilder;
};

export class OptimisticVerificationBuilder {
constructor(
private readonly executeOptimisticVerify: (
proofOptions: ProofOptions,
input: VerifyInput,
) => Promise<{ success: boolean; message: string }>,
private readonly proofOptions: ProofOptions,
) {}

/**
* Executes the optimistic verification process.
* @param {VerifyInput} input - Input for the verification, either proofData or an extrinsic.
* @returns {Promise<{ success: boolean; message: string }>} Resolves with an object indicating success or failure and any message.
*/
async execute(
input: VerifyInput,
): Promise<{ success: boolean; message: string }> {
return this.executeOptimisticVerify(this.proofOptions, input);
}
}
Loading