recursion circuit's verifier_data_hash include constant_sigmas_cap in the hash, and add explanation (#288)
This commit is contained in:
parent
462aaee061
commit
6ab0bc52fc
2 changed files with 71 additions and 17 deletions
|
|
@ -68,30 +68,37 @@ pub static DEFAULT_VD_SET: LazyLock<VDSet> = LazyLock::new(|| {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct VDSet {
|
pub struct VDSet {
|
||||||
root: Hash,
|
root: Hash,
|
||||||
// (verifier_data, merkleproof)
|
// (verifier_data's hash, merkleproof)
|
||||||
proofs_map: HashMap<HashOut<F>, MerkleClaimAndProof>,
|
proofs_map: HashMap<HashOut<F>, MerkleClaimAndProof>,
|
||||||
}
|
}
|
||||||
impl VDSet {
|
impl VDSet {
|
||||||
/// builds the verifier_datas tree, and returns the root and the proofs
|
/// builds the verifier_datas tree, and returns the root and the proofs
|
||||||
pub fn new(tree_depth: usize, vds: &[VerifierOnlyCircuitData]) -> Result<Self> {
|
pub fn new(tree_depth: usize, vds: &[VerifierOnlyCircuitData]) -> Result<Self> {
|
||||||
// first of all, sort the vds, so that each set of verifier_datas gets
|
// compute the verifier_data's hashes
|
||||||
// the same root
|
let vds_hashes: Vec<HashOut<F>> = vds
|
||||||
let vds: Vec<&VerifierOnlyCircuitData> = vds
|
|
||||||
.iter()
|
.iter()
|
||||||
.sorted_by_key(|vd| RawValue(vd.circuit_digest.elements))
|
.map(crate::backends::plonky2::recursion::circuit::hash_verifier_data)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// before using the hash values, sort them, so that each set of
|
||||||
|
// verifier_datas gets the same VDSet root
|
||||||
|
let vds_hashes: Vec<&HashOut<F>> = vds_hashes
|
||||||
|
.iter()
|
||||||
|
.sorted_by_key(|vd| RawValue(vd.elements))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let array = Array::new(
|
let array = Array::new(
|
||||||
tree_depth,
|
tree_depth,
|
||||||
vds.iter()
|
vds_hashes
|
||||||
.map(|vd| Value::from(RawValue(vd.circuit_digest.elements)))
|
.iter()
|
||||||
|
.map(|vd| Value::from(RawValue(vd.elements)))
|
||||||
.collect(),
|
.collect(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let root = array.commitment();
|
let root = array.commitment();
|
||||||
let mut proofs_map = HashMap::<HashOut<F>, MerkleClaimAndProof>::new();
|
let mut proofs_map = HashMap::<HashOut<F>, MerkleClaimAndProof>::new();
|
||||||
|
|
||||||
for (i, vd) in vds.iter().enumerate() {
|
for (i, vd) in vds_hashes.iter().enumerate() {
|
||||||
let (value, proof) = array.prove(i)?;
|
let (value, proof) = array.prove(i)?;
|
||||||
let p = MerkleClaimAndProof {
|
let p = MerkleClaimAndProof {
|
||||||
root,
|
root,
|
||||||
|
|
@ -99,7 +106,7 @@ impl VDSet {
|
||||||
value: value.raw(),
|
value: value.raw(),
|
||||||
proof,
|
proof,
|
||||||
};
|
};
|
||||||
proofs_map.insert(vd.circuit_digest, p);
|
proofs_map.insert(**vd, p);
|
||||||
}
|
}
|
||||||
Ok(Self { root, proofs_map })
|
Ok(Self { root, proofs_map })
|
||||||
}
|
}
|
||||||
|
|
@ -113,9 +120,9 @@ impl VDSet {
|
||||||
) -> Result<Vec<MerkleClaimAndProof>> {
|
) -> Result<Vec<MerkleClaimAndProof>> {
|
||||||
let mut proofs: Vec<MerkleClaimAndProof> = vec![];
|
let mut proofs: Vec<MerkleClaimAndProof> = vec![];
|
||||||
for vd in vds {
|
for vd in vds {
|
||||||
let p =
|
let p = self
|
||||||
self.proofs_map
|
.proofs_map
|
||||||
.get(&vd.circuit_digest)
|
.get(&crate::backends::plonky2::recursion::circuit::hash_verifier_data(vd))
|
||||||
.ok_or(crate::middleware::Error::custom(
|
.ok_or(crate::middleware::Error::custom(
|
||||||
"verifier_data not found in VDSet".to_string(),
|
"verifier_data not found in VDSet".to_string(),
|
||||||
))?;
|
))?;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,10 @@ use plonky2::{
|
||||||
self,
|
self,
|
||||||
field::{extension::quintic::QuinticExtension, types::Field},
|
field::{extension::quintic::QuinticExtension, types::Field},
|
||||||
gates::{gate::GateRef, noop::NoopGate},
|
gates::{gate::GateRef, noop::NoopGate},
|
||||||
hash::hash_types::HashOutTarget,
|
hash::{
|
||||||
|
hash_types::{HashOut, HashOutTarget},
|
||||||
|
poseidon::PoseidonHash,
|
||||||
|
},
|
||||||
iop::{
|
iop::{
|
||||||
target::Target,
|
target::Target,
|
||||||
witness::{PartialWitness, WitnessWrite},
|
witness::{PartialWitness, WitnessWrite},
|
||||||
|
|
@ -24,6 +27,7 @@ use plonky2::{
|
||||||
CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, VerifierCircuitData,
|
CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, VerifierCircuitData,
|
||||||
VerifierCircuitTarget, VerifierOnlyCircuitData,
|
VerifierCircuitTarget, VerifierOnlyCircuitData,
|
||||||
},
|
},
|
||||||
|
config::Hasher,
|
||||||
proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget},
|
proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget},
|
||||||
},
|
},
|
||||||
util::log2_ceil,
|
util::log2_ceil,
|
||||||
|
|
@ -201,7 +205,18 @@ impl<I: InnerCircuit> RecursiveCircuit<I> {
|
||||||
let verified_proofs = (0..arity)
|
let verified_proofs = (0..arity)
|
||||||
.map(|i| VerifiedProofTarget {
|
.map(|i| VerifiedProofTarget {
|
||||||
public_inputs: proofs_targ[i].public_inputs.clone(),
|
public_inputs: proofs_targ[i].public_inputs.clone(),
|
||||||
verifier_data_hash: verifier_datas_targ[i].circuit_digest,
|
// note: here we're hashing the verifier_data as Hash(vd.circuit_digest,
|
||||||
|
// vd.constant_sigmas_cap), despite the circuit_digest is already a hash containing
|
||||||
|
// the constant_sigmas_cap. Conceptually we would use the circuit_digest as the hash
|
||||||
|
// of the verifier_data, but unfortunately, the recursion verification circuit does
|
||||||
|
// not ensure this link. Alternatively we could calculate an modified
|
||||||
|
// circuit_digest, hashing as in the original plonky2's circuit_digest but
|
||||||
|
// additionally checking it in-circuit. But since in terms of circuit costs would
|
||||||
|
// require a hash (with similar amount of elements), the approach that we do is take
|
||||||
|
// the already computed circuit_digest and hash it together with the
|
||||||
|
// constant_sigmas_cap, doing the same computation in-circuit, obtaining a new hash
|
||||||
|
// that we use to represent the verifier_data.
|
||||||
|
verifier_data_hash: hash_verifier_data_gadget(builder, &verifier_datas_targ[i]),
|
||||||
})
|
})
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
|
|
@ -478,6 +493,38 @@ pub fn pad_circuit(builder: &mut CircuitBuilder<F, D>, common_data: &CommonCircu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hash_verifier_data_gadget(
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
verifier_data: &VerifierCircuitTarget,
|
||||||
|
) -> HashOutTarget {
|
||||||
|
let f: Vec<Target> = [
|
||||||
|
verifier_data.circuit_digest.elements.to_vec(),
|
||||||
|
verifier_data
|
||||||
|
.constants_sigmas_cap
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.flat_map(|e| e.elements)
|
||||||
|
.collect(),
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
builder.hash_n_to_hash_no_pad::<PoseidonHash>(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// compatible with hash_verifier_data_gadget.
|
||||||
|
pub(crate) fn hash_verifier_data(verifier_only_data: &VerifierOnlyCircuitData<C, D>) -> HashOut<F> {
|
||||||
|
let f: Vec<F> = [
|
||||||
|
verifier_only_data.circuit_digest.elements.to_vec(),
|
||||||
|
verifier_only_data
|
||||||
|
.constants_sigmas_cap
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.flat_map(|e| e.elements)
|
||||||
|
.collect(),
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
PoseidonHash::hash_no_pad(&f)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue