Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
39905dc
Add merkle verify gate
4l0n50 Sep 22, 2025
ade39b4
Move merkle air
4l0n50 Sep 22, 2025
f16c2f6
Fix circuit building hash size mismatch
4l0n50 Sep 23, 2025
a859dee
Simplify type parameters
4l0n50 Sep 23, 2025
6f184d8
Minor
4l0n50 Sep 24, 2025
555b296
Merge with main
4l0n50 Sep 24, 2025
126c0d3
Make merkle configuration dynamic
4l0n50 Sep 25, 2025
1eb65af
Merge with main
4l0n50 Sep 25, 2025
c854951
Address reviews
4l0n50 Sep 25, 2025
780c10a
Minor
4l0n50 Sep 25, 2025
39d04c8
Missing file
4l0n50 Sep 25, 2025
2ec1c5e
Make merkle verify air no std
4l0n50 Sep 25, 2025
62c879d
Merge with main
4l0n50 Sep 29, 2025
537216a
Merge remote-tracking branch 'origin/main' into alonso/merkle_gate_si…
4l0n50 Sep 30, 2025
4dfbed9
Address Reviews and Add config to enabled ops
4l0n50 Sep 30, 2025
a642f51
Missing changes
4l0n50 Sep 30, 2025
cbb97e7
Update circuit-prover/src/air/merkle_verify_air.rs
4l0n50 Sep 30, 2025
deaef21
Clipy
4l0n50 Sep 30, 2025
5b0c2ab
Merge remote-tracking branch 'origin/main' into alonso/merkle_gate_si…
4l0n50 Oct 2, 2025
94fcc1f
Add packing option
4l0n50 Oct 6, 2025
6560907
Merge with main
4l0n50 Oct 6, 2025
b3a38f8
Update ci
4l0n50 Oct 6, 2025
fd559f7
Adress some reviews
4l0n50 Oct 6, 2025
a73e00c
Maldito clippy
4l0n50 Oct 6, 2025
3a52319
Minor
4l0n50 Oct 6, 2025
6a5769f
Remove compression function dependency for circuits
4l0n50 Oct 7, 2025
b819d47
Merge with main
4l0n50 Oct 7, 2025
8295a0c
Address reviews
4l0n50 Oct 7, 2025
2a615a4
Add test for private data generation
4l0n50 Oct 7, 2025
8249117
rename
sai-deng Oct 7, 2025
7ed016e
Sai's fixes
sai-deng Oct 8, 2025
98e3f78
fix ci
sai-deng Oct 8, 2025
33c8543
Move TODO and add logger to mmcs_verify
4l0n50 Oct 8, 2025
2506178
Remove old TODOs
4l0n50 Oct 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ itertools = { version = "0.14.0", default-features = false, features = [
"use_alloc",
] }
rand = { version = "0.9.2", default-features = false, features = ["small_rng"] }
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
serde = { version = "1.0", default-features = false, features = [
"derive",
"alloc",
] }
thiserror = { version = "2.0", default-features = false }

# Local dependencies
Expand Down
12 changes: 8 additions & 4 deletions circuit-prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ keywords.workspace = true
categories.workspace = true

[dependencies]
itertools.workspace = true
p3-air.workspace = true
p3-baby-bear.workspace = true
p3-challenger.workspace = true
Expand All @@ -20,24 +21,27 @@ p3-dft.workspace = true
p3-field.workspace = true
p3-fri.workspace = true
p3-goldilocks.workspace = true
p3-keccak.workspace = true
p3-koala-bear.workspace = true
p3-matrix.workspace = true
p3-merkle-tree.workspace = true
p3-merkle-tree-air.workspace = true
p3-symmetric.workspace = true
p3-uni-stark.workspace = true
postcard = { version = "1.0", default-features = false, features = ["alloc"] }
rand.workspace = true
thiserror.workspace = true

[dev-dependencies]
postcard = { version = "1.0", default-features = false, features = ["alloc"] }
rand.workspace = true
tracing = { version = "0.1.40", default-features = false, features = ["attributes"] }
tracing = { version = "0.1.40", default-features = false, features = [
"attributes",
] }

[[example]]
name = "fibonacci"
path = "examples/fibonacci.rs"

[[example]]
name = "fake_merkle_verify"
path = "examples/fake_merkle_verify.rs"
name = "merkle_verify"
path = "examples/merkle_verify.rs"
83 changes: 0 additions & 83 deletions circuit-prover/examples/fake_merkle_verify.rs

This file was deleted.

9 changes: 5 additions & 4 deletions circuit-prover/examples/fibonacci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@ use std::env;
/// Fibonacci circuit: Compute F(n) and prove correctness
/// Public input: expected_result (F(n))
use p3_baby_bear::BabyBear;
use p3_circuit::builder::CircuitBuilder;
use p3_circuit::CircuitBuilder;
use p3_circuit_prover::config::babybear_config::build_standard_config_babybear;
use p3_circuit_prover::prover::ProverError;
use p3_circuit_prover::{MultiTableProver, TablePacking};
use p3_field::PrimeCharacteristicRing;
use p3_field::extension::BinomialExtensionField;

type F = BabyBear;
type F = BinomialExtensionField<BabyBear, 4>;

fn main() -> Result<(), ProverError> {
let n = env::args()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(100);

let mut builder = CircuitBuilder::<F>::new();
let mut builder = CircuitBuilder::new();

// Public input: expected F(n)
let expected_result = builder.add_public_input();
Expand All @@ -43,7 +44,7 @@ fn main() -> Result<(), ProverError> {
runner.set_public_inputs(&[expected_fib])?;

let traces = runner.run()?;
let config = build_standard_config_babybear();
let (config, _) = build_standard_config_babybear::<F>();
let table_packing = TablePacking::from_counts(4, 1);
let multi_prover = MultiTableProver::new(config).with_table_packing(table_packing);
let proof = multi_prover.prove_all_tables(&traces)?;
Expand Down
115 changes: 115 additions & 0 deletions circuit-prover/examples/merkle_verify.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use std::env;

/// Merkle verification circuit: Prove knowledge of a leaf in a Merkle tree
/// Public inputs: leaf_hash, leaf_index, expected_root
/// Private inputs: merkle path (siblings + directions)
use p3_baby_bear::BabyBear;
use p3_circuit::op::MerkleVerifyConfig;
use p3_circuit::tables::MerklePrivateData;
use p3_circuit::{CircuitBuilder, NonPrimitiveOpPrivateData};
use p3_circuit_prover::MultiTableProver;
use p3_circuit_prover::config::babybear_config::build_standard_config_babybear;
use p3_circuit_prover::prover::ProverError;
use p3_field::PrimeCharacteristicRing;
use p3_field::extension::BinomialExtensionField;

type F = BinomialExtensionField<BabyBear, 4>;

fn main() -> Result<(), ProverError> {
let depth = env::args().nth(1).and_then(|s| s.parse().ok()).unwrap_or(3);
let (config, merkle_config) = build_standard_config_babybear();

let mut builder = CircuitBuilder::new();

// Public inputs: leaf hash and expected root hash
let leaf_hash = vec![builder.add_public_input(), builder.add_public_input()];
let index_expr = builder.add_public_input();
let expected_root = vec![builder.add_public_input(), builder.add_public_input()];

// Add a Merkle verification operation
// This declares that leaf_hash and expected_root are connected to witness bus
// The AIR constraints will verify the Merkle path is valid
let merkle_op_id =
builder.add_merkle_verify(merkle_config.clone(), leaf_hash, index_expr, expected_root);

let circuit = builder.build()?;
let mut runner = circuit.runner();

// Set public inputs
let leaf_value = [F::ZERO, F::from_u64(42)]; // Our leaf value
let siblings: Vec<(Vec<F>, Option<Vec<F>>)> = (0..depth)
.map(|i| {
(
vec![F::ZERO, F::from_u64((i + 1) * 10)],
if i % 2 == 0 {
None
} else {
Some(vec![F::ZERO, F::from_u64(i + 1)])
},
)
})
.collect();
let directions: Vec<bool> = (0..depth).map(|i| i % 2 == 0).collect();
let index_value = F::from_u64(
(0..32)
.zip(directions.iter())
.filter(|(_, dir)| **dir)
.map(|(i, _)| 1 << i)
.sum(),
);
let expected_root_value =
compute_merkle_root(&merkle_config, &leaf_value, &siblings, &directions);

runner.set_public_inputs(&[
leaf_value[0],
leaf_value[1],
index_value,
expected_root_value[0],
expected_root_value[1],
])?;

// Set private Merkle path data
runner.set_non_primitive_op_private_data(
merkle_op_id,
NonPrimitiveOpPrivateData::MerkleVerify(MerklePrivateData {
path_siblings: siblings,
}),
)?;

let traces = runner.run()?;
let multi_prover = MultiTableProver::new(config).with_merkle_table(merkle_config.into());
let proof = multi_prover.prove_all_tables(&traces)?;
multi_prover.verify_all_tables(&proof)?;

println!(
"✅ Verified Merkle path for leaf {leaf_value:?}, index {index_value} with depth {depth} → root {expected_root_value:?}",
);

Ok(())
}

pub type Hash = [BabyBear; 8];

/// Simulate classical Merkle root computation for testing
fn compute_merkle_root(
merkle_config: &MerkleVerifyConfig<F>,
leaf: &[F; 2],
siblings: &[(Vec<F>, Option<Vec<F>>)],
directions: &[bool],
) -> Vec<F> {
directions.iter().zip(siblings.iter()).fold(
leaf.to_vec(),
|state, (direction, (sibling, other_sibling))| {
let (left, right) = if *direction {
(state.clone(), sibling.clone())
} else {
(sibling.clone(), state.clone())
};
let mut new_state = (merkle_config.compress)([&left, &right]);
if let Some(other_sibling) = other_sibling {
new_state = (merkle_config.compress)([&state, other_sibling]);
}
new_state
},
)
}
Loading
Loading