Skip to content

Commit 43a3b76

Browse files
chore!: Add tests for createProof and verifyProof alongside an easier to use abstraction (#58)
* add ProverInput and VerifierInput abstraction * add tests * Fix export typing --------- Co-authored-by: acolytec3 <[email protected]>
1 parent 47410f3 commit 43a3b76

File tree

5 files changed

+201
-17
lines changed

5 files changed

+201
-17
lines changed

src.rs/src/verkle_ffi_api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ impl Context {
219219
{
220220
let input_bytes = serde_wasm_bindgen::from_value(input.into()).unwrap();
221221
let result = ffi_interface::verify_proof(&self.inner, input_bytes).map(|_op |Boolean::from(true))
222-
.map_err(|err| JsError::new(&format!("proof verification failed]: {:?}", err)));
222+
.map_err(|err| JsError::new(&format!("proof verification failed: {:?}", err)));
223223
return result
224224
}
225225
}

src.ts/index.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
verifyExecutionWitnessPreState as verifyExecutionWitnessPreStateBase,
88
createProof as createProofBase,
99
verifyProof as verifyProofBase,
10+
type ProverInput as ProverInputBase,
11+
type VerifierInput as VerifierInputBase,
1012
} from './verkleFFIBindings/index.js'
1113
import { Context as VerkleFFI } from './wasm/rust_verkle_wasm.js'
1214

@@ -29,15 +31,18 @@ export const loadVerkleCrypto = async () => {
2931
): Commitment =>
3032
updateCommitmentBase(verkleFFI, commitment, commitmentIndex, oldScalarValue, newScalarValue)
3133

32-
const verifyExecutionWitnessPreState = (prestateRoot: string, execution_witness_json: string): boolean =>
33-
verifyExecutionWitnessPreStateBase(prestateRoot, execution_witness_json)
34+
const verifyExecutionWitnessPreState = (
35+
prestateRoot: string,
36+
execution_witness_json: string,
37+
): boolean => verifyExecutionWitnessPreStateBase(prestateRoot, execution_witness_json)
3438

3539
const zeroCommitment = zeroCommitmentBase()
3640

3741
const hashCommitment = (commitment: Uint8Array) => verkleFFI.hashCommitment(commitment)
3842
const serializeCommitment = (commitment: Uint8Array) => verkleFFI.serializeCommitment(commitment)
39-
const createProof = (input: Uint8Array) => verkleFFI.createProof(input)
40-
const verifyProof = (proofInput: Uint8Array) => verkleFFI.verifyProof(proofInput)
43+
const createProof = (proverInputs: ProverInput[]) => createProofBase(verkleFFI, proverInputs)
44+
const verifyProof = (proof: Uint8Array, verifierInputs: VerifierInput[]) =>
45+
verifyProofBase(verkleFFI, proof, verifierInputs)
4146
return {
4247
getTreeKey,
4348
getTreeKeyHash,
@@ -47,13 +52,19 @@ export const loadVerkleCrypto = async () => {
4752
hashCommitment,
4853
serializeCommitment,
4954
createProof,
50-
verifyProof
55+
verifyProof,
5156
}
5257
}
5358

59+
// Input used to create proofs over vectors
60+
export type ProverInput = ProverInputBase
61+
// Input needed to verify proofs over vectors
62+
// alongside the proof.
63+
export type VerifierInput = VerifierInputBase
64+
5465
// This is a 32 byte serialized field element
5566
export type Scalar = Uint8Array
5667

5768
// This is a 64 byte serialized point.
5869
// It is 64 bytes because the point is serialized in uncompressed format.
59-
export type Commitment = Uint8Array
70+
export type Commitment = Uint8Array

src.ts/tests/ffi.spec.ts

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { bytesToHex, randomBytes } from '@ethereumjs/util'
22
import { beforeAll, describe, expect, test, assert } from 'vitest'
33

4-
import { VerkleCrypto, loadVerkleCrypto } from '../index.js'
4+
import { ProverInput, VerifierInput, loadVerkleCrypto } from '../index.js'
55
import { verifyExecutionWitnessPreState, Context as VerkleFFI } from '../wasm/rust_verkle_wasm.js'
6+
67
import kaustinenBlock72 from './data/kaustinen6Block72.json'
78
import kaustinenBlock73 from './data/kaustinen6Block73.json'
89

910
describe('bindings', () => {
1011
let ffi: VerkleFFI
11-
let verkleCrypto: VerkleCrypto
12+
let verkleCrypto: Awaited<ReturnType<typeof loadVerkleCrypto>>
1213
beforeAll(async () => {
1314
verkleCrypto = await loadVerkleCrypto()
1415
ffi = new VerkleFFI()
@@ -138,6 +139,99 @@ describe('bindings', () => {
138139
}
139140
})
140141

142+
test('createVerifyProof', () => {
143+
// Preparation stage
144+
//
145+
// First we will emulate having a node with children.
146+
//
147+
// In verkle, the maximum number of children an internal node can have is 256.
148+
const NUMBER_OF_CHILDREN = 256
149+
150+
// Populate the vector with 256 values.
151+
const children = []
152+
for (let i = 0; i < NUMBER_OF_CHILDREN; i++) {
153+
children.push(createScalarFromIndex(i))
154+
}
155+
156+
// Commit to that vector/children
157+
const commitment = ffi.commitToScalars(children)
158+
159+
// Serialize the commitment
160+
//
161+
// This is the format that they should arrive in, over the wire
162+
const serializedCommitment = ffi.serializeCommitment(commitment)
163+
164+
// Create proof
165+
//
166+
const proofInputs: ProverInput[] = [
167+
{ serializedCommitment: serializedCommitment, vector: children, indices: [1, 2] },
168+
]
169+
170+
const proof = verkleCrypto.createProof(proofInputs)
171+
172+
// Verify proof
173+
//
174+
const verifierInputs: VerifierInput[] = [
175+
{
176+
serializedCommitment: serializedCommitment,
177+
indexValuePairs: [
178+
{ index: 1, value: children[1] },
179+
{ index: 2, value: children[2] },
180+
],
181+
},
182+
]
183+
184+
const valid = verkleCrypto.verifyProof(proof, verifierInputs)
185+
186+
expect(valid).toBe(true)
187+
})
188+
189+
test('createVerifyProofMultipleCommitments', () => {
190+
const NUMBER_OF_CHILDREN = 256
191+
192+
// Create two sets of vectors
193+
const children1 = Array.from({ length: NUMBER_OF_CHILDREN }, (_, i) => createScalarFromIndex(i))
194+
const children2 = Array.from({ length: NUMBER_OF_CHILDREN }, (_, i) =>
195+
createScalarFromIndex(i + NUMBER_OF_CHILDREN),
196+
)
197+
198+
// Create commitments for both sets
199+
const commitment1 = ffi.commitToScalars(children1)
200+
const commitment2 = ffi.commitToScalars(children2)
201+
202+
// Serialize the commitments
203+
const serializedCommitment1 = verkleCrypto.serializeCommitment(commitment1)
204+
const serializedCommitment2 = verkleCrypto.serializeCommitment(commitment2)
205+
206+
// Create proof
207+
const proofInputs: ProverInput[] = [
208+
{ serializedCommitment: serializedCommitment1, vector: children1, indices: [1, 2] },
209+
{ serializedCommitment: serializedCommitment2, vector: children2, indices: [3, 4] },
210+
]
211+
const proof = verkleCrypto.createProof(proofInputs)
212+
213+
// Verify proof
214+
const verifierInputs: VerifierInput[] = [
215+
{
216+
serializedCommitment: serializedCommitment1,
217+
indexValuePairs: [
218+
{ index: 1, value: children1[1] },
219+
{ index: 2, value: children1[2] },
220+
],
221+
},
222+
{
223+
serializedCommitment: serializedCommitment2,
224+
indexValuePairs: [
225+
{ index: 3, value: children2[3] },
226+
{ index: 4, value: children2[4] },
227+
],
228+
},
229+
]
230+
const valid = verkleCrypto.verifyProof(proof, verifierInputs)
231+
232+
expect(valid).toBe(true)
233+
})
234+
141235
test('serializeCommitment', () => {
142236
// Create a commitment that we can hash
143237
const scalar = new Uint8Array([
@@ -229,3 +323,11 @@ describe('bindings', () => {
229323
}).toThrow('Expected 32 bytes, got 1')
230324
})
231325
})
326+
327+
function createScalarFromIndex(index: number): Uint8Array {
328+
const BYTES_PER_SCALAR = 32
329+
330+
const scalar = new Uint8Array(BYTES_PER_SCALAR)
331+
scalar[0] = index // Set first byte to index (little-endian)
332+
return scalar
333+
}

src.ts/verkleFFIBindings/index.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
1-
import { initVerkleWasm, zeroCommitment, verifyExecutionWitnessPreState } from '../wasm/rust_verkle_wasm.js'
1+
import {
2+
initVerkleWasm,
3+
zeroCommitment,
4+
verifyExecutionWitnessPreState,
5+
} from '../wasm/rust_verkle_wasm.js'
26

3-
import { getTreeKey, getTreeKeyHash, updateCommitment, createProof, verifyProof } from './verkleFFI.js'
7+
import {
8+
getTreeKey,
9+
getTreeKeyHash,
10+
updateCommitment,
11+
createProof,
12+
verifyProof,
13+
type ProverInput,
14+
type VerifierInput,
15+
} from './verkleFFI.js'
416

517
export {
618
initVerkleWasm,
@@ -10,5 +22,7 @@ export {
1022
zeroCommitment,
1123
verifyExecutionWitnessPreState,
1224
createProof,
13-
verifyProof
25+
verifyProof,
26+
type ProverInput,
27+
type VerifierInput,
1428
}

src.ts/verkleFFIBindings/verkleFFI.ts

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { concatBytes } from '@ethereumjs/util'
2+
13
import { Context as VerkleFFI } from '../wasm/rust_verkle_wasm.js'
24

35
// This is equal to 2n + 256n * 64n.
@@ -114,10 +116,65 @@ export function updateCommitment(
114116
return verkleFFI.updateCommitment(commitment, commitmentIndex, oldScalarValue, newScalarValue)
115117
}
116118

117-
export function createProof(verkleFFI: VerkleFFI, bytes: Uint8Array): Uint8Array {
118-
return verkleFFI.createProof(bytes)
119+
export function createProof(verkleFFI: VerkleFFI, proverInputs: ProverInput[]): Uint8Array {
120+
const serializedProofInputs = serializedProverInputs(proverInputs)
121+
return verkleFFI.createProof(serializedProofInputs)
122+
}
123+
124+
export function verifyProof(
125+
verkleFFI: VerkleFFI,
126+
proof: Uint8Array,
127+
verifierInputs: VerifierInput[],
128+
): boolean {
129+
const serializedVerifierInput = serializeVerifierInputs(proof, verifierInputs)
130+
return verkleFFI.verifyProof(serializedVerifierInput)
131+
}
132+
133+
export interface ProverInput {
134+
// Commitment to the vector we want to create a proof for
135+
serializedCommitment: Uint8Array
136+
// The vector that we want to make proofs over
137+
vector: Uint8Array[]
138+
// The indices that we want to prove exist in the vector
139+
indices: number[]
119140
}
120141

121-
export function verifyProof(verkleFFI: VerkleFFI, proof: Uint8Array): boolean {
122-
return verkleFFI.verifyProof(proof)
123-
}
142+
function serializedProverInputs(proofInputs: ProverInput[]): Uint8Array {
143+
const serializedProverInputs = proofInputs.flatMap(({ serializedCommitment, vector, indices }) =>
144+
indices.flatMap((index) => [
145+
serializedCommitment,
146+
...vector,
147+
new Uint8Array([index]),
148+
vector[index],
149+
]),
150+
)
151+
152+
return concatBytes(...serializedProverInputs)
153+
}
154+
155+
export interface VerifierInput {
156+
// A commitment to the vector that we want to verify
157+
// proofs over.
158+
serializedCommitment: Uint8Array
159+
// A tuple of index and values that we want to verify about the
160+
// committed vector.
161+
//
162+
// ie (index_i, value_i) will verify that the value of the committed
163+
// vector at index `index_i` was `value_i`
164+
indexValuePairs: Array<{ index: number; value: Uint8Array }>
165+
}
166+
167+
function serializeVerifierInputs(proof: Uint8Array, verifierInputs: VerifierInput[]): Uint8Array {
168+
const serializedVerifierInputs = [
169+
proof,
170+
...verifierInputs.flatMap(({ serializedCommitment, indexValuePairs }) =>
171+
indexValuePairs.flatMap(({ index, value }) => [
172+
serializedCommitment,
173+
new Uint8Array([index]),
174+
value,
175+
]),
176+
),
177+
]
178+
179+
return concatBytes(...serializedVerifierInputs)
180+
}

0 commit comments

Comments
 (0)