Skip to content

feat: implement getProof verifier and testing prover#521

Open
antoniolocascio wants to merge 8 commits intodevfrom
alocascio-get-proof
Open

feat: implement getProof verifier and testing prover#521
antoniolocascio wants to merge 8 commits intodevfrom
alocascio-get-proof

Conversation

@antoniolocascio
Copy link
Contributor

What ❔

Add verifier for zks_getProof to the API and a testing prover. Main prover will live in the server.

Why ❔

Is this a breaking change?

  • Yes
  • No

Checklist

  • PR title corresponds to the body of PR (we generate changelog entries from PRs).
  • Tests for the changes have been added / updated.
  • Documentation comments have been added / updated.
  • Code has been formatted.

@antoniolocascio antoniolocascio force-pushed the alocascio-get-proof branch 4 times, most recently from 92eb500 to 0e71c0d Compare February 18, 2026 11:08
Comment on lines +347 to +349
let commitment =
compute_state_commitment(&state_root, &response.state_commitment_preimage);
if &commitment != expected_batch_hash {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Are extra hash operations not a concern? This fragment could be reworked to call compute_state_commitment once and then just compare recovered state root hashes on subsequent iterations. Then again, if the number of hashing operations a concern, having separate Merkle paths for storage slots is suboptimal on its own 🙃

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doubt that this extra hashing will be a concern, but I've implemented your suggestion. I think this method will largely be used for single proofs, so I guess I didn't consider the batching optimization too much

@github-actions
Copy link
Contributor

Benchmark report

Benchmark Symbol Base Eff Head Eff (%) Base Raw Head Raw (%) Base Blake Head Blake (%) Base Bigint Head Bigint (%)
block_19299001 process_block 315,727,782 315,727,782 (+0.00%) 273,062,382 273,062,382 (+0.00%) 410,630 410,630 (+0.00%) 9,023,830 9,023,830 (+0.00%)
block_22244135 process_block 197,606,679 197,606,679 (+0.00%) 170,690,887 170,690,887 (+0.00%) 172,040 172,040 (+0.00%) 6,040,788 6,040,788 (+0.00%)
precompiles bn254_ecadd 53,268 53,268 (+0.00%) 47,816 47,816 (+0.00%) 0 0 (+0.00%) 1,363 1,363 (+0.00%)
precompiles bn254_ecmul 728,781 728,781 (+0.00%) 564,593 564,593 (+0.00%) 0 0 (+0.00%) 41,047 41,047 (+0.00%)
precompiles bn254_pairing 72,336,733 72,336,733 (+0.00%) 57,808,589 57,808,589 (+0.00%) 0 0 (+0.00%) 3,632,036 3,632,036 (+0.00%)
precompiles ecrecover 477,444 478,229 (+0.16%) 309,612 309,909 (+0.10%) 0 0 (+0.00%) 41,958 42,080 (+0.29%)
precompiles id 927 927 (+0.00%) 927 927 (+0.00%) 0 0 (+0.00%) 0 0 (+0.00%)
precompiles keccak 137,579 137,579 (+0.00%) 137,579 137,579 (+0.00%) 0 0 (+0.00%) 0 0 (+0.00%)
precompiles modexp 31,267,898 31,267,898 (+0.00%) 20,610,078 20,610,078 (+0.00%) 0 0 (+0.00%) 2,664,455 2,664,455 (+0.00%)
precompiles p256_verify 748,861 748,861 (+0.00%) 470,169 470,169 (+0.00%) 0 0 (+0.00%) 69,673 69,673 (+0.00%)
precompiles point_evaluation 52,918,413 52,918,413 (+0.00%) 40,570,377 40,570,377 (+0.00%) 0 0 (+0.00%) 3,087,009 3,087,009 (+0.00%)
precompiles process_block 147,545,668 147,551,689 (+0.00%) 118,066,104 118,070,009 (+0.00%) 5,140 5,160 (+0.39%) 7,349,331 7,349,780 (+0.01%)
precompiles process_transaction 73,483,666 73,482,532 (-0.00%) 58,788,866 58,781,832 (-0.01%) 160 160 (+0.00%) 3,673,060 3,674,535 (+0.04%)
precompiles ripemd 8,013 8,013 (+0.00%) 8,013 8,013 (+0.00%) 0 0 (+0.00%) 0 0 (+0.00%)
precompiles run_tx_loop 146,894,674 146,900,106 (+0.00%) 117,494,470 117,498,106 (+0.00%) 180 180 (+0.00%) 7,349,331 7,349,780 (+0.01%)
precompiles sha256 13,168 13,168 (+0.00%) 13,168 13,168 (+0.00%) 0 0 (+0.00%) 0 0 (+0.00%)
precompiles system_init 46,797 46,797 (+0.00%) 46,797 46,797 (+0.00%) 0 0 (+0.00%) 0 0 (+0.00%)
precompiles verify_and_apply_batch 146,508 146,831 (+0.22%) 110,188 110,511 (+0.29%) 2,270 2,270 (+0.00%) 0 0 (+0.00%)

Copy link

@slowli slowli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, looks good 👍

Comment on lines +74 to +82
#[cfg_attr(feature = "serde", serde(with = "serde_hex::u64"))]
index: u64,
#[cfg_attr(feature = "serde", serde(with = "serde_hex::bytes32"))]
value: [u8; 32],
#[cfg_attr(feature = "serde", serde(rename = "nextIndex"))]
#[cfg_attr(feature = "serde", serde(with = "serde_hex::u64"))]
next_index: u64,
#[cfg_attr(feature = "serde", serde(with = "serde_hex::vec_bytes32"))]
siblings: Vec<[u8; 32]>,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible to reuse these 4 fields with LeafWithProof via #[serde(flatten)]. I've (temporarily) modeled API types like this in my PR.

let mut index = index;
for path in path.iter() {
let path: &[u8; 32] = path;
let (left, right) = if index & 1 == 0 {
Copy link

@slowli slowli Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bikeshedding: Don't really have time to play much with the disaassembler / Godbolt, but AFAIU, index % 2 == 0 (and index /= 2 below) would be transformed to binary and / shift right anyway, and they would look more human-readable.

return Ok(alloc::vec![]);
}

let empty_hashes = compute_empty_hashes::<N, H>(hasher);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JFYI, the server implementation goes one step further and has empty subtree hashes precomputed once on program initialization. (It could be possible to go even further and embed the hash values as a constant, e.g. covering its validity via a dedicated unit test.) Doesn't matter much from the server integration perspective.

Comment on lines +102 to +105
pub trait ZksGetProofHasher {
fn update(&mut self, input: impl AsRef<[u8]>);
fn finalize_reset(&mut self) -> [u8; 32];
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • JFYI, the server also has a hasher trait, and it's more high-level. Subjectively, it's easier to work with, but I'm obviously biased as the author 🙃
  • Bikeshedding: impl AsRef looks overly complicated; just input: &[u8] would suffice (and make the trait dyn-compatible, although that's not a requirement).

Copy link
Contributor Author

@antoniolocascio antoniolocascio Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I basically copied the one in our crypto crate. Will take a look at it tomorrow, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants