Skip to content

Commit 4ee3c5c

Browse files
committed
refactor(stm): compute the certificate key through the verification key provider
1 parent b918b3b commit 4ee3c5c

12 files changed

Lines changed: 351 additions & 220 deletions

File tree

mithril-stm/src/circuits/circuit_verification_key_provider.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ pub(crate) struct CircuitVerificationKeyProvider<C: CircuitKeyGenerator> {
2222

2323
#[allow(dead_code)] // consumed by the certificate and recursive setups in the following steps
2424
impl<C: CircuitKeyGenerator> CircuitVerificationKeyProvider<C> {
25-
/// Builds a provider rooted at `base_dir`. The cached verifying key is compared against
26-
/// `expected_verification_key_bytes`; empty bytes skip the comparison and trust the cache
27-
/// directory (used by content-keyed test caches). Keys are computed from `circuit` on a miss.
25+
/// Builds a provider rooted at `base_dir`. On read, the cached verifying key is compared against
26+
/// `expected_verification_key_bytes` and recomputed on a mismatch; empty expected bytes
27+
/// therefore always recompute (no real key matches them). Keys are computed from `circuit` on a
28+
/// miss.
2829
pub(crate) fn new(
2930
base_dir: PathBuf,
3031
circuit_name: &str,
@@ -82,9 +83,13 @@ impl<C: CircuitKeyGenerator> CircuitVerificationKeyProvider<C> {
8283

8384
#[cfg(test)]
8485
impl<C: CircuitKeyGenerator> CircuitVerificationKeyProvider<C> {
85-
fn verification_key_path(&self) -> &std::path::Path {
86+
pub(crate) fn verification_key_path(&self) -> &std::path::Path {
8687
self.cache.verification_key_path()
8788
}
89+
90+
pub(crate) fn proving_key_path(&self) -> &std::path::Path {
91+
self.cache.proving_key_path()
92+
}
8893
}
8994

9095
#[cfg(test)]
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
// Per-circuit key newtypes for the non-recursive (certificate) circuit, and the circuit's
2+
// implementation of [`CircuitKeyGenerator`]. The newtypes wrap Midnight's self-describing
3+
// `MidnightVK` / `MidnightPK` and delegate their byte (de)serialization to the impls in
4+
// `key_serialization`.
5+
use midnight_curves::Bls12;
6+
use midnight_proofs::poly::kzg::params::ParamsKZG;
7+
use midnight_zk_stdlib::{self as zk, MidnightPK, MidnightVK};
8+
use serde::{Deserialize, Serialize};
9+
10+
use crate::circuits::circuit_key_generator::CircuitKeyGenerator;
11+
use crate::circuits::circuit_verification_key_provider::CircuitVerificationKeyProvider;
12+
use crate::codec::{TryFromBytes, TryToBytes};
13+
use crate::{Parameters, StmResult};
14+
15+
use super::circuit::StmCertificateCircuit;
16+
17+
/// Verifying key of the non-recursive certificate circuit.
18+
#[derive(Clone, Debug, Serialize, Deserialize)]
19+
pub(crate) struct NonRecursiveCircuitVerifyingKey(
20+
#[serde(with = "midnight_verifying_key_serde")] MidnightVK,
21+
);
22+
23+
/// Proving key of the non-recursive certificate circuit.
24+
pub(crate) struct NonRecursiveCircuitProvingKey(MidnightPK<StmCertificateCircuit>);
25+
26+
impl NonRecursiveCircuitVerifyingKey {
27+
/// Wraps a Midnight verifying key.
28+
pub(crate) fn new(midnight_vk: MidnightVK) -> Self {
29+
Self(midnight_vk)
30+
}
31+
32+
/// Borrows the wrapped Midnight verifying key.
33+
pub(crate) fn midnight_vk(&self) -> &MidnightVK {
34+
&self.0
35+
}
36+
}
37+
38+
/// Serde for the wrapped Midnight verifying key: the raw-bytes encoding, matching the embedded
39+
/// SNARK-proof wire format.
40+
mod midnight_verifying_key_serde {
41+
use midnight_proofs::utils::SerdeFormat;
42+
use midnight_zk_stdlib::MidnightVK;
43+
use serde::{Deserializer, Serializer};
44+
45+
pub(super) fn serialize<S: Serializer>(
46+
verifying_key: &MidnightVK,
47+
serializer: S,
48+
) -> Result<S::Ok, S::Error> {
49+
let mut buffer = Vec::new();
50+
verifying_key
51+
.write(&mut buffer, SerdeFormat::RawBytes)
52+
.map_err(serde::ser::Error::custom)?;
53+
serializer.serialize_bytes(&buffer)
54+
}
55+
56+
pub(super) fn deserialize<'de, D: Deserializer<'de>>(
57+
deserializer: D,
58+
) -> Result<MidnightVK, D::Error> {
59+
let bytes: Vec<u8> = serde::Deserialize::deserialize(deserializer)?;
60+
MidnightVK::read(&mut bytes.as_slice(), SerdeFormat::RawBytes)
61+
.map_err(serde::de::Error::custom)
62+
}
63+
}
64+
65+
impl NonRecursiveCircuitProvingKey {
66+
/// Borrows the wrapped Midnight proving key, for proof generation.
67+
pub(crate) fn midnight_pk(&self) -> &MidnightPK<StmCertificateCircuit> {
68+
&self.0
69+
}
70+
}
71+
72+
impl CircuitVerificationKeyProvider<StmCertificateCircuit> {
73+
/// Production certificate-circuit provider: builds the circuit from `parameters`, roots the
74+
/// cache at the temporary directory, and validates against the embedded production verifying key.
75+
pub(crate) fn for_non_recursive_circuit(
76+
parameters: &Parameters,
77+
merkle_tree_depth: u32,
78+
) -> StmResult<Self> {
79+
let circuit = StmCertificateCircuit::try_new(parameters, merkle_tree_depth)?;
80+
Ok(Self::new(
81+
std::env::temp_dir(),
82+
"non-recursive-keys",
83+
super::NON_RECURSIVE_CIRCUIT_VERIFICATION_KEY_FOR_PRODUCTION,
84+
circuit,
85+
))
86+
}
87+
}
88+
89+
impl TryToBytes for NonRecursiveCircuitVerifyingKey {
90+
fn to_bytes_vec(&self) -> StmResult<Vec<u8>> {
91+
self.0.to_bytes_vec()
92+
}
93+
}
94+
95+
impl TryFromBytes for NonRecursiveCircuitVerifyingKey {
96+
fn try_from_bytes(bytes: &[u8]) -> StmResult<Self> {
97+
Ok(Self(MidnightVK::try_from_bytes(bytes)?))
98+
}
99+
}
100+
101+
impl TryToBytes for NonRecursiveCircuitProvingKey {
102+
fn to_bytes_vec(&self) -> StmResult<Vec<u8>> {
103+
self.0.to_bytes_vec()
104+
}
105+
}
106+
107+
impl TryFromBytes for NonRecursiveCircuitProvingKey {
108+
fn try_from_bytes(bytes: &[u8]) -> StmResult<Self> {
109+
Ok(Self(MidnightPK::<StmCertificateCircuit>::try_from_bytes(
110+
bytes,
111+
)?))
112+
}
113+
}
114+
115+
impl CircuitKeyGenerator for StmCertificateCircuit {
116+
type VerifyingKey = NonRecursiveCircuitVerifyingKey;
117+
type ProvingKey = NonRecursiveCircuitProvingKey;
118+
119+
fn generate_key_pair(
120+
&self,
121+
srs: &ParamsKZG<Bls12>,
122+
) -> StmResult<(Self::VerifyingKey, Self::ProvingKey)> {
123+
let mut certificate_srs = srs.clone();
124+
zk::downsize_srs_for_relation(&mut certificate_srs, self);
125+
let verifying_key = zk::setup_vk(&certificate_srs, self);
126+
let proving_key = zk::setup_pk(self, &verifying_key);
127+
Ok((
128+
NonRecursiveCircuitVerifyingKey(verifying_key),
129+
NonRecursiveCircuitProvingKey(proving_key),
130+
))
131+
}
132+
}
133+
134+
#[cfg(test)]
135+
mod tests {
136+
use std::fs;
137+
138+
use midnight_proofs::poly::commitment::Params;
139+
use midnight_proofs::poly::kzg::params::ParamsKZG;
140+
use midnight_zk_stdlib::MidnightCircuit;
141+
use rand_chacha::ChaCha20Rng;
142+
use rand_core::SeedableRng;
143+
144+
use super::{NonRecursiveCircuitProvingKey, NonRecursiveCircuitVerifyingKey};
145+
use crate::Parameters;
146+
use crate::circuits::circuit_key_generator::CircuitKeyGenerator;
147+
use crate::circuits::circuit_verification_key_provider::CircuitVerificationKeyProvider;
148+
use crate::circuits::halo2::circuit::StmCertificateCircuit;
149+
use crate::codec::{TryFromBytes, TryToBytes};
150+
151+
#[test]
152+
fn generate_key_pair_downsizes_a_clone_and_round_trips() {
153+
let parameters = Parameters {
154+
k: 3,
155+
m: 10,
156+
phi_f: 0.2,
157+
};
158+
let merkle_tree_depth = 4;
159+
let circuit = StmCertificateCircuit::try_new(&parameters, merkle_tree_depth)
160+
.expect("certificate circuit should build");
161+
// Oversized on purpose: the generator must clone and downsize to the circuit's degree
162+
// (keygen would otherwise fail), and the caller's SRS must be left untouched.
163+
let oversized_degree = MidnightCircuit::from_relation(&circuit).min_k() + 1;
164+
let srs = ParamsKZG::unsafe_setup(oversized_degree, ChaCha20Rng::seed_from_u64(42));
165+
166+
let (verifying_key, proving_key) = circuit
167+
.generate_key_pair(&srs)
168+
.expect("key generation should succeed");
169+
170+
assert_eq!(
171+
srs.max_k(),
172+
oversized_degree,
173+
"the generator must downsize a clone, leaving the caller's SRS untouched"
174+
);
175+
176+
let verifying_key_bytes = verifying_key.to_bytes_vec().expect("serialize should succeed");
177+
let restored_verifying_key =
178+
NonRecursiveCircuitVerifyingKey::try_from_bytes(&verifying_key_bytes)
179+
.expect("deserialize should succeed");
180+
assert_eq!(
181+
verifying_key_bytes,
182+
restored_verifying_key.to_bytes_vec().unwrap(),
183+
"verifying key bytes must be stable across a round trip"
184+
);
185+
186+
let proving_key_bytes = proving_key.to_bytes_vec().expect("serialize should succeed");
187+
let restored_proving_key =
188+
NonRecursiveCircuitProvingKey::try_from_bytes(&proving_key_bytes)
189+
.expect("deserialize should succeed");
190+
assert_eq!(
191+
proving_key_bytes,
192+
restored_proving_key.to_bytes_vec().unwrap(),
193+
"proving key bytes must be stable across a round trip"
194+
);
195+
}
196+
197+
#[test]
198+
fn provider_returns_cached_verifying_key_on_hit() {
199+
let parameters = Parameters {
200+
k: 3,
201+
m: 10,
202+
phi_f: 0.2,
203+
};
204+
let merkle_tree_depth = 4;
205+
let circuit = StmCertificateCircuit::try_new(&parameters, merkle_tree_depth)
206+
.expect("certificate circuit should build");
207+
let circuit_degree = MidnightCircuit::from_relation(&circuit).min_k();
208+
let srs = ParamsKZG::unsafe_setup(circuit_degree, ChaCha20Rng::seed_from_u64(42));
209+
210+
// Derive the real key pair and use its verifying-key bytes as the cache golden, so a
211+
// pre-populated cache is a fresh hit rather than stale.
212+
let (verifying_key, proving_key) = circuit
213+
.generate_key_pair(&srs)
214+
.expect("key generation should succeed");
215+
let verifying_key_bytes = verifying_key.to_bytes_vec().unwrap();
216+
let proving_key_bytes = proving_key.to_bytes_vec().unwrap();
217+
218+
let base_dir = std::env::temp_dir().join(current_function!());
219+
fs::remove_dir_all(&base_dir).ok();
220+
let provider = CircuitVerificationKeyProvider::new(
221+
base_dir.clone(),
222+
"non-recursive",
223+
&verifying_key_bytes,
224+
circuit,
225+
);
226+
fs::create_dir_all(provider.verification_key_path().parent().unwrap()).unwrap();
227+
fs::write(provider.verification_key_path(), &verifying_key_bytes).unwrap();
228+
fs::write(provider.proving_key_path(), &proving_key_bytes).unwrap();
229+
230+
let (hit_verifying_key, _) = provider.key_pair(&srs).expect("cache hit should succeed");
231+
232+
assert_eq!(
233+
hit_verifying_key.to_bytes_vec().unwrap(),
234+
verifying_key_bytes,
235+
"the provider must return the real verifying key read from the populated cache"
236+
);
237+
fs::remove_dir_all(&base_dir).ok();
238+
}
239+
}

mithril-stm/src/circuits/halo2/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub(crate) mod gadgets;
2323
#[cfg_attr(not(test), allow(dead_code))]
2424
pub(crate) mod key_serialization;
2525
#[cfg_attr(not(test), allow(dead_code))]
26+
pub(crate) mod keys;
27+
#[cfg_attr(not(test), allow(dead_code))]
2628
pub(crate) mod types;
2729
#[cfg_attr(not(test), allow(dead_code))]
2830
pub(crate) mod witness;

mithril-stm/src/proof_system/halo2_snark/circuit_verification_key.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ use serde::{Deserialize, Serialize};
55
use crate::codec::TryToBytes;
66
use crate::{StmResult, codec::TryFromBytes};
77

8-
/// Wrapper type of MidnightVK, the circuit verification key
8+
/// Wrapper type of MidnightVK, the circuit verification key.
9+
///
10+
/// The wrapper itself is currently unused; it is retained alongside its serde module, which the IVC
11+
/// verifier data still relies on to (de)serialize the certificate verifying key.
912
#[derive(Clone, Debug, Serialize, Deserialize)]
13+
#[allow(dead_code)]
1014
pub struct CircuitVerificationKey(
1115
#[serde(with = "midnight_certificate_verification_key_serde")] MidnightVK,
1216
);
1317

18+
#[allow(dead_code)]
1419
impl CircuitVerificationKey {
1520
/// Creates a new CircuitVerificationKey from a MidnightVK
1621
pub fn new(midnight_vk: MidnightVK) -> Self {

mithril-stm/src/proof_system/halo2_snark/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ mod single_signature;
1212
pub use aggregate_key::AggregateVerificationKeyForSnark;
1313
#[cfg(test)]
1414
pub(crate) use aggregate_key::RIGID_SLOT_BYTES;
15-
#[cfg(test)]
16-
pub(crate) use circuit_verification_key::CircuitVerificationKey;
1715
pub(crate) use circuit_verification_key::midnight_certificate_verification_key_serde;
1816
pub(crate) use clerk::SnarkClerk;
1917
pub(crate) use eligibility::{

0 commit comments

Comments
 (0)