Skip to content

Commit f7e7216

Browse files
committed
feat(examples): add Midnight Poseidon EVM transcript end-to-end flow
Add a Midnight Poseidon example and transcript adapter that exercise proof generation, verification, and EVM verifier artifact generation. - Add Midnight EVM transcript hash helper with tests for scalar/point encoding behavior. - Add midnight_poseidon_evm example to prove/verify and emit Solidity, bytecode, and calldata artifacts. - Ignore generated Midnight artifact output directory and keep incidental example formatting tidy.
1 parent 6e4a8dd commit f7e7216

4 files changed

Lines changed: 404 additions & 4 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
.DS_Store
33

44
/target
5+
snark-verifier-sdk/target/midnight/
56
testdata
67

78
# Cargo.lock
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//! Generate and verify the Poseidon example with Midnight's EVM transcript.
2+
//! Run with:
3+
//! cargo run --example midnight_poseidon_evm --features midnight,loader_evm,revm -p snark-verifier-sdk
4+
use ff::Field;
5+
use midnight_circuits::{
6+
hash::poseidon::PoseidonChip,
7+
instructions::{hash::HashCPU, AssignmentInstructions, PublicInputInstructions},
8+
};
9+
use midnight_curves::Bls12;
10+
use midnight_proofs::poly::kzg::params::ParamsKZG;
11+
use midnight_proofs::{
12+
circuit::{Layouter, Value},
13+
plonk::Error,
14+
};
15+
use midnight_zk_stdlib::{Relation, ZkStdLib, ZkStdLibArch};
16+
use rand::{rngs::OsRng, SeedableRng};
17+
use rand_chacha::ChaCha8Rng;
18+
use snark_verifier_sdk::midnight_adapter::{MidnightBundleOptions, MidnightProofBundle};
19+
use std::path::PathBuf;
20+
21+
#[path = "support/midnight_evm_transcript.rs"]
22+
mod midnight_evm_transcript;
23+
use midnight_evm_transcript::MidnightEvmHash;
24+
25+
type F = midnight_curves::Fq;
26+
27+
#[derive(Clone, Default)]
28+
pub struct PoseidonExample;
29+
30+
impl Relation for PoseidonExample {
31+
type Instance = F;
32+
33+
type Witness = [F; 3];
34+
35+
// Expose one public output column containing the Poseidon hash value.
36+
fn format_instance(instance: &Self::Instance) -> Result<Vec<F>, Error> {
37+
Ok(vec![*instance])
38+
}
39+
40+
// Build the relation: hash witness values with Poseidon and constrain as public output.
41+
fn circuit(
42+
&self,
43+
std_lib: &ZkStdLib,
44+
layouter: &mut impl Layouter<F>,
45+
_instance: Value<Self::Instance>,
46+
witness: Value<Self::Witness>,
47+
) -> Result<(), Error> {
48+
let assigned_message = std_lib.assign_many(layouter, &witness.transpose_array())?;
49+
let output = std_lib.poseidon(layouter, &assigned_message)?;
50+
std_lib.constrain_as_public_input(layouter, &output)
51+
}
52+
53+
// Enable only Poseidon chip plumbing for this example circuit.
54+
fn used_chips(&self) -> ZkStdLibArch {
55+
ZkStdLibArch { poseidon: true, ..ZkStdLibArch::default() }
56+
}
57+
58+
// No custom relation payload is needed for this stateless demo relation.
59+
fn write_relation<W: std::io::Write>(&self, _writer: &mut W) -> std::io::Result<()> {
60+
Ok(())
61+
}
62+
63+
// No custom relation payload is needed for this stateless demo relation.
64+
fn read_relation<R: std::io::Read>(_reader: &mut R) -> std::io::Result<Self> {
65+
Ok(PoseidonExample)
66+
}
67+
}
68+
69+
fn main() {
70+
const K: u32 = 6;
71+
let srs = ParamsKZG::<Bls12>::unsafe_setup(K, OsRng);
72+
73+
// Build proving/verifying keys for the Poseidon relation.
74+
let relation = PoseidonExample;
75+
let vk = midnight_zk_stdlib::setup_vk(&srs, &relation);
76+
let pk = midnight_zk_stdlib::setup_pk(&relation, &vk);
77+
78+
// Sample random witness and derive the expected public output.
79+
let mut rng = ChaCha8Rng::from_entropy();
80+
let witness: [F; 3] = core::array::from_fn(|_| F::random(&mut rng));
81+
let instance = <PoseidonChip<F> as HashCPU<F, F>>::hash(&witness);
82+
83+
// Produce and verify an EVM-transcript proof using MidnightEvmHash.
84+
let proof = midnight_zk_stdlib::prove::<PoseidonExample, MidnightEvmHash>(
85+
&srs, &pk, &relation, &instance, witness, OsRng,
86+
)
87+
.expect("EVM-transcript proof generation should not fail");
88+
89+
midnight_zk_stdlib::verify::<PoseidonExample, MidnightEvmHash>(
90+
&srs.verifier_params(),
91+
&vk,
92+
&instance,
93+
None,
94+
&proof,
95+
)
96+
.expect("EVM-transcript native verification should succeed");
97+
98+
// Build adapter bundle used for Solidity/codegen bridge.
99+
let bundle = MidnightProofBundle::from_vk(
100+
srs.verifier_params(),
101+
vk.vk().clone(),
102+
proof.clone(),
103+
vec![vec![instance]],
104+
MidnightBundleOptions::default(),
105+
)
106+
.expect("Bundle creation should succeed");
107+
108+
// Generate Solidity source, compile bytecode, and encode calldata artifacts.
109+
let solidity = bundle
110+
.generate_evm_verifier_solidity()
111+
.expect("failed to generate Solidity verifier source");
112+
let bytecode =
113+
bundle.generate_evm_verifier_bytecode().expect("failed to compile Solidity verifier");
114+
let calldata = bundle.encode_evm_calldata().expect("failed to encode EVM calldata");
115+
116+
let out_dir =
117+
std::env::var_os("MIDNIGHT_ARTIFACT_DIR").map(PathBuf::from).unwrap_or_else(|| {
118+
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("target").join("midnight")
119+
});
120+
std::fs::create_dir_all(&out_dir).expect("failed to create output directory");
121+
let solidity_path = out_dir.join("MidnightPoseidonVerifier.sol");
122+
let bytecode_path = out_dir.join("midnight_poseidon.bytecode");
123+
let calldata_path = out_dir.join("midnight_poseidon.calldata");
124+
125+
// Persist generated artifacts to disk (default: target/midnight).
126+
std::fs::write(&solidity_path, &solidity).expect("failed to write Solidity verifier");
127+
std::fs::write(&bytecode_path, format!("0x{}", hex::encode(&bytecode)))
128+
.expect("failed to write verifier bytecode");
129+
std::fs::write(&calldata_path, hex::encode(&calldata)).expect("failed to write calldata");
130+
131+
println!("proof bytes: {}", proof.len());
132+
println!("deployment code bytes: {}", bytecode.len());
133+
println!("calldata bytes: {}", calldata.len());
134+
println!("wrote {}", solidity_path.display());
135+
println!("wrote {}", bytecode_path.display());
136+
println!("wrote {}", calldata_path.display());
137+
138+
#[cfg(feature = "revm")]
139+
{
140+
// Optional local revm simulation for end-to-end gas measurement.
141+
if std::env::var("RUN_REVM").ok().as_deref() == Some("1") {
142+
let gas = bundle
143+
.verify_with_generated_solidity_revm()
144+
.expect("revm verification should succeed");
145+
println!("revm gas: {gas}");
146+
} else {
147+
println!("revm verification skipped (set RUN_REVM=1 to run local revm simulation)");
148+
}
149+
}
150+
}

snark-verifier-sdk/examples/standard_plonk.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ mod application {
4747
[a, b, c].map(|column| meta.query_advice(column, Rotation::cur()));
4848
let [q_a, q_b, q_c, q_ab, constant] = [q_a, q_b, q_c, q_ab, constant]
4949
.map(|column| meta.query_fixed(column, Rotation::cur()));
50-
let instance = meta.query_instance(instance, Rotation::cur());
50+
let instance = meta.query_instance(instance, Rotation::cur());
5151
Some(
5252
q_a * a.clone()
5353
+ q_b * b.clone()
@@ -200,9 +200,7 @@ fn main() {
200200
}
201201
}
202202
} else {
203-
println!(
204-
"revm verification skipped (set RUN_REVM=1 to run local revm simulation)"
205-
);
203+
println!("revm verification skipped (set RUN_REVM=1 to run local revm simulation)");
206204
}
207205
}
208206
}

0 commit comments

Comments
 (0)