From 2a66c41b79f10d2c4e28ae7f561f16ba9b03175b Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Tue, 30 Sep 2025 10:48:57 +0200 Subject: [PATCH 01/12] wip --- Cargo.toml | 5 ++-- provekit/common/src/skyscraper/mod.rs | 4 +-- provekit/common/src/skyscraper/whir.rs | 34 +++++++++++++++++++++++++- provekit/prover/src/whir_r1cs.rs | 14 ++++++++--- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a801cd70..b31a3709 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,11 +138,12 @@ noirc_driver = { git = "https://github.com/noir-lang/noir", rev = "v1.0.0-beta.1 # Cryptography and proof systems ark-bn254 = { version = "0.5.0", default-features = false, features = ["scalar_field"] } -ark-crypto-primitives = { version = "0.5", features = ["merkle_tree"] } +ark-crypto-primitives = { path="../crypto-primitives/crypto-primitives", features = ["merkle_tree"] } ark-ff = { version = "0.5", features = ["asm", "std"] } ark-poly = "0.5" ark-serialize = "0.5" ark-std = { version = "0.5", features = ["std"] } spongefish = { git = "https://github.com/arkworks-rs/spongefish", features = ["arkworks-algebra"] } spongefish-pow = { git = "https://github.com/arkworks-rs/spongefish" } -whir = { git = "https://github.com/WizardOfMenlo/whir/", features = ["tracing"], rev = "48fc0909d6a0d803d61c35b0c18499ad704da211" } \ No newline at end of file +#whir = { git = "https://github.com/WizardOfMenlo/whir/", features = ["tracing"], rev = "48fc0909d6a0d803d61c35b0c18499ad704da211" } +whir = { path = "../latest-whir/whir", features = ["tracing"] } \ No newline at end of file diff --git a/provekit/common/src/skyscraper/mod.rs b/provekit/common/src/skyscraper/mod.rs index 3b6da92a..480a31c9 100644 --- a/provekit/common/src/skyscraper/mod.rs +++ b/provekit/common/src/skyscraper/mod.rs @@ -1,5 +1,5 @@ mod pow; mod sponge; -mod whir; +pub mod whir; -pub use self::{pow::SkyscraperPoW, sponge::SkyscraperSponge, whir::SkyscraperMerkleConfig}; +pub use self::{pow::SkyscraperPoW, sponge::SkyscraperSponge, whir::SkyscraperMerkleConfig, whir::SkyscraperHasher}; diff --git a/provekit/common/src/skyscraper/whir.rs b/provekit/common/src/skyscraper/whir.rs index d855f414..ab6139d1 100644 --- a/provekit/common/src/skyscraper/whir.rs +++ b/provekit/common/src/skyscraper/whir.rs @@ -14,9 +14,11 @@ use { }, DomainSeparator, ProofResult, ProverState, VerifierState, }, - std::borrow::Borrow, + core::mem::size_of, + std::borrow::Borrow, whir::merkle_tree::{Hasher, Hash}, }; + fn compress(l: FieldElement, r: FieldElement) -> FieldElement { let l64 = l.into_bigint().0; let r64 = r.into_bigint().0; @@ -109,3 +111,33 @@ impl whir::whir::utils::DigestToUnitDeserialize Ok(r) } } + +pub struct SkyscraperHasher; + +impl SkyscraperHasher { + pub fn new() -> Self { + // Skyscraper outputs 32 bytes (one field element) + assert_eq!(size_of::(), 32); + Self + } +} + +impl Hasher for SkyscraperHasher { + fn hash_many(&self, size: usize, input: &[u8], output: &mut [Hash]) { + assert_eq!(input.len() % size, 0, "Input length not a multiple of message size."); + assert_eq!(input.len() / 2, output.len() * 32, "Output length mismatch."); + + // Reinterpret `&mut [Hash]` as a flat `&mut [u8]` + let out_bytes_len = output.len() * size_of::(); + let out_bytes = unsafe { + core::slice::from_raw_parts_mut(output.as_mut_ptr().cast::(), out_bytes_len) + }; + + // Choose the implementation you want: + // skyscraper::reference::compress_many(input, out_bytes); + // skyscraper::v1::compress_many(input, out_bytes); + // skyscraper::block3::compress_many(input, out_bytes); + skyscraper::block4::compress_many(input, out_bytes); + // skyscraper::simple::compress_many(input, out_bytes); + } +} \ No newline at end of file diff --git a/provekit/prover/src/whir_r1cs.rs b/provekit/prover/src/whir_r1cs.rs index 26bfc1a7..33f6e428 100644 --- a/provekit/prover/src/whir_r1cs.rs +++ b/provekit/prover/src/whir_r1cs.rs @@ -179,6 +179,14 @@ pub fn sum_over_hypercube(g_univariates: &[[FieldElement; 4]]) -> FieldElement { + eval_cubic_poly(&polynomial_coefficient, &FieldElement::one()) } +use provekit_common::skyscraper::whir::SkyscraperHasher; +use whir::merkle_tree::Hasher; + + +pub fn construct_skyscraper() -> Box { + Box::new(SkyscraperHasher::new()) +} + pub fn batch_commit_to_polynomial( m: usize, whir_config: &WhirConfig, @@ -200,9 +208,9 @@ pub fn batch_commit_to_polynomial( let committer = CommitmentWriter::new(whir_config.clone()); let witness_new = committer .commit_batch(merlin, &[ - masked_polynomial_coeff.clone(), - random_polynomial_coeff.clone(), - ]) + &masked_polynomial_coeff, + &random_polynomial_coeff, + ], construct_skyscraper) .expect("WHIR prover failed to commit"); (witness_new, masked_polynomial, random_polynomial_eval) From 5d05954d0ab3ac470a745055761f06fc787e98ac Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Wed, 8 Oct 2025 14:12:58 +0200 Subject: [PATCH 02/12] wip - working mt first pass --- provekit/common/src/skyscraper/whir.rs | 4 ++-- provekit/prover/src/whir_r1cs.rs | 2 +- provekit/r1cs-compiler/src/whir_r1cs.rs | 13 ++++++++----- provekit/verifier/src/whir_r1cs.rs | 17 ++++++++++++----- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/provekit/common/src/skyscraper/whir.rs b/provekit/common/src/skyscraper/whir.rs index ab6139d1..0845f873 100644 --- a/provekit/common/src/skyscraper/whir.rs +++ b/provekit/common/src/skyscraper/whir.rs @@ -137,7 +137,7 @@ impl Hasher for SkyscraperHasher { // skyscraper::reference::compress_many(input, out_bytes); // skyscraper::v1::compress_many(input, out_bytes); // skyscraper::block3::compress_many(input, out_bytes); - skyscraper::block4::compress_many(input, out_bytes); - // skyscraper::simple::compress_many(input, out_bytes); + // skyscraper::block4::compress_many(input, out_bytes); + skyscraper::simple::compress_many(input, out_bytes); } } \ No newline at end of file diff --git a/provekit/prover/src/whir_r1cs.rs b/provekit/prover/src/whir_r1cs.rs index 33f6e428..1af98948 100644 --- a/provekit/prover/src/whir_r1cs.rs +++ b/provekit/prover/src/whir_r1cs.rs @@ -438,7 +438,7 @@ pub fn run_zk_whir_pcs_prover( let prover = Prover::new(params.clone()); let (randomness, deferred) = prover - .prove(&mut merlin, statement, witness) + .prove(&mut merlin, statement, witness, construct_skyscraper) .expect("WHIR prover failed to generate a proof"); (merlin, randomness, deferred) diff --git a/provekit/r1cs-compiler/src/whir_r1cs.rs b/provekit/r1cs-compiler/src/whir_r1cs.rs index 30eccb03..e053284c 100644 --- a/provekit/r1cs-compiler/src/whir_r1cs.rs +++ b/provekit/r1cs-compiler/src/whir_r1cs.rs @@ -1,11 +1,14 @@ use { - provekit_common::{utils::next_power_of_two, WhirConfig, WhirR1CSScheme, R1CS}, - whir::parameters::{ - default_max_pow, DeduplicationStrategy, FoldingFactor, MerkleProofStrategy, + provekit_common::{skyscraper::SkyscraperHasher, utils::next_power_of_two, WhirConfig, WhirR1CSScheme, R1CS}, + whir::{merkle_tree::{Hasher, Hashers}, parameters::{ + default_max_pow, DeduplicationStrategy, FoldingFactor, MultivariateParameters, ProtocolParameters, SoundnessType, - }, + }}, }; +pub fn construct_skyscraper() -> Box { + Box::new(SkyscraperHasher::new()) +} pub trait WhirR1CSSchemeBuilder { fn new_for_r1cs(r1cs: &R1CS) -> Self; @@ -50,7 +53,7 @@ impl WhirR1CSSchemeBuilder for WhirR1CSScheme { starting_log_inv_rate: 1, batch_size, deduplication_strategy: DeduplicationStrategy::Disabled, - merkle_proof_strategy: MerkleProofStrategy::Uncompressed, + merkle_runtime_config: Hashers::Skyscraper2, }; WhirConfig::new(mv_params, whir_params) } diff --git a/provekit/verifier/src/whir_r1cs.rs b/provekit/verifier/src/whir_r1cs.rs index e56bbb6b..2e83ff6e 100644 --- a/provekit/verifier/src/whir_r1cs.rs +++ b/provekit/verifier/src/whir_r1cs.rs @@ -2,7 +2,7 @@ use { anyhow::{ensure, Context, Result}, ark_std::{One, Zero}, provekit_common::{ - skyscraper::SkyscraperSponge, + skyscraper::{SkyscraperHasher, SkyscraperSponge}, utils::sumcheck::{calculate_eq, eval_cubic_poly}, FieldElement, WhirConfig, WhirR1CSProof, WhirR1CSScheme, }, @@ -12,13 +12,12 @@ use { }, tracing::instrument, whir::{ - poly_utils::{evals::EvaluationsList, multilinear::MultilinearPoint}, - whir::{ + merkle_tree::Hasher, poly_utils::{evals::EvaluationsList, multilinear::MultilinearPoint}, whir::{ committer::{reader::ParsedCommitment, CommitmentReader}, statement::{Statement, Weights}, utils::HintDeserialize, verifier::Verifier, - }, + } }, }; @@ -32,6 +31,10 @@ pub trait WhirR1CSVerifier { fn verify(&self, proof: &WhirR1CSProof) -> Result<()>; } +pub fn construct_skyscraper() -> Box { + Box::new(SkyscraperHasher::new()) +} + impl WhirR1CSVerifier for WhirR1CSScheme { #[instrument(skip_all)] #[allow(unused)] // TODO: Fix implementation @@ -43,6 +46,7 @@ impl WhirR1CSVerifier for WhirR1CSScheme { let commitment_reader = CommitmentReader::new(&self.whir_witness); let parsed_commitment = commitment_reader.parse_commitment(&mut arthur).unwrap(); + println!("parsed_commitment.root : {:?}", parsed_commitment.root); let data_from_sumcheck_verifier = run_sumcheck_verifier( &mut arthur, self.m_0, @@ -119,6 +123,8 @@ pub fn run_sumcheck_verifier( let commitment_reader = CommitmentReader::new(whir_for_spartan_blinding_config); let parsed_commitment = commitment_reader.parse_commitment(arthur).unwrap(); + println!("parsed_commitment.root : {:?}", parsed_commitment.root); + let mut sum_g_buf = [FieldElement::zero()]; arthur.fill_next_scalars(&mut sum_g_buf)?; @@ -180,8 +186,9 @@ pub fn run_whir_pcs_verifier( ) -> Result<(MultilinearPoint, Vec)> { let verifier = Verifier::new(params); + println!("parsed_commitmen.root : {:?}", parsed_commitment.root); let (folding_randomness, deferred) = verifier - .verify(arthur, parsed_commitment, statement_verifier) + .verify(arthur, parsed_commitment, statement_verifier, construct_skyscraper) .context("while verifying WHIR")?; Ok((folding_randomness, deferred)) From dd911300c4e874a7384e599da84b50eb04d5aed9 Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Tue, 4 Nov 2025 10:55:22 +0100 Subject: [PATCH 03/12] recursive new merkle tree lib --- provekit/verifier/src/whir_r1cs.rs | 4 - recursive-verifier/app/circuit/circuit.go | 5 +- recursive-verifier/app/circuit/common.go | 223 +++++++++++- .../app/circuit/matrix_evaluation.go | 2 +- recursive-verifier/app/circuit/mt.go | 4 +- recursive-verifier/app/circuit/skyscraper2.go | 319 ++++++++++++++++++ recursive-verifier/app/circuit/types.go | 14 +- recursive-verifier/app/circuit/utilities.go | 6 +- recursive-verifier/app/circuit/whir.go | 160 +-------- .../app/circuit/whir_utilities.go | 23 -- .../app/keccakSponge/keccakSponge.go | 82 ----- tooling/provekit-gnark/src/gnark_config.rs | 3 - 12 files changed, 557 insertions(+), 288 deletions(-) create mode 100644 recursive-verifier/app/circuit/skyscraper2.go delete mode 100644 recursive-verifier/app/keccakSponge/keccakSponge.go diff --git a/provekit/verifier/src/whir_r1cs.rs b/provekit/verifier/src/whir_r1cs.rs index 2e83ff6e..656e2b42 100644 --- a/provekit/verifier/src/whir_r1cs.rs +++ b/provekit/verifier/src/whir_r1cs.rs @@ -46,7 +46,6 @@ impl WhirR1CSVerifier for WhirR1CSScheme { let commitment_reader = CommitmentReader::new(&self.whir_witness); let parsed_commitment = commitment_reader.parse_commitment(&mut arthur).unwrap(); - println!("parsed_commitment.root : {:?}", parsed_commitment.root); let data_from_sumcheck_verifier = run_sumcheck_verifier( &mut arthur, self.m_0, @@ -123,8 +122,6 @@ pub fn run_sumcheck_verifier( let commitment_reader = CommitmentReader::new(whir_for_spartan_blinding_config); let parsed_commitment = commitment_reader.parse_commitment(arthur).unwrap(); - println!("parsed_commitment.root : {:?}", parsed_commitment.root); - let mut sum_g_buf = [FieldElement::zero()]; arthur.fill_next_scalars(&mut sum_g_buf)?; @@ -186,7 +183,6 @@ pub fn run_whir_pcs_verifier( ) -> Result<(MultilinearPoint, Vec)> { let verifier = Verifier::new(params); - println!("parsed_commitmen.root : {:?}", parsed_commitment.root); let (folding_randomness, deferred) = verifier .verify(arthur, parsed_commitment, statement_verifier, construct_skyscraper) .context("while verifying WHIR")?; diff --git a/recursive-verifier/app/circuit/circuit.go b/recursive-verifier/app/circuit/circuit.go index 35bd1f9c..ff3a1e63 100644 --- a/recursive-verifier/app/circuit/circuit.go +++ b/recursive-verifier/app/circuit/circuit.go @@ -64,7 +64,6 @@ func (circuit *Circuit) Define(api frontend.API) error { } whirFoldingRandomness, err := RunZKWhir(api, arthur, uapi, sc, circuit.WitnessMerkle, circuit.WitnessFirstRound, circuit.WHIRParamsWitness, [][]frontend.Variable{circuit.WitnessClaimedEvaluations, circuit.WitnessBlindingEvaluations}, circuit.WitnessLinearStatementEvaluations, batchingRandomness, initialOODQueries, initialOODAnswers, rootHash) - if err != nil { return err } @@ -84,8 +83,8 @@ func (circuit *Circuit) Define(api frontend.API) error { func verifyCircuit( deferred []Fp256, cfg Config, hints Hints, pk *groth16.ProvingKey, vk *groth16.VerifyingKey, outputCcsPath string, claimedEvaluations ClaimedEvaluations, internedR1CS R1CS, interner Interner, ) error { - transcriptT := make([]uints.U8, cfg.TranscriptLen) - contTranscript := make([]uints.U8, cfg.TranscriptLen) + transcriptT := make([]uints.U8, len(cfg.Transcript)) + contTranscript := make([]uints.U8, len(cfg.Transcript)) for i := range cfg.Transcript { transcriptT[i] = uints.NewU8(cfg.Transcript[i]) diff --git a/recursive-verifier/app/circuit/common.go b/recursive-verifier/app/circuit/common.go index 5e681dc1..4bb1c548 100644 --- a/recursive-verifier/app/circuit/common.go +++ b/recursive-verifier/app/circuit/common.go @@ -6,12 +6,127 @@ import ( "encoding/hex" "fmt" "log" + "sort" "github.com/consensys/gnark/backend/groth16" gnarkNimue "github.com/reilabs/gnark-nimue" arkSerialize "github.com/reilabs/go-ark-serialize" ) +type IndexPair struct { + depth uint64 + index uint64 +} + +func convertMultiIndexMTProofsToFullMultiPath( + merklePaths []MultiIndexMerkleTreeProof[Digest], + stirAnswers [][][]Fp256, + fullMerklePaths *[]FullMultiPath[Digest], +) { + for mIndex, mp := range merklePaths { + depth := mp.Depth + proofIter := 0 + + currentMPAnswers := stirAnswers[mIndex] + if len(currentMPAnswers) != len(mp.Indices) { + panic(fmt.Sprintf("mismatched stirAnswers (%d) and indices (%d)", len(currentMPAnswers), len(mp.Indices))) + } + + leafHashes := make(map[uint64]Digest) + for i := range mp.Indices { + leafHashes[mp.Indices[i]] = HashLeafData(currentMPAnswers[i]) + } + + uniqueIndices := make(map[uint64]bool) + for _, idx := range mp.Indices { + uniqueIndices[idx] = true + } + + indices := make([]uint64, 0, len(uniqueIndices)) + for idx := range uniqueIndices { + indices = append(indices, idx) + } + sort.Slice(indices, func(i, j int) bool { + return indices[i] < indices[j] + }) + + treeElements := make(map[IndexPair]Digest, len(indices)) + + for d := depth; d > 0; d-- { + nextIndices := make([]uint64, 0, len(indices)) + + i := 0 + for i < len(indices) { + idx := indices[i] + var node Digest + if d == depth { + node = leafHashes[idx] + } else { + node = treeElements[IndexPair{depth: d, index: idx}] + } + + if idx%2 == 0 { + treeElements[IndexPair{depth: d, index: idx}] = node + if i+1 < len(indices) && indices[i+1] == idx+1 { + var otherNode Digest + if d == depth { + otherNode = leafHashes[idx+1] + } else { + otherNode = treeElements[IndexPair{depth: d, index: idx + 1}] + } + parentHash := HashTwoDigests(node, otherNode) + treeElements[IndexPair{depth: d, index: idx + 1}] = otherNode + treeElements[IndexPair{depth: d - 1, index: idx / 2}] = parentHash + nextIndices = append(nextIndices, idx/2) + i += 2 + } else { + // missing right sibling → from proof + if proofIter >= len(mp.Proof) { + panic("insufficient siblings") + } + sib := mp.Proof[proofIter] + treeElements[IndexPair{depth: d, index: idx + 1}] = sib + treeElements[IndexPair{depth: d - 1, index: idx / 2}] = HashTwoDigests(node, sib) + proofIter++ + nextIndices = append(nextIndices, idx/2) + i++ + } + } else { + // right child + if proofIter >= len(mp.Proof) { + panic("insufficient siblings") + } + sib := mp.Proof[proofIter] + treeElements[IndexPair{depth: d, index: idx - 1}] = sib + treeElements[IndexPair{depth: d - 1, index: idx / 2}] = HashTwoDigests(sib, node) + + proofIter++ + nextIndices = append(nextIndices, idx/2) + i++ + } + } + indices = nextIndices + } + + var paths []Path[Digest] + for _, origIdx := range mp.Indices { + leafSibling, authPath, err := ExtractAuthPath(treeElements, origIdx, depth) + if err != nil { + panic(fmt.Sprintf("failed to extract auth path for index %d: %v", origIdx, err)) + } + + paths = append(paths, Path[Digest]{ + LeafHash: leafHashes[origIdx], + LeafIndex: origIdx, + LeafSiblingHash: leafSibling, + AuthPath: authPath, + }) + } + + *fullMerklePaths = append(*fullMerklePaths, FullMultiPath[Digest]{Proofs: paths}) + } +} + func PrepareAndVerifyCircuit(config Config, r1cs R1CS, pk *groth16.ProvingKey, vk *groth16.VerifyingKey, outputCcsPath string) error { io := gnarkNimue.IOPattern{} err := io.Parse([]byte(config.IOPattern)) @@ -22,7 +137,7 @@ func PrepareAndVerifyCircuit(config Config, r1cs R1CS, pk *groth16.ProvingKey, v var pointer uint64 var truncated []byte - var merklePaths []FullMultiPath[KeccakDigest] + var merklePaths []MultiIndexMerkleTreeProof[Digest] var stirAnswers [][][]Fp256 var deferred []Fp256 var claimedEvaluations ClaimedEvaluations @@ -43,7 +158,7 @@ func PrepareAndVerifyCircuit(config Config, r1cs R1CS, pk *groth16.ProvingKey, v switch string(op.Label) { case "merkle_proof": - var path FullMultiPath[KeccakDigest] + var path MultiIndexMerkleTreeProof[Digest] _, err = arkSerialize.CanonicalDeserializeWithMode( bytes.NewReader(config.Transcript[start:end]), &path, @@ -119,14 +234,17 @@ func PrepareAndVerifyCircuit(config Config, r1cs R1CS, pk *groth16.ProvingKey, v return fmt.Errorf("failed to deserialize interner: %w", err) } - var hidingSpartanData = consumeWhirData(config.WHIRConfigHidingSpartan, &merklePaths, &stirAnswers) + var fullMerklePaths []FullMultiPath[Digest] + convertMultiIndexMTProofsToFullMultiPath(merklePaths, stirAnswers, &fullMerklePaths) + var hidingSpartanData = consumeWhirData(config.WHIRConfigHidingSpartan, &fullMerklePaths, &stirAnswers) - var witnessData = consumeWhirData(config.WHIRConfigWitness, &merklePaths, &stirAnswers) + var witnessData = consumeWhirData(config.WHIRConfigWitness, &fullMerklePaths, &stirAnswers) hints := Hints{ witnessHints: witnessData, spartanHidingHint: hidingSpartanData, } + err = verifyCircuit(deferred, config, hints, pk, vk, outputCcsPath, claimedEvaluations, r1cs, interner) if err != nil { return fmt.Errorf("verification failed: %w", err) @@ -178,3 +296,100 @@ func GetR1csFromUrl(r1csUrl string) ([]byte, error) { log.Printf("Successfully downloaded") return r1csFile, nil } + +func ExtractAuthPath( + treeElements map[IndexPair]Digest, + leafIndex uint64, + depth uint64, +) (leafSiblingHash Digest, authPath []Digest, err error) { + leafSiblingIdx := leafIndex ^ 1 + leafSibling, ok := treeElements[IndexPair{depth: depth, index: leafSiblingIdx}] + if !ok { + return Digest{}, nil, fmt.Errorf("missing leaf sibling at depth=%d, index=%d", depth, leafSiblingIdx) + } + + authPath = make([]Digest, 0, depth-1) + currentIdx := leafIndex + + for d := depth - 1; d >= 1; d-- { + parentIdx := currentIdx / 2 + siblingIdx := parentIdx ^ 1 + + sibling, ok := treeElements[IndexPair{depth: d, index: siblingIdx}] + if !ok { + return Digest{}, nil, fmt.Errorf("missing sibling at depth=%d, index=%d (parent=%d)", d, siblingIdx, parentIdx) + } + + authPath = append(authPath, sibling) + currentIdx = parentIdx + } + + return leafSibling, authPath, nil +} + +func VerifyAuthPath( + leafHash Digest, + leafSiblingHash Digest, + authPath []Digest, + leafIndex uint64, + depth uint64, + expectedRoot Digest, +) error { + var currentHash Digest + if leafIndex%2 == 0 { + currentHash = HashTwoDigests(leafHash, leafSiblingHash) + } else { + currentHash = HashTwoDigests(leafSiblingHash, leafHash) + } + + currentIdx := leafIndex + for level := 0; level < len(authPath); level++ { + parentIdx := currentIdx / 2 + sibling := authPath[level] + if parentIdx%2 == 0 { + currentHash = HashTwoDigests(currentHash, sibling) + } else { + currentHash = HashTwoDigests(sibling, currentHash) + } + currentIdx = parentIdx + } + + if currentHash != expectedRoot { + return fmt.Errorf("root mismatch: got %x, expected %x", currentHash, expectedRoot) + } + + return nil +} + +func TestExtractAndVerifyAuthPaths( + treeElements map[IndexPair]Digest, + leafHashes map[uint64]Digest, + indices []uint64, + depth uint64, +) error { + root, ok := treeElements[IndexPair{depth: 0, index: 0}] + if !ok { + return fmt.Errorf("root not found in treeElements") + } + + for _, idx := range indices { + leafSibling, authPath, err := ExtractAuthPath(treeElements, idx, depth) + if err != nil { + return fmt.Errorf("failed to extract auth path for index %d: %w", idx, err) + } + + leafHash, ok := leafHashes[idx] + if !ok { + return fmt.Errorf("leaf hash not found for index %d", idx) + } + + err = VerifyAuthPath(leafHash, leafSibling, authPath, idx, depth, root) + if err != nil { + return fmt.Errorf("failed to verify auth path for index %d: %w", idx, err) + } + + fmt.Printf("✓ Index %d: verified successfully (auth path length: %d)\n", idx, len(authPath)) + } + + return nil +} diff --git a/recursive-verifier/app/circuit/matrix_evaluation.go b/recursive-verifier/app/circuit/matrix_evaluation.go index adaa8466..3dedfa23 100644 --- a/recursive-verifier/app/circuit/matrix_evaluation.go +++ b/recursive-verifier/app/circuit/matrix_evaluation.go @@ -46,7 +46,7 @@ func evaluateR1CSMatrixExtension(api frontend.API, circuit *Circuit, rowRand []f rowEval := calculateEQOverBooleanHypercube(api, rowRand) colEval := calculateEQOverBooleanHypercube(api, colRand) - for i := range len(circuit.MatrixA) { + for i := range circuit.MatrixA { ansA = api.Add(ansA, api.Mul(circuit.MatrixA[i].value, api.Mul(rowEval[circuit.MatrixA[i].row], colEval[circuit.MatrixA[i].column]))) } for i := range circuit.MatrixB { diff --git a/recursive-verifier/app/circuit/mt.go b/recursive-verifier/app/circuit/mt.go index 4930c399..cbcee5f3 100644 --- a/recursive-verifier/app/circuit/mt.go +++ b/recursive-verifier/app/circuit/mt.go @@ -37,11 +37,11 @@ func newMerkle( for z := range treeHeight { totalAuthPath[i][j][z] = typeConverters. - LittleEndianUint8ToBigInt(proof.AuthPath[treeHeight-1-z].KeccakDigest[:]) + LittleEndianUint8ToBigInt(proof.AuthPath[z].Digest[:]) } totalLeafSiblingHashes[i][j] = typeConverters. - LittleEndianUint8ToBigInt(proof.LeafSiblingHash.KeccakDigest[:]) + LittleEndianUint8ToBigInt(proof.LeafSiblingHash.Digest[:]) totalLeafIndexes[i][j] = uints.NewU64(proof.LeafIndex) for k := range hint.stirAnswers[i][j] { diff --git a/recursive-verifier/app/circuit/skyscraper2.go b/recursive-verifier/app/circuit/skyscraper2.go new file mode 100644 index 00000000..9fd16a9e --- /dev/null +++ b/recursive-verifier/app/circuit/skyscraper2.go @@ -0,0 +1,319 @@ +package circuit + +import ( + "math/big" +) + +// BN254 scalar field modulus +var bn254Modulus = new(big.Int) + +// SIGMA_INV constant for Skyscraper +var sigmaInv = new(big.Int) + +// Round constants for Skyscraper +var roundConstants [18]*big.Int + +func init() { + // BN254 modulus: 21888242871839275222246405745257275088548364400416034343698204186575808495617 + bn254Modulus.SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10) + + // SIGMA_INV: 9915499612839321149637521777990102151350674507940716049588462388200839649614 + sigmaInv.SetString("9915499612839321149637521777990102151350674507940716049588462388200839649614", 10) + + // Initialize round constants from the Rust implementation + rcHex := [][4]uint64{ + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x903c4324270bd744, 0x873125f708a7d269, 0x081dd27906c83855, 0x276b1823ea6d7667}, + {0x7ac8edbb4b378d71, 0xe29d79f3d99e2cb7, 0x751417914c1a5a18, 0x0cf02bd758a484a6}, + {0xfa7adc6769e5bc36, 0x1c3f8e297cca387d, 0x0eb7730d63481db0, 0x25b0e03f18ede544}, + {0x57847e652f03cfb7, 0x33440b9668873404, 0x955a32e849af80bc, 0x002882fcbe14ae70}, + {0x979231396257d4d7, 0x29989c3e1b37d3c1, 0x12ef02b47f1277ba, 0x039ad8571e2b7a9c}, + {0xb5b48465abbb7887, 0xa72a6bc5e6ba2d2b, 0x4cd48043712f7b29, 0x1142d5410fc1fc1a}, + {0x7ab2c156059075d3, 0x17cb3594047999b2, 0x44f2c93598f289f7, 0x1d78439f69bc0bec}, + {0x05d7a965138b8edb, 0x36ef35a3d55c48b1, 0x8ddfb8a1ac6f1628, 0x258588a508f4ff82}, + {0x1596fb9afccb49e9, 0x9a7367d69a09a95b, 0x9bc43f6984e4c157, 0x13087879d2f514fe}, + {0x295ccd233b4109fa, 0xe1d72f89ed868012, 0x2e9e1eea4bc88a8e, 0x17dadee898c45232}, + {0x9a8590b4aa1f486f, 0xb75834b430e9130e, 0xb8e90b1034d5de31, 0x295c6d1546e7f4a6}, + {0x850adcb74c6eb892, 0x07699ef305b92fc3, 0x4ef96a2ba1720f2d, 0x1288ca0e1d3ed446}, + {0x01960f9349d1b5ee, 0x8ccad30769371c69, 0xe5c81e8991c98662, 0x17563b4d1ae023f3}, + {0x6ba01e9476b32917, 0xa1cb0a3add977bc9, 0x86815a945815f030, 0x2869043be91a1eea}, + {0x81776c885511d976, 0x7475d34f47f414e7, 0x5d090056095d96cf, 0x14941f0aff59e79a}, + {0xbc40b4fd8fc8c034, 0xbb7142c3cce4fd48, 0x318356758a39005a, 0x1ce337a190f4379f}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + } + + for i := range rcHex { + roundConstants[i] = limbsToBigInt(rcHex[i]) + } +} + +// Convert [4]uint64 limbs to big.Int (little-endian) +func limbsToBigInt(limbs [4]uint64) *big.Int { + result := new(big.Int) + + // limbs[0] + limbs[1]<<64 + limbs[2]<<128 + limbs[3]<<192 + result.SetUint64(limbs[0]) + + temp := new(big.Int) + temp.SetUint64(limbs[1]) + temp.Lsh(temp, 64) + result.Add(result, temp) + + temp.SetUint64(limbs[2]) + temp.Lsh(temp, 128) + result.Add(result, temp) + + temp.SetUint64(limbs[3]) + temp.Lsh(temp, 192) + result.Add(result, temp) + + return result.Mod(result, bn254Modulus) +} + +// Convert Fp256 to big.Int +func fp256ToBigInt(fp Fp256) *big.Int { + return limbsToBigInt(fp.Limbs) +} + +// Convert big.Int to [4]uint64 limbs +func bigIntToLimbs(val *big.Int) [4]uint64 { + // Ensure value is positive and reduced + v := new(big.Int).Set(val) + v.Mod(v, bn254Modulus) + + limbs := [4]uint64{} + mask := new(big.Int).SetUint64(0xFFFFFFFFFFFFFFFF) + + for i := 0; i < 4; i++ { + temp := new(big.Int).And(v, mask) + limbs[i] = temp.Uint64() + v.Rsh(v, 64) + } + + return limbs +} + +// Field modular addition +func fieldAdd(a, b *big.Int) *big.Int { + result := new(big.Int).Add(a, b) + return result.Mod(result, bn254Modulus) +} + +// Field modular multiplication +func fieldMul(a, b *big.Int) *big.Int { + result := new(big.Int).Mul(a, b) + return result.Mod(result, bn254Modulus) +} + +// Field squaring +func fieldSquare(a *big.Int) *big.Int { + return fieldMul(a, a) +} + +// S-box function for bar operation +func sbox(v byte) byte { + notV := ^v + rotLeft1 := (notV << 1) | (notV >> 7) + rotLeft2 := (v << 2) | (v >> 6) + rotLeft3 := (v << 3) | (v >> 5) + + xor := v ^ (rotLeft1 & rotLeft2 & rotLeft3) + return (xor << 1) | (xor >> 7) // rotate left by 1 +} + +// Bar function: cyclic rotate bytes + s-box +func bar(x *big.Int) *big.Int { + // Convert to 32 bytes (little-endian) + bytes := make([]byte, 32) + xBytes := x.Bytes() + + // x.Bytes() returns big-endian, we need little-endian + for i := 0; i < len(xBytes) && i < 32; i++ { + bytes[i] = xBytes[len(xBytes)-1-i] + } + + // Cyclic rotate by 16 bytes + rotated := make([]byte, 32) + copy(rotated[:16], bytes[16:]) + copy(rotated[16:], bytes[:16]) + + // Apply s-box to each byte + for i := range rotated { + rotated[i] = sbox(rotated[i]) + } + + // Convert back to big.Int (little-endian) + result := new(big.Int) + for i := 31; i >= 0; i-- { + result.Lsh(result, 8) + result.Or(result, new(big.Int).SetUint64(uint64(rotated[i]))) + } + + return result.Mod(result, bn254Modulus) +} + +// SS round (square-sigma round) +func ss(round int, l, r *big.Int) (*big.Int, *big.Int) { + // r += l^2 * sigma_inv + rc[round] + lSquared := fieldSquare(l) + term := fieldMul(lSquared, sigmaInv) + term = fieldAdd(term, roundConstants[round]) + r = fieldAdd(r, term) + + // swap(l, r) + l, r = r, l + + // r += l^2 * sigma_inv + rc[round+1] + lSquared = fieldSquare(l) + term = fieldMul(lSquared, sigmaInv) + term = fieldAdd(term, roundConstants[round+1]) + r = fieldAdd(r, term) + + // swap(l, r) + l, r = r, l + + return l, r +} + +// BB round (bar-bar round) +func bb(round int, l, r *big.Int) (*big.Int, *big.Int) { + // r += bar(l) + rc[round] + barL := bar(l) + r = fieldAdd(r, barL) + r = fieldAdd(r, roundConstants[round]) + + // swap(l, r) + l, r = r, l + + // r += bar(l) + rc[round+1] + barL = bar(l) + r = fieldAdd(r, barL) + r = fieldAdd(r, roundConstants[round+1]) + + // swap(l, r) + l, r = r, l + + return l, r +} + +// Permute function: 9 rounds of alternating ss and bb +func permute(l, r *big.Int) (*big.Int, *big.Int) { + l, r = ss(0, l, r) + l, r = ss(2, l, r) + l, r = ss(4, l, r) + l, r = bb(6, l, r) + l, r = ss(8, l, r) + l, r = bb(10, l, r) + l, r = ss(12, l, r) + l, r = ss(14, l, r) + l, r = ss(16, l, r) + return l, r +} + +// SkyscraperCompress: Main compression function +// Takes two field elements (as [4]uint64 limbs) and returns compressed hash +func SkyscraperCompress(left, right [4]uint64) [4]uint64 { + l := limbsToBigInt(left) + r := limbsToBigInt(right) + + t := new(big.Int).Set(l) + l, _ = permute(l, r) + result := fieldAdd(l, t) + + return bigIntToLimbs(result) +} + +// SkyscraperCompressFp256: Compress two Fp256 field elements +func SkyscraperCompressFp256(left, right Fp256) Digest { + leftLimbs := left.Limbs + rightLimbs := right.Limbs + + resultLimbs := SkyscraperCompress(leftLimbs, rightLimbs) + + // Convert result limbs to Digest (32 bytes, little-endian) + var digest Digest + for i := 0; i < 4; i++ { + limb := resultLimbs[i] + for j := 0; j < 8; j++ { + digest.Digest[i*8+j] = byte(limb & 0xFF) + limb >>= 8 + } + } + + return digest +} + +// HashLeafData: Hash multiple Fp256 elements to create a leaf digest +// This iteratively compresses pairs of elements +func HashLeafData(leafData []Fp256) Digest { + if len(leafData) == 0 { + panic("Cannot hash empty leaf data") + } + + // Start with first element + currentLimbs := leafData[0].Limbs + // Iteratively compress with remaining elements + for i := 1; i < len(leafData); i++ { + currentLimbs = SkyscraperCompress(currentLimbs, leafData[i].Limbs) + } + + var digest Digest + // Convert to Digest + for i := 0; i < 4; i++ { + limb := currentLimbs[i] + for j := 0; j < 8; j++ { + digest.Digest[i*8+j] = byte(limb & 0xFF) + limb >>= 8 + } + } + + return digest +} + +// HashTwoDigests: Hash two digests together +func HashTwoDigests(left, right Digest) Digest { + // Convert digests to [4]uint64 limbs (little-endian) + leftLimbs := [4]uint64{} + rightLimbs := [4]uint64{} + + for i := 0; i < 4; i++ { + var limb uint64 + for j := 0; j < 8; j++ { + limb |= uint64(left.Digest[i*8+j]) << (8 * j) + } + leftLimbs[i] = limb + } + + for i := 0; i < 4; i++ { + var limb uint64 + for j := 0; j < 8; j++ { + limb |= uint64(right.Digest[i*8+j]) << (8 * j) + } + rightLimbs[i] = limb + } + + resultLimbs := SkyscraperCompress(leftLimbs, rightLimbs) + + var digest Digest + for i := 0; i < 4; i++ { + limb := resultLimbs[i] + for j := 0; j < 8; j++ { + digest.Digest[i*8+j] = byte(limb & 0xFF) + limb >>= 8 + } + } + + return digest +} + +func DigestToFieldElement(d Digest) *big.Int { + var result = new(big.Int) + + for i := 31; i >= 0; i-- { + result.Lsh(result, 8) + result.Add(result, big.NewInt(int64(d.Digest[i]))) + } + + result.Mod(result, bn254Modulus) + return result +} diff --git a/recursive-verifier/app/circuit/types.go b/recursive-verifier/app/circuit/types.go index 67bc53b4..64eb20f9 100644 --- a/recursive-verifier/app/circuit/types.go +++ b/recursive-verifier/app/circuit/types.go @@ -6,8 +6,8 @@ import ( ) // Common types -type KeccakDigest struct { - KeccakDigest [32]uint8 +type Digest struct { + Digest [32]uint8 } type Fp256 struct { @@ -15,6 +15,7 @@ type Fp256 struct { } type Path[Digest any] struct { + LeafHash Digest LeafSiblingHash Digest AuthPath []Digest LeafIndex uint64 @@ -24,6 +25,12 @@ type FullMultiPath[Digest any] struct { Proofs []Path[Digest] } +type MultiIndexMerkleTreeProof[Digest any] struct { + Depth uint64 + Indices []uint64 + Proof []Digest +} + // WHIR specific types type WHIRConfig struct { NRounds int `json:"n_rounds"` @@ -96,7 +103,6 @@ type Config struct { LogANumTerms int `json:"log_a_num_terms"` IOPattern string `json:"io_pattern"` Transcript []byte `json:"transcript"` - TranscriptLen int `json:"transcript_len"` WitnessStatementEvaluations []string `json:"witness_statement_evaluations"` BlindingStatementEvaluations []string `json:"blinding_statement_evaluations"` } @@ -107,7 +113,7 @@ type Hints struct { } type Hint struct { - merklePaths []FullMultiPath[KeccakDigest] + merklePaths []FullMultiPath[Digest] stirAnswers [][][]Fp256 } diff --git a/recursive-verifier/app/circuit/utilities.go b/recursive-verifier/app/circuit/utilities.go index 65f76d69..38bc3ff3 100644 --- a/recursive-verifier/app/circuit/utilities.go +++ b/recursive-verifier/app/circuit/utilities.go @@ -242,7 +242,7 @@ func consumeFront[T any](slice *[]T) T { return head } -func consumeWhirData(whirConfig WHIRConfig, merkle_paths *[]FullMultiPath[KeccakDigest], stir_answers *[][][]Fp256) ZKHint { +func consumeWhirData(whirConfig WHIRConfig, merkle_paths *[]FullMultiPath[Digest], stir_answers *[][][]Fp256) ZKHint { var zkHint ZKHint if len(*merkle_paths) > 0 && len(*stir_answers) > 0 { @@ -251,7 +251,7 @@ func consumeWhirData(whirConfig WHIRConfig, merkle_paths *[]FullMultiPath[Keccak zkHint.firstRoundMerklePaths = FirstRoundHint{ path: Hint{ - merklePaths: []FullMultiPath[KeccakDigest]{firstRoundMerklePath}, + merklePaths: []FullMultiPath[Digest]{firstRoundMerklePath}, stirAnswers: [][][]Fp256{firstRoundStirAnswers}, }, expectedStirAnswers: firstRoundStirAnswers, @@ -260,7 +260,7 @@ func consumeWhirData(whirConfig WHIRConfig, merkle_paths *[]FullMultiPath[Keccak expectedRounds := whirConfig.NRounds - var remainingMerklePaths []FullMultiPath[KeccakDigest] + var remainingMerklePaths []FullMultiPath[Digest] var remainingStirAnswers [][][]Fp256 for i := 0; i < expectedRounds && len(*merkle_paths) > 0 && len(*stir_answers) > 0; i++ { diff --git a/recursive-verifier/app/circuit/whir.go b/recursive-verifier/app/circuit/whir.go index b43ec3ad..82e450be 100644 --- a/recursive-verifier/app/circuit/whir.go +++ b/recursive-verifier/app/circuit/whir.go @@ -65,34 +65,14 @@ func RunZKWhir( ) (totalFoldingRandomness []frontend.Variable, err error) { initialOODs := oodAnswers(api, initialOODAnswers, batchingRandomness) - // batchSizeLen := whirParams.BatchSize initialSumcheckData, lastEval, initialSumcheckFoldingRandomness, err := initialSumcheck(api, arthur, batchingRandomness, initialOODQueries, initialOODs, whirParams, linearStatementEvaluations) if err != nil { return } - copyOfFirstLeaves := make([][][]frontend.Variable, len(firstRound.Leaves)) - for i := range len(firstRound.Leaves) { - copyOfFirstLeaves[i] = make([][]frontend.Variable, len(firstRound.Leaves[i])) - for j := range len(firstRound.Leaves[i]) { - copyOfFirstLeaves[i][j] = make([]frontend.Variable, len(firstRound.Leaves[i][j])) - for k := range len(firstRound.Leaves[i][j]) { - copyOfFirstLeaves[i][j][k] = firstRound.Leaves[i][j][k] - } - } - } - - roundAnswers := make([][][]frontend.Variable, len(circuit.Leaves)+1) - foldSize := 1 << whirParams.FoldingFactorArray[0] collapsed := rlcBatchedLeaves(api, firstRound.Leaves[0], foldSize, whirParams.BatchSize, batchingRandomness) - roundAnswers[0] = collapsed - - for i := range len(circuit.Leaves) { - roundAnswers[i+1] = circuit.Leaves[i] - } - computedFold := computeFold(collapsed, initialSumcheckFoldingRandomness, api) mainRoundData := generateEmptyMainRoundData(whirParams) @@ -143,7 +123,7 @@ func RunZKWhir( if err != nil { return } - err = verifyMerkleTreeProofs(api, uapi, sc, circuit.LeafIndexes[r-1], roundAnswers[r], circuit.LeafSiblingHashes[r-1], circuit.AuthPaths[r-1], rootHashList[r-1]) + err = verifyMerkleTreeProofs(api, uapi, sc, circuit.LeafIndexes[r-1], circuit.Leaves[r-1], circuit.LeafSiblingHashes[r-1], circuit.AuthPaths[r-1], rootHashList[r-1]) if err != nil { return } @@ -217,144 +197,6 @@ func RunZKWhir( return totalFoldingRandomness, nil } -//nolint:unused -func runWhir( - api frontend.API, - arthur gnarkNimue.Arthur, - uapi *uints.BinaryField[uints.U64], - sc *skyscraper.Skyscraper, - circuit Merkle, - whirParams WHIRParams, - linearStatementEvaluations []frontend.Variable, - linearStatementValuesAtPoints []frontend.Variable, -) (totalFoldingRandomness []frontend.Variable, err error) { - if err = fillInAndVerifyRootHash(0, api, uapi, sc, circuit, arthur); err != nil { - return - } - - initialOODQueries, initialOODAnswers, tempErr := fillInOODPointsAndAnswers(whirParams.CommittmentOODSamples, arthur) - if tempErr != nil { - err = tempErr - return - } - - initialCombinationRandomness, tempErr := GenerateCombinationRandomness(api, arthur, whirParams.CommittmentOODSamples+len(linearStatementEvaluations)) - if tempErr != nil { - err = tempErr - return - } - - OODAnswersAndStatmentEvaluations := append(initialOODAnswers, linearStatementEvaluations...) - lastEval := utilities.DotProduct(api, initialCombinationRandomness, OODAnswersAndStatmentEvaluations) - - initialSumcheckFoldingRandomness, lastEval, tempErr := runWhirSumcheckRounds(api, lastEval, arthur, whirParams.FoldingFactorArray[0], 3) - if tempErr != nil { - err = tempErr - return - } - - initialData := InitialSumcheckData{ - InitialOODQueries: initialOODQueries, - InitialCombinationRandomness: initialCombinationRandomness, - } - - computedFold := computeFold(circuit.Leaves[0], initialSumcheckFoldingRandomness, api) - - mainRoundData := generateEmptyMainRoundData(whirParams) - - expDomainGenerator := utilities.Exponent(api, uapi, whirParams.StartingDomainBackingDomainGenerator, uints.NewU64(uint64(1<, - /// length of the transcript - pub transcript_len: usize, } #[derive(Debug, Serialize, Deserialize)] @@ -117,7 +115,6 @@ pub fn gnark_parameters( log_a_num_terms: a_num_terms, io_pattern: String::from_utf8(io.as_bytes().to_vec()).unwrap(), transcript: transcript.to_vec(), - transcript_len: transcript.to_vec().len(), } } From d52953148383894fc9bf2b113159d11bbe9c225f Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Mon, 10 Nov 2025 13:27:51 +0100 Subject: [PATCH 04/12] merkle cap, initial implementation --- recursive-verifier/app/circuit/circuit.go | 77 ++++++++++++++++++- recursive-verifier/app/circuit/common.go | 35 ++++++++- recursive-verifier/app/circuit/mt.go | 7 +- recursive-verifier/app/circuit/types.go | 4 +- recursive-verifier/app/circuit/whir.go | 4 +- .../app/circuit/whir_utilities.go | 39 +++++++++- 6 files changed, 155 insertions(+), 11 deletions(-) diff --git a/recursive-verifier/app/circuit/circuit.go b/recursive-verifier/app/circuit/circuit.go index ff3a1e63..d1f44e17 100644 --- a/recursive-verifier/app/circuit/circuit.go +++ b/recursive-verifier/app/circuit/circuit.go @@ -2,6 +2,7 @@ package circuit import ( "log" + "math/bits" "os" "reilabs/whir-verifier-circuit/app/typeConverters" @@ -13,7 +14,9 @@ import ( "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/std/lookup/logderivlookup" "github.com/consensys/gnark/std/math/uints" + skyscraper "github.com/reilabs/gnark-skyscraper" ) type Circuit struct { @@ -40,6 +43,79 @@ type Circuit struct { Transcript []uints.U8 `gnark:",public"` } +func log2(n int) int { + if n <= 0 { + return 0 + } + return bits.Len(uint(n)) - 1 +} + +func verifyCapContainer(api frontend.API, sc *skyscraper.Skyscraper, uapi *uints.BinaryField[uints.U64], capContainer []frontend.Variable, leaves [][]frontend.Variable, leafIndexes []uints.U64, leafSiblingHashes []frontend.Variable, authPaths [][]frontend.Variable) error { + // api.Println(capContainer) + // api.Println(len(capContainer)) + if len(capContainer) > 0 { + for i := ((len(capContainer) / 2) - 1); i > 0; i-- { + supposedHash := sc.CompressV2(capContainer[2*i], capContainer[2*i+1]) + actualHash := api.Select(api.IsZero(capContainer[2*i]), capContainer[i], supposedHash) + api.AssertIsEqual(actualHash, capContainer[i]) + } + } + + capDepth := log2(len(capContainer) / 2) + // api.Println(capDepth) + dedupedLUT := logderivlookup.New(api) + + for i := (len(capContainer) / 2); i < len(capContainer); i++ { + // api.Println(capContainer[i]) + dedupedLUT.Insert(capContainer[i]) + } + + // api.AssertIsEqual(x, searchRes[0]) + + numOfLeavesProved := len(leaves) + // api.Println(numOfLeavesProved) + + for i := range numOfLeavesProved { + treeHeight := len(authPaths[i]) + 1 + leafIndexBits := api.ToBinary(uapi.ToValue(leafIndexes[i]), treeHeight) + + // api.Println(uapi.ToValue(leafIndexes[i])) + // api.Println(1 << treeHeight) + // api.Println(leafIndexBits[treeHeight-capDepth:]) + rootIndex := api.FromBinary(leafIndexBits[treeHeight-capDepth:]...) + // api.Println(rootIndex) + searchRes := dedupedLUT.Lookup(rootIndex) + rootHash := searchRes[0] + // api.Println(rootHash) + + leafSiblingHash := leafSiblingHashes[i] + claimedLeafHash := sc.CompressV2(leaves[i][0], leaves[i][1]) + for x := range len(leaves[i]) - 2 { + claimedLeafHash = sc.CompressV2(claimedLeafHash, leaves[i][x+2]) + } + dir := leafIndexBits[0] + + xLeftChild := api.Select(dir, leafSiblingHash, claimedLeafHash) + xRightChild := api.Select(dir, claimedLeafHash, leafSiblingHash) + + currentHash := sc.CompressV2(xLeftChild, xRightChild) + for level := 1; level < treeHeight-capDepth; level++ { + indexBit := leafIndexBits[level] + + siblingHash := authPaths[i][level-1] + + dir := api.And(indexBit, 1) + left := api.Select(dir, siblingHash, currentHash) + right := api.Select(dir, currentHash, siblingHash) + + currentHash = sc.CompressV2(left, right) + } + // api.Println(currentHash) + api.AssertIsEqual(currentHash, rootHash) + } + return nil +} + func (circuit *Circuit) Define(api frontend.API) error { sc, arthur, uapi, err := initializeComponents(api, circuit) if err != nil { @@ -47,7 +123,6 @@ func (circuit *Circuit) Define(api frontend.API) error { } rootHash, batchingRandomness, initialOODQueries, initialOODAnswers, err := parseBatchedCommitment(arthur, circuit.WHIRParamsWitness) - if err != nil { return err } diff --git a/recursive-verifier/app/circuit/common.go b/recursive-verifier/app/circuit/common.go index 4bb1c548..fdc76eb5 100644 --- a/recursive-verifier/app/circuit/common.go +++ b/recursive-verifier/app/circuit/common.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "fmt" "log" + "math/bits" "sort" "github.com/consensys/gnark/backend/groth16" @@ -52,9 +53,19 @@ func convertMultiIndexMTProofsToFullMultiPath( treeElements := make(map[IndexPair]Digest, len(indices)) + cappedDepth := 0 + cappedDepth = bits.Len(uint(len(mp.Indices))) - 1 + + if cappedDepth >= int(mp.Depth) { + cappedDepth = int(mp.Depth) - 1 + } + capContainer := make([]Digest, 2< 0; d-- { nextIndices := make([]uint64, 0, len(indices)) - + capIndices := make([]uint64, 0, 1< 0 { + for i := ((len(capContainer) / 2) - 1); i > 0; i-- { + supposedHash := sc.CompressV2(capContainer[2*i], capContainer[2*i+1]) + actualHash := api.Select(api.IsZero(capContainer[2*i]), capContainer[i], supposedHash) + api.AssertIsEqual(actualHash, capContainer[i]) + } + } + + capDepth := bits.Len(uint(len(capContainer)/2)) - 1 + // api.Println(capDepth) + dedupedLUT := logderivlookup.New(api) + + for i := (len(capContainer) / 2); i < len(capContainer); i++ { + // api.Println(capContainer[i]) + dedupedLUT.Insert(capContainer[i]) + } + + // api.AssertIsEqual(x, searchRes[0]) + numOfLeavesProved := len(leaves) + // api.Println(numOfLeavesProved) + for i := range numOfLeavesProved { - treeHeight := len(authPaths[i]) + 1 + treeHeight := len(authPaths[i]) + 1 + capDepth leafIndexBits := api.ToBinary(uapi.ToValue(leafIndexes[i]), treeHeight) + + // api.Println(uapi.ToValue(leafIndexes[i])) + // api.Println(1 << treeHeight) + // api.Println(leafIndexBits[treeHeight-capDepth:]) + rootIndex := api.FromBinary(leafIndexBits[treeHeight-capDepth:]...) + // api.Println(rootIndex) + searchRes := dedupedLUT.Lookup(rootIndex) + rootHash := searchRes[0] + // api.Println(rootHash) + leafSiblingHash := leafSiblingHashes[i] claimedLeafHash := sc.CompressV2(leaves[i][0], leaves[i][1]) for x := range len(leaves[i]) - 2 { @@ -26,7 +58,7 @@ func verifyMerkleTreeProofs(api frontend.API, uapi *uints.BinaryField[uints.U64] xRightChild := api.Select(dir, claimedLeafHash, leafSiblingHash) currentHash := sc.CompressV2(xLeftChild, xRightChild) - for level := 1; level < treeHeight; level++ { + for level := 1; level < treeHeight-capDepth; level++ { indexBit := leafIndexBits[level] siblingHash := authPaths[i][level-1] @@ -37,6 +69,7 @@ func verifyMerkleTreeProofs(api frontend.API, uapi *uints.BinaryField[uints.U64] currentHash = sc.CompressV2(left, right) } + // api.Println(currentHash) api.AssertIsEqual(currentHash, rootHash) } return nil From 82b0591fb3e33b153841f0c42de11099760a766b Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Tue, 11 Nov 2025 16:14:37 +0100 Subject: [PATCH 05/12] merkle cap improvements --- recursive-verifier/app/circuit/circuit.go | 76 ------------------- recursive-verifier/app/circuit/common.go | 10 +-- recursive-verifier/app/circuit/mt.go | 8 +- .../app/circuit/whir_utilities.go | 30 +++----- 4 files changed, 21 insertions(+), 103 deletions(-) diff --git a/recursive-verifier/app/circuit/circuit.go b/recursive-verifier/app/circuit/circuit.go index d1f44e17..3c3f6387 100644 --- a/recursive-verifier/app/circuit/circuit.go +++ b/recursive-verifier/app/circuit/circuit.go @@ -2,7 +2,6 @@ package circuit import ( "log" - "math/bits" "os" "reilabs/whir-verifier-circuit/app/typeConverters" @@ -14,9 +13,7 @@ import ( "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" - "github.com/consensys/gnark/std/lookup/logderivlookup" "github.com/consensys/gnark/std/math/uints" - skyscraper "github.com/reilabs/gnark-skyscraper" ) type Circuit struct { @@ -43,79 +40,6 @@ type Circuit struct { Transcript []uints.U8 `gnark:",public"` } -func log2(n int) int { - if n <= 0 { - return 0 - } - return bits.Len(uint(n)) - 1 -} - -func verifyCapContainer(api frontend.API, sc *skyscraper.Skyscraper, uapi *uints.BinaryField[uints.U64], capContainer []frontend.Variable, leaves [][]frontend.Variable, leafIndexes []uints.U64, leafSiblingHashes []frontend.Variable, authPaths [][]frontend.Variable) error { - // api.Println(capContainer) - // api.Println(len(capContainer)) - if len(capContainer) > 0 { - for i := ((len(capContainer) / 2) - 1); i > 0; i-- { - supposedHash := sc.CompressV2(capContainer[2*i], capContainer[2*i+1]) - actualHash := api.Select(api.IsZero(capContainer[2*i]), capContainer[i], supposedHash) - api.AssertIsEqual(actualHash, capContainer[i]) - } - } - - capDepth := log2(len(capContainer) / 2) - // api.Println(capDepth) - dedupedLUT := logderivlookup.New(api) - - for i := (len(capContainer) / 2); i < len(capContainer); i++ { - // api.Println(capContainer[i]) - dedupedLUT.Insert(capContainer[i]) - } - - // api.AssertIsEqual(x, searchRes[0]) - - numOfLeavesProved := len(leaves) - // api.Println(numOfLeavesProved) - - for i := range numOfLeavesProved { - treeHeight := len(authPaths[i]) + 1 - leafIndexBits := api.ToBinary(uapi.ToValue(leafIndexes[i]), treeHeight) - - // api.Println(uapi.ToValue(leafIndexes[i])) - // api.Println(1 << treeHeight) - // api.Println(leafIndexBits[treeHeight-capDepth:]) - rootIndex := api.FromBinary(leafIndexBits[treeHeight-capDepth:]...) - // api.Println(rootIndex) - searchRes := dedupedLUT.Lookup(rootIndex) - rootHash := searchRes[0] - // api.Println(rootHash) - - leafSiblingHash := leafSiblingHashes[i] - claimedLeafHash := sc.CompressV2(leaves[i][0], leaves[i][1]) - for x := range len(leaves[i]) - 2 { - claimedLeafHash = sc.CompressV2(claimedLeafHash, leaves[i][x+2]) - } - dir := leafIndexBits[0] - - xLeftChild := api.Select(dir, leafSiblingHash, claimedLeafHash) - xRightChild := api.Select(dir, claimedLeafHash, leafSiblingHash) - - currentHash := sc.CompressV2(xLeftChild, xRightChild) - for level := 1; level < treeHeight-capDepth; level++ { - indexBit := leafIndexBits[level] - - siblingHash := authPaths[i][level-1] - - dir := api.And(indexBit, 1) - left := api.Select(dir, siblingHash, currentHash) - right := api.Select(dir, currentHash, siblingHash) - - currentHash = sc.CompressV2(left, right) - } - // api.Println(currentHash) - api.AssertIsEqual(currentHash, rootHash) - } - return nil -} - func (circuit *Circuit) Define(api frontend.API) error { sc, arthur, uapi, err := initializeComponents(api, circuit) if err != nil { diff --git a/recursive-verifier/app/circuit/common.go b/recursive-verifier/app/circuit/common.go index fdc76eb5..678980b8 100644 --- a/recursive-verifier/app/circuit/common.go +++ b/recursive-verifier/app/circuit/common.go @@ -53,10 +53,10 @@ func convertMultiIndexMTProofsToFullMultiPath( treeElements := make(map[IndexPair]Digest, len(indices)) - cappedDepth := 0 - cappedDepth = bits.Len(uint(len(mp.Indices))) - 1 + cappedDepth := bits.Len(uint(len(mp.Indices))) - 1 if cappedDepth >= int(mp.Depth) { + // We need to have at least one level, in order to produce the hash and consume the siblings cappedDepth = int(mp.Depth) - 1 } capContainer := make([]Digest, 2< 0 { + totalAuthPath[i] = make([][]frontend.Variable, numOfLeavesProved) + } totalLeaves[i] = make([][]frontend.Variable, numOfLeavesProved) totalLeafSiblingHashes[i] = make([]frontend.Variable, numOfLeavesProved) totalCapContainer[i] = make([]frontend.Variable, len(merkle_path.CapContainer)) for j := range numOfLeavesProved { - totalAuthPath[i][j] = make([]frontend.Variable, treeHeight) + if treeHeight > 0 { + totalAuthPath[i][j] = make([]frontend.Variable, treeHeight) + } totalLeaves[i][j] = make([]frontend.Variable, len(hint.stirAnswers[i][j])) } diff --git a/recursive-verifier/app/circuit/whir_utilities.go b/recursive-verifier/app/circuit/whir_utilities.go index 0361c619..71365f2f 100644 --- a/recursive-verifier/app/circuit/whir_utilities.go +++ b/recursive-verifier/app/circuit/whir_utilities.go @@ -19,33 +19,28 @@ func verifyMerkleTreeProofs(api frontend.API, uapi *uints.BinaryField[uints.U64] api.AssertIsEqual(actualHash, capContainer[i]) } } + api.AssertIsEqual(rootHash, capContainer[1]) capDepth := bits.Len(uint(len(capContainer)/2)) - 1 - // api.Println(capDepth) - dedupedLUT := logderivlookup.New(api) + cappedNodesLUT := logderivlookup.New(api) for i := (len(capContainer) / 2); i < len(capContainer); i++ { - // api.Println(capContainer[i]) - dedupedLUT.Insert(capContainer[i]) + cappedNodesLUT.Insert(capContainer[i]) } - // api.AssertIsEqual(x, searchRes[0]) - numOfLeavesProved := len(leaves) - // api.Println(numOfLeavesProved) + + trimmedTreeHeight := 0 + if len(authPaths) > 0 { + trimmedTreeHeight = len(authPaths[0]) + } for i := range numOfLeavesProved { - treeHeight := len(authPaths[i]) + 1 + capDepth + treeHeight := trimmedTreeHeight + 1 + capDepth leafIndexBits := api.ToBinary(uapi.ToValue(leafIndexes[i]), treeHeight) - - // api.Println(uapi.ToValue(leafIndexes[i])) - // api.Println(1 << treeHeight) - // api.Println(leafIndexBits[treeHeight-capDepth:]) rootIndex := api.FromBinary(leafIndexBits[treeHeight-capDepth:]...) - // api.Println(rootIndex) - searchRes := dedupedLUT.Lookup(rootIndex) - rootHash := searchRes[0] - // api.Println(rootHash) + searchRes := cappedNodesLUT.Lookup(rootIndex) + cappedNodeHash := searchRes[0] leafSiblingHash := leafSiblingHashes[i] claimedLeafHash := sc.CompressV2(leaves[i][0], leaves[i][1]) @@ -69,8 +64,7 @@ func verifyMerkleTreeProofs(api frontend.API, uapi *uints.BinaryField[uints.U64] currentHash = sc.CompressV2(left, right) } - // api.Println(currentHash) - api.AssertIsEqual(currentHash, rootHash) + api.AssertIsEqual(currentHash, cappedNodeHash) } return nil } From 68d1d30f4ff26c8ed4c93b4fcfd71dd3419d0036 Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Wed, 12 Nov 2025 10:56:22 +0100 Subject: [PATCH 06/12] minor cleanup --- provekit/prover/src/whir_r1cs.rs | 3 +-- recursive-verifier/app/circuit/types.go | 8 -------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/provekit/prover/src/whir_r1cs.rs b/provekit/prover/src/whir_r1cs.rs index 1af98948..e162faec 100644 --- a/provekit/prover/src/whir_r1cs.rs +++ b/provekit/prover/src/whir_r1cs.rs @@ -9,7 +9,7 @@ use { sumcheck::{ calculate_evaluations_over_boolean_hypercube_for_eq, calculate_external_row_of_r1cs_matrices, calculate_witness_bounds, eval_cubic_poly, - sumcheck_fold_map_reduce, SumcheckIOPattern, + sumcheck_fold_map_reduce, }, zk_utils::{create_masked_polynomial, generate_random_multilinear_polynomial}, HALF, @@ -25,7 +25,6 @@ use { poly_utils::{evals::EvaluationsList, multilinear::MultilinearPoint}, whir::{ committer::{CommitmentWriter, Witness}, - domainsep::WhirDomainSeparator, prover::Prover, statement::{Statement, Weights}, utils::HintSerialize, diff --git a/recursive-verifier/app/circuit/types.go b/recursive-verifier/app/circuit/types.go index 796009a7..ea05feb1 100644 --- a/recursive-verifier/app/circuit/types.go +++ b/recursive-verifier/app/circuit/types.go @@ -76,14 +76,6 @@ type InitialSumcheckData struct { InitialCombinationRandomness []frontend.Variable } -// Merkle specific types -type MerklePaths struct { - Leaves [][][]frontend.Variable - LeafIndexes [][]uints.U64 - LeafSiblingHashes [][][]uints.U8 - AuthPaths [][][][]uints.U8 -} - type Merkle struct { Leaves [][][]frontend.Variable LeafIndexes [][]uints.U64 From 78a6db045c673c560bece16f3ab3e163e51fac32 Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Fri, 14 Nov 2025 14:28:07 +0100 Subject: [PATCH 07/12] keep merkle cap changes only --- Cargo.toml | 5 ++-- provekit/common/src/skyscraper/mod.rs | 4 +-- provekit/common/src/skyscraper/whir.rs | 33 +------------------------ provekit/prover/src/whir_r1cs.rs | 16 +++--------- provekit/r1cs-compiler/src/whir_r1cs.rs | 14 ++++------- provekit/verifier/src/whir_r1cs.rs | 10 +++----- 6 files changed, 17 insertions(+), 65 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b31a3709..1cfe308d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,12 +138,11 @@ noirc_driver = { git = "https://github.com/noir-lang/noir", rev = "v1.0.0-beta.1 # Cryptography and proof systems ark-bn254 = { version = "0.5.0", default-features = false, features = ["scalar_field"] } -ark-crypto-primitives = { path="../crypto-primitives/crypto-primitives", features = ["merkle_tree"] } +ark-crypto-primitives = { version = "0.5.0", features = ["merkle_tree"] } ark-ff = { version = "0.5", features = ["asm", "std"] } ark-poly = "0.5" ark-serialize = "0.5" ark-std = { version = "0.5", features = ["std"] } spongefish = { git = "https://github.com/arkworks-rs/spongefish", features = ["arkworks-algebra"] } spongefish-pow = { git = "https://github.com/arkworks-rs/spongefish" } -#whir = { git = "https://github.com/WizardOfMenlo/whir/", features = ["tracing"], rev = "48fc0909d6a0d803d61c35b0c18499ad704da211" } -whir = { path = "../latest-whir/whir", features = ["tracing"] } \ No newline at end of file +whir = { git = "https://github.com/WizardOfMenlo/whir/", features = ["tracing"], rev = "48fc0909d6a0d803d61c35b0c18499ad704da211" } diff --git a/provekit/common/src/skyscraper/mod.rs b/provekit/common/src/skyscraper/mod.rs index 480a31c9..3b6da92a 100644 --- a/provekit/common/src/skyscraper/mod.rs +++ b/provekit/common/src/skyscraper/mod.rs @@ -1,5 +1,5 @@ mod pow; mod sponge; -pub mod whir; +mod whir; -pub use self::{pow::SkyscraperPoW, sponge::SkyscraperSponge, whir::SkyscraperMerkleConfig, whir::SkyscraperHasher}; +pub use self::{pow::SkyscraperPoW, sponge::SkyscraperSponge, whir::SkyscraperMerkleConfig}; diff --git a/provekit/common/src/skyscraper/whir.rs b/provekit/common/src/skyscraper/whir.rs index 0845f873..f41a4842 100644 --- a/provekit/common/src/skyscraper/whir.rs +++ b/provekit/common/src/skyscraper/whir.rs @@ -14,8 +14,7 @@ use { }, DomainSeparator, ProofResult, ProverState, VerifierState, }, - core::mem::size_of, - std::borrow::Borrow, whir::merkle_tree::{Hasher, Hash}, + std::borrow::Borrow, }; @@ -110,34 +109,4 @@ impl whir::whir::utils::DigestToUnitDeserialize let [r] = self.next_scalars()?; Ok(r) } -} - -pub struct SkyscraperHasher; - -impl SkyscraperHasher { - pub fn new() -> Self { - // Skyscraper outputs 32 bytes (one field element) - assert_eq!(size_of::(), 32); - Self - } -} - -impl Hasher for SkyscraperHasher { - fn hash_many(&self, size: usize, input: &[u8], output: &mut [Hash]) { - assert_eq!(input.len() % size, 0, "Input length not a multiple of message size."); - assert_eq!(input.len() / 2, output.len() * 32, "Output length mismatch."); - - // Reinterpret `&mut [Hash]` as a flat `&mut [u8]` - let out_bytes_len = output.len() * size_of::(); - let out_bytes = unsafe { - core::slice::from_raw_parts_mut(output.as_mut_ptr().cast::(), out_bytes_len) - }; - - // Choose the implementation you want: - // skyscraper::reference::compress_many(input, out_bytes); - // skyscraper::v1::compress_many(input, out_bytes); - // skyscraper::block3::compress_many(input, out_bytes); - // skyscraper::block4::compress_many(input, out_bytes); - skyscraper::simple::compress_many(input, out_bytes); - } } \ No newline at end of file diff --git a/provekit/prover/src/whir_r1cs.rs b/provekit/prover/src/whir_r1cs.rs index e162faec..96fb3d63 100644 --- a/provekit/prover/src/whir_r1cs.rs +++ b/provekit/prover/src/whir_r1cs.rs @@ -178,14 +178,6 @@ pub fn sum_over_hypercube(g_univariates: &[[FieldElement; 4]]) -> FieldElement { + eval_cubic_poly(&polynomial_coefficient, &FieldElement::one()) } -use provekit_common::skyscraper::whir::SkyscraperHasher; -use whir::merkle_tree::Hasher; - - -pub fn construct_skyscraper() -> Box { - Box::new(SkyscraperHasher::new()) -} - pub fn batch_commit_to_polynomial( m: usize, whir_config: &WhirConfig, @@ -207,9 +199,9 @@ pub fn batch_commit_to_polynomial( let committer = CommitmentWriter::new(whir_config.clone()); let witness_new = committer .commit_batch(merlin, &[ - &masked_polynomial_coeff, - &random_polynomial_coeff, - ], construct_skyscraper) + masked_polynomial_coeff.clone(), + random_polynomial_coeff.clone(), + ]) .expect("WHIR prover failed to commit"); (witness_new, masked_polynomial, random_polynomial_eval) @@ -437,7 +429,7 @@ pub fn run_zk_whir_pcs_prover( let prover = Prover::new(params.clone()); let (randomness, deferred) = prover - .prove(&mut merlin, statement, witness, construct_skyscraper) + .prove(&mut merlin, statement, witness) .expect("WHIR prover failed to generate a proof"); (merlin, randomness, deferred) diff --git a/provekit/r1cs-compiler/src/whir_r1cs.rs b/provekit/r1cs-compiler/src/whir_r1cs.rs index e053284c..566bcec5 100644 --- a/provekit/r1cs-compiler/src/whir_r1cs.rs +++ b/provekit/r1cs-compiler/src/whir_r1cs.rs @@ -1,14 +1,10 @@ use { - provekit_common::{skyscraper::SkyscraperHasher, utils::next_power_of_two, WhirConfig, WhirR1CSScheme, R1CS}, - whir::{merkle_tree::{Hasher, Hashers}, parameters::{ - default_max_pow, DeduplicationStrategy, FoldingFactor, - MultivariateParameters, ProtocolParameters, SoundnessType, - }}, + provekit_common::{utils::next_power_of_two, WhirConfig, WhirR1CSScheme, R1CS}, + whir::parameters::{ + default_max_pow, DeduplicationStrategy, FoldingFactor, MerkleProofStrategy, MultivariateParameters, ProtocolParameters, SoundnessType + }, }; -pub fn construct_skyscraper() -> Box { - Box::new(SkyscraperHasher::new()) -} pub trait WhirR1CSSchemeBuilder { fn new_for_r1cs(r1cs: &R1CS) -> Self; @@ -53,7 +49,7 @@ impl WhirR1CSSchemeBuilder for WhirR1CSScheme { starting_log_inv_rate: 1, batch_size, deduplication_strategy: DeduplicationStrategy::Disabled, - merkle_runtime_config: Hashers::Skyscraper2, + merkle_proof_strategy: MerkleProofStrategy::Compressed, }; WhirConfig::new(mv_params, whir_params) } diff --git a/provekit/verifier/src/whir_r1cs.rs b/provekit/verifier/src/whir_r1cs.rs index 656e2b42..3d21c727 100644 --- a/provekit/verifier/src/whir_r1cs.rs +++ b/provekit/verifier/src/whir_r1cs.rs @@ -2,7 +2,7 @@ use { anyhow::{ensure, Context, Result}, ark_std::{One, Zero}, provekit_common::{ - skyscraper::{SkyscraperHasher, SkyscraperSponge}, + skyscraper::SkyscraperSponge, utils::sumcheck::{calculate_eq, eval_cubic_poly}, FieldElement, WhirConfig, WhirR1CSProof, WhirR1CSScheme, }, @@ -12,7 +12,7 @@ use { }, tracing::instrument, whir::{ - merkle_tree::Hasher, poly_utils::{evals::EvaluationsList, multilinear::MultilinearPoint}, whir::{ + poly_utils::{evals::EvaluationsList, multilinear::MultilinearPoint}, whir::{ committer::{reader::ParsedCommitment, CommitmentReader}, statement::{Statement, Weights}, utils::HintDeserialize, @@ -31,10 +31,6 @@ pub trait WhirR1CSVerifier { fn verify(&self, proof: &WhirR1CSProof) -> Result<()>; } -pub fn construct_skyscraper() -> Box { - Box::new(SkyscraperHasher::new()) -} - impl WhirR1CSVerifier for WhirR1CSScheme { #[instrument(skip_all)] #[allow(unused)] // TODO: Fix implementation @@ -184,7 +180,7 @@ pub fn run_whir_pcs_verifier( let verifier = Verifier::new(params); let (folding_randomness, deferred) = verifier - .verify(arthur, parsed_commitment, statement_verifier, construct_skyscraper) + .verify(arthur, parsed_commitment, statement_verifier) .context("while verifying WHIR")?; Ok((folding_randomness, deferred)) From be35c74dba8479b92a6158892acfd8da8c2df554 Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Tue, 18 Nov 2025 13:59:37 +0100 Subject: [PATCH 08/12] cap in merkle --- provekit/common/src/skyscraper/whir.rs | 3 +- provekit/r1cs-compiler/src/whir_r1cs.rs | 5 +- provekit/verifier/src/whir_r1cs.rs | 5 +- recursive-verifier/app/circuit/common.go | 127 ++++++++++++++++-- recursive-verifier/app/circuit/types.go | 7 +- recursive-verifier/app/circuit/utilities.go | 6 +- .../app/circuit/whir_utilities.go | 1 - 7 files changed, 132 insertions(+), 22 deletions(-) diff --git a/provekit/common/src/skyscraper/whir.rs b/provekit/common/src/skyscraper/whir.rs index f41a4842..d855f414 100644 --- a/provekit/common/src/skyscraper/whir.rs +++ b/provekit/common/src/skyscraper/whir.rs @@ -17,7 +17,6 @@ use { std::borrow::Borrow, }; - fn compress(l: FieldElement, r: FieldElement) -> FieldElement { let l64 = l.into_bigint().0; let r64 = r.into_bigint().0; @@ -109,4 +108,4 @@ impl whir::whir::utils::DigestToUnitDeserialize let [r] = self.next_scalars()?; Ok(r) } -} \ No newline at end of file +} diff --git a/provekit/r1cs-compiler/src/whir_r1cs.rs b/provekit/r1cs-compiler/src/whir_r1cs.rs index 566bcec5..30eccb03 100644 --- a/provekit/r1cs-compiler/src/whir_r1cs.rs +++ b/provekit/r1cs-compiler/src/whir_r1cs.rs @@ -1,7 +1,8 @@ use { provekit_common::{utils::next_power_of_two, WhirConfig, WhirR1CSScheme, R1CS}, whir::parameters::{ - default_max_pow, DeduplicationStrategy, FoldingFactor, MerkleProofStrategy, MultivariateParameters, ProtocolParameters, SoundnessType + default_max_pow, DeduplicationStrategy, FoldingFactor, MerkleProofStrategy, + MultivariateParameters, ProtocolParameters, SoundnessType, }, }; @@ -49,7 +50,7 @@ impl WhirR1CSSchemeBuilder for WhirR1CSScheme { starting_log_inv_rate: 1, batch_size, deduplication_strategy: DeduplicationStrategy::Disabled, - merkle_proof_strategy: MerkleProofStrategy::Compressed, + merkle_proof_strategy: MerkleProofStrategy::Uncompressed, }; WhirConfig::new(mv_params, whir_params) } diff --git a/provekit/verifier/src/whir_r1cs.rs b/provekit/verifier/src/whir_r1cs.rs index 3d21c727..e56bbb6b 100644 --- a/provekit/verifier/src/whir_r1cs.rs +++ b/provekit/verifier/src/whir_r1cs.rs @@ -12,12 +12,13 @@ use { }, tracing::instrument, whir::{ - poly_utils::{evals::EvaluationsList, multilinear::MultilinearPoint}, whir::{ + poly_utils::{evals::EvaluationsList, multilinear::MultilinearPoint}, + whir::{ committer::{reader::ParsedCommitment, CommitmentReader}, statement::{Statement, Weights}, utils::HintDeserialize, verifier::Verifier, - } + }, }, }; diff --git a/recursive-verifier/app/circuit/common.go b/recursive-verifier/app/circuit/common.go index 678980b8..720e679a 100644 --- a/recursive-verifier/app/circuit/common.go +++ b/recursive-verifier/app/circuit/common.go @@ -22,8 +22,8 @@ type IndexPair struct { func convertMultiIndexMTProofsToFullMultiPath( merklePaths []MultiIndexMerkleTreeProof[Digest], stirAnswers [][][]Fp256, - fullMerklePaths *[]FullMultiPath[Digest], -) { +) []FullMultiPathWithCapping[Digest] { + fullMerklePaths := make([]FullMultiPathWithCapping[Digest], 0, len(merklePaths)) for mIndex, mp := range merklePaths { depth := mp.Depth proofIter := 0 @@ -141,14 +141,122 @@ func convertMultiIndexMTProofsToFullMultiPath( } paths = append(paths, Path[Digest]{ - LeafHash: leafHashes[origIdx], LeafIndex: origIdx, LeafSiblingHash: leafSibling, AuthPath: authPath[:len(authPath)-cappedDepth], }) } - *fullMerklePaths = append(*fullMerklePaths, FullMultiPath[Digest]{Proofs: paths, CapContainer: capContainer}) + fullMerklePaths = append(fullMerklePaths, FullMultiPathWithCapping[Digest]{Proofs: paths, CapContainer: capContainer}) + } + return fullMerklePaths +} + +func convertFullMultiPathToFullMultiPathWithCapping( + paths []FullMultiPath[Digest], + stirAnswers [][][]Fp256, +) ([]FullMultiPathWithCapping[Digest], error) { + fullMerklePaths := make([]FullMultiPathWithCapping[Digest], 0, len(paths)) + for mIndex, path := range paths { + currentMPAnswers := stirAnswers[mIndex] + + if len(currentMPAnswers) != len(path.Proofs) { + panic(fmt.Sprintf("mismatched stirAnswers (%d) and indices (%d)", len(currentMPAnswers), len(path.Proofs))) + } + depth := len(path.Proofs[0].AuthPath) + 1 + + cappedDepth := 0 + if len(path.Proofs) > 1 { + cappedDepth = bits.Len(uint(len(path.Proofs))) - 1 + } + if cappedDepth >= depth { + cappedDepth = depth - 1 + } + if cappedDepth < 0 { + cappedDepth = 0 + } + tree := make(map[IndexPair]Digest, 2<= 0; idx-- { + sibling := proof.AuthPath[idx] + siblingIdx := currentIdx ^ 1 + tree[IndexPair{depth: uint64(currentDepth), index: siblingIdx}] = sibling + + if currentIdx%2 == 0 { + currentHash = HashTwoDigests(currentHash, sibling) + } else { + currentHash = HashTwoDigests(sibling, currentHash) + } + + currentIdx /= 2 + currentDepth-- + + tree[IndexPair{depth: uint64(currentDepth), index: currentIdx}] = currentHash } } @@ -162,7 +270,7 @@ func PrepareAndVerifyCircuit(config Config, r1cs R1CS, pk *groth16.ProvingKey, v var pointer uint64 var truncated []byte - var merklePaths []MultiIndexMerkleTreeProof[Digest] + var merklePaths []FullMultiPath[Digest] var stirAnswers [][][]Fp256 var deferred []Fp256 var claimedEvaluations ClaimedEvaluations @@ -183,7 +291,7 @@ func PrepareAndVerifyCircuit(config Config, r1cs R1CS, pk *groth16.ProvingKey, v switch string(op.Label) { case "merkle_proof": - var path MultiIndexMerkleTreeProof[Digest] + var path FullMultiPath[Digest] _, err = arkSerialize.CanonicalDeserializeWithMode( bytes.NewReader(config.Transcript[start:end]), &path, @@ -259,10 +367,10 @@ func PrepareAndVerifyCircuit(config Config, r1cs R1CS, pk *groth16.ProvingKey, v return fmt.Errorf("failed to deserialize interner: %w", err) } - var fullMerklePaths []FullMultiPath[Digest] - convertMultiIndexMTProofsToFullMultiPath(merklePaths, stirAnswers, &fullMerklePaths) - var hidingSpartanData = consumeWhirData(config.WHIRConfigHidingSpartan, &fullMerklePaths, &stirAnswers) + // convertMultiIndexMTProofsToFullMultiPath(merklePaths, stirAnswers, &fullMerklePaths) + fullMerklePaths, err := convertFullMultiPathToFullMultiPathWithCapping(merklePaths, stirAnswers) + var hidingSpartanData = consumeWhirData(config.WHIRConfigHidingSpartan, &fullMerklePaths, &stirAnswers) var witnessData = consumeWhirData(config.WHIRConfigWitness, &fullMerklePaths, &stirAnswers) hints := Hints{ @@ -378,7 +486,6 @@ func VerifyAuthPath( } currentIdx = parentIdx } - if currentHash != expectedRoot { return fmt.Errorf("root mismatch: got %x, expected %x", currentHash, expectedRoot) } diff --git a/recursive-verifier/app/circuit/types.go b/recursive-verifier/app/circuit/types.go index ea05feb1..2ba591d9 100644 --- a/recursive-verifier/app/circuit/types.go +++ b/recursive-verifier/app/circuit/types.go @@ -15,13 +15,16 @@ type Fp256 struct { } type Path[Digest any] struct { - LeafHash Digest LeafSiblingHash Digest AuthPath []Digest LeafIndex uint64 } type FullMultiPath[Digest any] struct { + Proofs []Path[Digest] +} + +type FullMultiPathWithCapping[Digest any] struct { Proofs []Path[Digest] CapContainer []Digest } @@ -107,7 +110,7 @@ type Hints struct { } type Hint struct { - merklePaths []FullMultiPath[Digest] + merklePaths []FullMultiPathWithCapping[Digest] stirAnswers [][][]Fp256 } diff --git a/recursive-verifier/app/circuit/utilities.go b/recursive-verifier/app/circuit/utilities.go index 38bc3ff3..305333cd 100644 --- a/recursive-verifier/app/circuit/utilities.go +++ b/recursive-verifier/app/circuit/utilities.go @@ -242,7 +242,7 @@ func consumeFront[T any](slice *[]T) T { return head } -func consumeWhirData(whirConfig WHIRConfig, merkle_paths *[]FullMultiPath[Digest], stir_answers *[][][]Fp256) ZKHint { +func consumeWhirData(whirConfig WHIRConfig, merkle_paths *[]FullMultiPathWithCapping[Digest], stir_answers *[][][]Fp256) ZKHint { var zkHint ZKHint if len(*merkle_paths) > 0 && len(*stir_answers) > 0 { @@ -251,7 +251,7 @@ func consumeWhirData(whirConfig WHIRConfig, merkle_paths *[]FullMultiPath[Digest zkHint.firstRoundMerklePaths = FirstRoundHint{ path: Hint{ - merklePaths: []FullMultiPath[Digest]{firstRoundMerklePath}, + merklePaths: []FullMultiPathWithCapping[Digest]{firstRoundMerklePath}, stirAnswers: [][][]Fp256{firstRoundStirAnswers}, }, expectedStirAnswers: firstRoundStirAnswers, @@ -260,7 +260,7 @@ func consumeWhirData(whirConfig WHIRConfig, merkle_paths *[]FullMultiPath[Digest expectedRounds := whirConfig.NRounds - var remainingMerklePaths []FullMultiPath[Digest] + var remainingMerklePaths []FullMultiPathWithCapping[Digest] var remainingStirAnswers [][][]Fp256 for i := 0; i < expectedRounds && len(*merkle_paths) > 0 && len(*stir_answers) > 0; i++ { diff --git a/recursive-verifier/app/circuit/whir_utilities.go b/recursive-verifier/app/circuit/whir_utilities.go index 71365f2f..7d9563f6 100644 --- a/recursive-verifier/app/circuit/whir_utilities.go +++ b/recursive-verifier/app/circuit/whir_utilities.go @@ -34,7 +34,6 @@ func verifyMerkleTreeProofs(api frontend.API, uapi *uints.BinaryField[uints.U64] if len(authPaths) > 0 { trimmedTreeHeight = len(authPaths[0]) } - for i := range numOfLeavesProved { treeHeight := trimmedTreeHeight + 1 + capDepth leafIndexBits := api.ToBinary(uapi.ToValue(leafIndexes[i]), treeHeight) From 15e57c0dfc1cac9ace60b9eda704e9dd0fba3077 Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Tue, 18 Nov 2025 14:09:08 +0100 Subject: [PATCH 09/12] cleanup --- recursive-verifier/app/circuit/common.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/recursive-verifier/app/circuit/common.go b/recursive-verifier/app/circuit/common.go index 720e679a..64b90d5b 100644 --- a/recursive-verifier/app/circuit/common.go +++ b/recursive-verifier/app/circuit/common.go @@ -199,10 +199,6 @@ func convertFullMultiPathToFullMultiPathWithCapping( for i := 0; i < len(proof.AuthPath); i = i + 1 { authPath[i] = proof.AuthPath[len(proof.AuthPath)-1-i] } - err := VerifyAuthPath(HashLeafData(currentMPAnswers[i]), proof.LeafSiblingHash, authPath, proof.LeafIndex, uint64(depth), capContainer[1]) - if err != nil { - return nil, fmt.Errorf("failed to verify auth path for index %d: %w", proof.LeafIndex, err) - } trimLen := len(proof.AuthPath) - cappedDepth if trimLen < 0 { From 2ca3ff7b2742f1c455639e99201f631a79ec53b2 Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Tue, 18 Nov 2025 14:09:59 +0100 Subject: [PATCH 10/12] cargo cleanup --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1cfe308d..a801cd70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,11 +138,11 @@ noirc_driver = { git = "https://github.com/noir-lang/noir", rev = "v1.0.0-beta.1 # Cryptography and proof systems ark-bn254 = { version = "0.5.0", default-features = false, features = ["scalar_field"] } -ark-crypto-primitives = { version = "0.5.0", features = ["merkle_tree"] } +ark-crypto-primitives = { version = "0.5", features = ["merkle_tree"] } ark-ff = { version = "0.5", features = ["asm", "std"] } ark-poly = "0.5" ark-serialize = "0.5" ark-std = { version = "0.5", features = ["std"] } spongefish = { git = "https://github.com/arkworks-rs/spongefish", features = ["arkworks-algebra"] } spongefish-pow = { git = "https://github.com/arkworks-rs/spongefish" } -whir = { git = "https://github.com/WizardOfMenlo/whir/", features = ["tracing"], rev = "48fc0909d6a0d803d61c35b0c18499ad704da211" } +whir = { git = "https://github.com/WizardOfMenlo/whir/", features = ["tracing"], rev = "48fc0909d6a0d803d61c35b0c18499ad704da211" } \ No newline at end of file From 9b19c4017204b06ea84fd19d169dfb403a3242c4 Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Tue, 18 Nov 2025 15:45:42 +0100 Subject: [PATCH 11/12] go lint --- recursive-verifier/app/circuit/common.go | 270 +++++++++--------- recursive-verifier/app/circuit/skyscraper2.go | 5 - 2 files changed, 136 insertions(+), 139 deletions(-) diff --git a/recursive-verifier/app/circuit/common.go b/recursive-verifier/app/circuit/common.go index 33e6a167..c2ea01b5 100644 --- a/recursive-verifier/app/circuit/common.go +++ b/recursive-verifier/app/circuit/common.go @@ -7,7 +7,6 @@ import ( "fmt" "log" "math/bits" - "sort" "github.com/consensys/gnark/backend/groth16" gnarkNimue "github.com/reilabs/gnark-nimue" @@ -21,138 +20,138 @@ type IndexPair struct { index uint64 } -func convertMultiIndexMTProofsToFullMultiPath( - merklePaths []MultiIndexMerkleTreeProof[Digest], - stirAnswers [][][]Fp256, -) []FullMultiPathWithCapping[Digest] { - fullMerklePaths := make([]FullMultiPathWithCapping[Digest], 0, len(merklePaths)) - for mIndex, mp := range merklePaths { - depth := mp.Depth - proofIter := 0 - - currentMPAnswers := stirAnswers[mIndex] - if len(currentMPAnswers) != len(mp.Indices) { - panic(fmt.Sprintf("mismatched stirAnswers (%d) and indices (%d)", len(currentMPAnswers), len(mp.Indices))) - } - - leafHashes := make(map[uint64]Digest) - for i := range mp.Indices { - leafHashes[mp.Indices[i]] = HashLeafData(currentMPAnswers[i]) - } - - uniqueIndices := make(map[uint64]bool) - for _, idx := range mp.Indices { - uniqueIndices[idx] = true - } - - indices := make([]uint64, 0, len(uniqueIndices)) - for idx := range uniqueIndices { - indices = append(indices, idx) - } - sort.Slice(indices, func(i, j int) bool { - return indices[i] < indices[j] - }) - - treeElements := make(map[IndexPair]Digest, len(indices)) - - cappedDepth := bits.Len(uint(len(mp.Indices))) - 1 - - if cappedDepth >= int(mp.Depth) { - // We need to have at least one level, in order to produce the hash and consume the siblings - cappedDepth = int(mp.Depth) - 1 - } - capContainer := make([]Digest, 2< 0; d-- { - nextIndices := make([]uint64, 0, len(indices)) - capIndices := make([]uint64, 0, 1<= len(mp.Proof) { - panic("insufficient siblings") - } - sib := mp.Proof[proofIter] - capIndices = append(capIndices, idx) - capIndices = append(capIndices, idx+1) - treeElements[IndexPair{depth: d, index: idx + 1}] = sib - treeElements[IndexPair{depth: d - 1, index: idx / 2}] = HashTwoDigests(node, sib) - proofIter++ - nextIndices = append(nextIndices, idx/2) - i++ - } - } else { - // right child - if proofIter >= len(mp.Proof) { - panic("insufficient siblings") - } - sib := mp.Proof[proofIter] - capIndices = append(capIndices, idx-1) - capIndices = append(capIndices, idx) - treeElements[IndexPair{depth: d, index: idx - 1}] = sib - treeElements[IndexPair{depth: d - 1, index: idx / 2}] = HashTwoDigests(sib, node) - - proofIter++ - nextIndices = append(nextIndices, idx/2) - i++ - } - } - if d <= (uint64(cappedDepth)) { - for j := range capIndices { - offset := 1 << d - capContainer[int(offset)+int(capIndices[j])] = treeElements[IndexPair{depth: d, index: uint64(capIndices[j])}] - } - } - indices = nextIndices - } - - capContainer[1] = treeElements[IndexPair{depth: 0, index: 0}] - - var paths []Path[Digest] - for _, origIdx := range mp.Indices { - leafSibling, authPath, err := ExtractAuthPath(treeElements, origIdx, depth) - if err != nil { - panic(fmt.Sprintf("failed to extract auth path for index %d: %v", origIdx, err)) - } - - paths = append(paths, Path[Digest]{ - LeafIndex: origIdx, - LeafSiblingHash: leafSibling, - AuthPath: authPath[:len(authPath)-cappedDepth], - }) - } - - fullMerklePaths = append(fullMerklePaths, FullMultiPathWithCapping[Digest]{Proofs: paths, CapContainer: capContainer}) - } - return fullMerklePaths -} +// func convertMultiIndexMTProofsToFullMultiPath( +// merklePaths []MultiIndexMerkleTreeProof[Digest], +// stirAnswers [][][]Fp256, +// ) []FullMultiPathWithCapping[Digest] { +// fullMerklePaths := make([]FullMultiPathWithCapping[Digest], 0, len(merklePaths)) +// for mIndex, mp := range merklePaths { +// depth := mp.Depth +// proofIter := 0 + +// currentMPAnswers := stirAnswers[mIndex] +// if len(currentMPAnswers) != len(mp.Indices) { +// panic(fmt.Sprintf("mismatched stirAnswers (%d) and indices (%d)", len(currentMPAnswers), len(mp.Indices))) +// } + +// leafHashes := make(map[uint64]Digest) +// for i := range mp.Indices { +// leafHashes[mp.Indices[i]] = HashLeafData(currentMPAnswers[i]) +// } + +// uniqueIndices := make(map[uint64]bool) +// for _, idx := range mp.Indices { +// uniqueIndices[idx] = true +// } + +// indices := make([]uint64, 0, len(uniqueIndices)) +// for idx := range uniqueIndices { +// indices = append(indices, idx) +// } +// sort.Slice(indices, func(i, j int) bool { +// return indices[i] < indices[j] +// }) + +// treeElements := make(map[IndexPair]Digest, len(indices)) + +// cappedDepth := bits.Len(uint(len(mp.Indices))) - 1 + +// if cappedDepth >= int(mp.Depth) { +// // We need to have at least one level, in order to produce the hash and consume the siblings +// cappedDepth = int(mp.Depth) - 1 +// } +// capContainer := make([]Digest, 2< 0; d-- { +// nextIndices := make([]uint64, 0, len(indices)) +// capIndices := make([]uint64, 0, 1<= len(mp.Proof) { +// panic("insufficient siblings") +// } +// sib := mp.Proof[proofIter] +// capIndices = append(capIndices, idx) +// capIndices = append(capIndices, idx+1) +// treeElements[IndexPair{depth: d, index: idx + 1}] = sib +// treeElements[IndexPair{depth: d - 1, index: idx / 2}] = HashTwoDigests(node, sib) +// proofIter++ +// nextIndices = append(nextIndices, idx/2) +// i++ +// } +// } else { +// // right child +// if proofIter >= len(mp.Proof) { +// panic("insufficient siblings") +// } +// sib := mp.Proof[proofIter] +// capIndices = append(capIndices, idx-1) +// capIndices = append(capIndices, idx) +// treeElements[IndexPair{depth: d, index: idx - 1}] = sib +// treeElements[IndexPair{depth: d - 1, index: idx / 2}] = HashTwoDigests(sib, node) + +// proofIter++ +// nextIndices = append(nextIndices, idx/2) +// i++ +// } +// } +// if d <= (uint64(cappedDepth)) { +// for j := range capIndices { +// offset := 1 << d +// capContainer[int(offset)+int(capIndices[j])] = treeElements[IndexPair{depth: d, index: uint64(capIndices[j])}] +// } +// } +// indices = nextIndices +// } + +// capContainer[1] = treeElements[IndexPair{depth: 0, index: 0}] + +// var paths []Path[Digest] +// for _, origIdx := range mp.Indices { +// leafSibling, authPath, err := ExtractAuthPath(treeElements, origIdx, depth) +// if err != nil { +// panic(fmt.Sprintf("failed to extract auth path for index %d: %v", origIdx, err)) +// } + +// paths = append(paths, Path[Digest]{ +// LeafIndex: origIdx, +// LeafSiblingHash: leafSibling, +// AuthPath: authPath[:len(authPath)-cappedDepth], +// }) +// } + +// fullMerklePaths = append(fullMerklePaths, FullMultiPathWithCapping[Digest]{Proofs: paths, CapContainer: capContainer}) +// } +// return fullMerklePaths +// } func convertFullMultiPathToFullMultiPathWithCapping( paths []FullMultiPath[Digest], @@ -163,7 +162,7 @@ func convertFullMultiPathToFullMultiPathWithCapping( currentMPAnswers := stirAnswers[mIndex] if len(currentMPAnswers) != len(path.Proofs) { - panic(fmt.Sprintf("mismatched stirAnswers (%d) and indices (%d)", len(currentMPAnswers), len(path.Proofs))) + return nil, fmt.Errorf("mismatched stirAnswers (%d) and indices (%d)", len(currentMPAnswers), len(path.Proofs)) } depth := len(path.Proofs[0].AuthPath) + 1 @@ -367,6 +366,9 @@ func PrepareAndVerifyCircuit(config Config, r1cs R1CS, pk *groth16.ProvingKey, v // convertMultiIndexMTProofsToFullMultiPath(merklePaths, stirAnswers, &fullMerklePaths) fullMerklePaths, err := convertFullMultiPathToFullMultiPathWithCapping(merklePaths, stirAnswers) + if err != nil { + return fmt.Errorf("failed to convert full multi path to full multi path with capping: %w", err) + } var hidingSpartanData = consumeWhirData(config.WHIRConfigHidingSpartan, &fullMerklePaths, &stirAnswers) var witnessData = consumeWhirData(config.WHIRConfigWitness, &fullMerklePaths, &stirAnswers) diff --git a/recursive-verifier/app/circuit/skyscraper2.go b/recursive-verifier/app/circuit/skyscraper2.go index 9fd16a9e..bcf68edc 100644 --- a/recursive-verifier/app/circuit/skyscraper2.go +++ b/recursive-verifier/app/circuit/skyscraper2.go @@ -70,11 +70,6 @@ func limbsToBigInt(limbs [4]uint64) *big.Int { return result.Mod(result, bn254Modulus) } -// Convert Fp256 to big.Int -func fp256ToBigInt(fp Fp256) *big.Int { - return limbsToBigInt(fp.Limbs) -} - // Convert big.Int to [4]uint64 limbs func bigIntToLimbs(val *big.Int) [4]uint64 { // Ensure value is positive and reduced From 22e390563f620b11b86458e72b7b215edd1707d5 Mon Sep 17 00:00:00 2001 From: Veljko Vranic Date: Wed, 19 Nov 2025 17:38:54 +0100 Subject: [PATCH 12/12] temporarily fix spongefish version --- Cargo.toml | 6 +++--- provekit/r1cs-compiler/src/whir_r1cs.rs | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a0a0f09f..621ddf00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,6 +149,6 @@ ark-serialize = "0.5" ark-std = { version = "0.5", features = ["std"] } spongefish = { git = "https://github.com/arkworks-rs/spongefish", features = [ "arkworks-algebra", -] } -spongefish-pow = { git = "https://github.com/arkworks-rs/spongefish" } -whir = { git = "https://github.com/WizardOfMenlo/whir/", features = ["tracing"], rev = "3d627d31cec7d73a470a31a913229dd3128ee0cf" } +], rev = "ecb4f08373ed930175585c856517efdb1851fb47" } +spongefish-pow = { git = "https://github.com/arkworks-rs/spongefish", rev = "ecb4f08373ed930175585c856517efdb1851fb47" } +whir = { git = "https://github.com/WizardOfMenlo/whir/", features = ["tracing"], rev = "c2bafc36a878500a3e19c12d3239d488ff7b5d61" } \ No newline at end of file diff --git a/provekit/r1cs-compiler/src/whir_r1cs.rs b/provekit/r1cs-compiler/src/whir_r1cs.rs index e08ac26f..b7509738 100644 --- a/provekit/r1cs-compiler/src/whir_r1cs.rs +++ b/provekit/r1cs-compiler/src/whir_r1cs.rs @@ -1,9 +1,8 @@ use { - provekit_common::{utils::next_power_of_two, FieldElement, WhirConfig, WhirR1CSScheme, R1CS}, - whir::parameters::{ + provekit_common::{utils::next_power_of_two, WhirConfig, WhirR1CSScheme, R1CS}, std::sync::Arc, whir::{ntt::RSDefault, parameters::{ default_max_pow, DeduplicationStrategy, FoldingFactor, MerkleProofStrategy, MultivariateParameters, ProtocolParameters, SoundnessType, - }, + }} }; // Minimum log2 of the WHIR evaluation domain (lower bound for m). @@ -63,6 +62,8 @@ impl WhirR1CSSchemeBuilder for WhirR1CSScheme { deduplication_strategy: DeduplicationStrategy::Disabled, merkle_proof_strategy: MerkleProofStrategy::Uncompressed, }; - WhirConfig::new(mv_params, whir_params) + let reed_solomon = Arc::new(RSDefault); + let basefield_reed_solomon = reed_solomon.clone(); + WhirConfig::new(reed_solomon, basefield_reed_solomon, mv_params, whir_params) } }