Add versioning features (#387)
- Add a function to calculate the hash of the `CommonCircuitData`. The hash uniquely identify the `CommonCircuitData` used for a circuit/proof. Serializing the struct is not enough because the polynomial identities of the custom gates are not serialized (only their parameters are); so I made a function to extract "fingerprints" of the custom gates by evaluating them over a predefined list of uniform values, and then doing a random linear combination over the results.
- Store the full verifier only circuit data of a proof in the MainPod so that we can verify pods from old circuits in new circuits and code
- Store the hash of the `CommonCircuitData` in the MainPod so that we can reject verifying old pods that use a different `CommonCircuitData` than the current one. This has two goals
- If the `CommonCircuitData` changes it's very likely that the verification will fail, but it will be hard to debug. Doing this early check helps identify the origin of the verification failure as early as possible
- There's a chance that the verification could succeed when the `CommonCircuitData` changes, and that could be dangerous because the verification will be doing different checks than the ones intended for the original proof, so we may be skipping some constraints that could lead to exploiting the system. For this reason, whenever the common circuit data hash changes, all previous verifying keys should be discarded (that is, not included in the VDSet)
- The fingerprint only has ~64 bits and the "random evaluation point" is fixed. The assumption is that the pod developers are not malicious and are not changing the gates such that different gates give the same fingerprint. With this assumption, I find it reasonable to assume that with high probability if a gate changes, its fingerprint changes as well.
- Add a github action that updates a wiki page with a table that contains: date, commit, params hash (with a link to the actual params), verifier data only circuit data hash and common circuit data hash. This will make it easy to track when the common circuit data changes as well as track the verifier data corresponding to various versions (identified by commit)
- The edited page is this one https://github.com/0xPARC/pod2/wiki/MainPod-circuit-info
Resolve https://github.com/0xPARC/pod2/issues/386
Summary of breaking changes:
- The `RecursivePod` trait has a new method `common_hash` that needs to return the result of `hash_common_data` on the `CommonCircuitData` that the circuit uses.
This commit is contained in:
parent
594c4d2e63
commit
656cae77e0
12 changed files with 357 additions and 27 deletions
|
|
@ -14,9 +14,10 @@ use crate::{
|
|||
cache::{self, CacheEntry},
|
||||
cache_get_standard_rec_main_pod_common_circuit_data,
|
||||
circuits::mainpod::{CustomPredicateVerification, MainPodVerifyInput, MainPodVerifyTarget},
|
||||
deserialize_proof,
|
||||
deserialize_proof, deserialize_verifier_only,
|
||||
emptypod::EmptyPod,
|
||||
error::{Error, Result},
|
||||
hash_common_data,
|
||||
mock::emptypod::MockEmptyPod,
|
||||
primitives::{ec::schnorr::SecretKey, merkletree::MerkleClaimAndProof},
|
||||
recursion::{
|
||||
|
|
@ -25,7 +26,7 @@ use crate::{
|
|||
serialization::{
|
||||
CircuitDataSerializer, CommonCircuitDataSerializer, VerifierCircuitDataSerializer,
|
||||
},
|
||||
serialize_proof,
|
||||
serialize_proof, serialize_verifier_only,
|
||||
signedpod::SignedPod,
|
||||
},
|
||||
middleware::{
|
||||
|
|
@ -482,10 +483,12 @@ impl PodProver for Prover {
|
|||
// get the id out of the public statements
|
||||
let id: PodId = PodId(calculate_id(&public_statements, params));
|
||||
|
||||
let common_hash: String = cache_get_rec_main_pod_common_hash(params).clone();
|
||||
let proofs = inputs
|
||||
.recursive_pods
|
||||
.iter()
|
||||
.map(|pod| {
|
||||
assert_eq!(pod.common_hash(), common_hash);
|
||||
assert_eq!(inputs.vd_set.root(), pod.vd_set().root());
|
||||
ProofWithPublicInputs {
|
||||
proof: pod.proof(),
|
||||
|
|
@ -528,6 +531,8 @@ impl PodProver for Prover {
|
|||
|
||||
Ok(Box::new(MainPod {
|
||||
params: params.clone(),
|
||||
verifier_only: circuit_data.verifier_only.clone(),
|
||||
common_hash,
|
||||
id,
|
||||
vd_set: inputs.vd_set,
|
||||
public_statements,
|
||||
|
|
@ -540,6 +545,8 @@ impl PodProver for Prover {
|
|||
pub struct MainPod {
|
||||
params: Params,
|
||||
id: PodId,
|
||||
verifier_only: VerifierOnlyCircuitData,
|
||||
common_hash: String,
|
||||
/// vds_root is the merkle-root of the `VDSet`, which contains the
|
||||
/// verifier_data hashes of the allowed set of VerifierOnlyCircuitData, for
|
||||
/// the succession of recursive MainPods, which when proving the POD, it is
|
||||
|
|
@ -605,10 +612,20 @@ pub fn cache_get_rec_main_pod_common_circuit_data(
|
|||
.expect("cache ok")
|
||||
}
|
||||
|
||||
pub fn cache_get_rec_main_pod_common_hash(params: &Params) -> CacheEntry<String> {
|
||||
cache::get("rec_main_pod_common_hash", params, |params| {
|
||||
let common = &*cache_get_rec_main_pod_common_circuit_data(params);
|
||||
hash_common_data(common).expect("hash ok")
|
||||
})
|
||||
.expect("cache ok")
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Data {
|
||||
public_statements: Vec<Statement>,
|
||||
proof: String,
|
||||
verifier_only: String,
|
||||
common_hash: String,
|
||||
}
|
||||
|
||||
impl MainPod {
|
||||
|
|
@ -626,6 +643,14 @@ impl Pod for MainPod {
|
|||
&self.params
|
||||
}
|
||||
fn verify(&self) -> Result<()> {
|
||||
// 0. Assert that the CommonCircuitData of the pod is the current one
|
||||
let expect_common_hash = &*cache_get_rec_main_pod_common_hash(&self.params);
|
||||
if &self.common_hash != expect_common_hash {
|
||||
return Err(Error::custom(format!(
|
||||
"The pod common_hash: {} is different than the current one: {}",
|
||||
self.common_hash, expect_common_hash,
|
||||
)));
|
||||
}
|
||||
// 2. get the id out of the public statements
|
||||
let id = PodId(calculate_id(&self.public_statements, &self.params));
|
||||
if id != self.id {
|
||||
|
|
@ -679,6 +704,8 @@ impl Pod for MainPod {
|
|||
serde_json::to_value(Data {
|
||||
proof: serialize_proof(&self.proof),
|
||||
public_statements: self.public_statements.clone(),
|
||||
verifier_only: serialize_verifier_only(&self.verifier_only),
|
||||
common_hash: self.common_hash.clone(),
|
||||
})
|
||||
.expect("serialization to json")
|
||||
}
|
||||
|
|
@ -686,9 +713,10 @@ impl Pod for MainPod {
|
|||
|
||||
impl RecursivePod for MainPod {
|
||||
fn verifier_data(&self) -> VerifierOnlyCircuitData {
|
||||
let rec_main_pod_verifier_circuit_data =
|
||||
cache_get_rec_main_pod_verifier_circuit_data(&self.params);
|
||||
rec_main_pod_verifier_circuit_data.verifier_only.clone()
|
||||
self.verifier_only.clone()
|
||||
}
|
||||
fn common_hash(&self) -> String {
|
||||
self.common_hash.clone()
|
||||
}
|
||||
fn proof(&self) -> Proof {
|
||||
self.proof.clone()
|
||||
|
|
@ -705,9 +733,12 @@ impl RecursivePod for MainPod {
|
|||
let data: Data = serde_json::from_value(data)?;
|
||||
let common = cache_get_rec_main_pod_common_circuit_data(¶ms);
|
||||
let proof = deserialize_proof(&common, &data.proof)?;
|
||||
let verifier_only = deserialize_verifier_only(&data.verifier_only)?;
|
||||
Ok(Box::new(Self {
|
||||
params,
|
||||
id,
|
||||
verifier_only,
|
||||
common_hash: data.common_hash,
|
||||
vd_set,
|
||||
proof,
|
||||
public_statements: data.public_statements,
|
||||
|
|
@ -1019,4 +1050,13 @@ pub mod tests {
|
|||
let pod = (proof.pod as Box<dyn Any>).downcast::<MainPod>().unwrap();
|
||||
Ok(pod.verify()?)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_common() {
|
||||
use pretty_assertions::assert_eq;
|
||||
let params = Params::default();
|
||||
let main_common = &*cache_get_rec_main_pod_common_circuit_data(¶ms);
|
||||
let std_common = &*cache_get_standard_rec_main_pod_common_circuit_data();
|
||||
assert_eq!(std_common.0, main_common.0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue