- middleware:
- Add `Statement::Intro`
- Add `SignedBy` native predicate and operation. The signature is auxiliary data to the operation
- Rename `PodSigner` to `Signer` with a new API (just for signing `RawValue`)
- Removed `NewEntry` operation. Use `ContainsFromEntries` instead
- Remove `KEY_SIGNER` and `KEY_TYPE` which are no longer used
- Merge `RecursivePod` and `Pod` traits
- Change the `Pod::deserialize_data` method to use `Self` instead of `Box<dyn Pod>`
- Extend `Pod` trait with these methods:
- `is_main`: when the pod is Main, in a (recursive) verification its vk will be checked to exist in the vd_set but not if it's intro pod
- `is_mock`: skip some verifications in the recursive mock MainPod verification
- `verifier_data_hash`
- `pod_id` renamed to `statements_hash`
- AnchoredKeys are now a pair of dictionary root and key
- Entry statements are now defined as Contains with literal arguments
- Operations that take Entries now use Contains statements with literal arguments
- frontend:
- Rename `SignedPod` to `SignedDict` (which now contains the dict, public key and signature, and can still `verify(self)`ed)
- The `SignedDict` keeps the method `get_statement` for convenience but now it returns a `Contains` statement that proves the existence of the key in the dict
- The `MainPodBuilder` automatically inserts a `Contains` statement when an operation is added that uses an entry as argument that was not yet "opened".
- Removed the `literal` methods from the `MainPodBuilder` that were loading literals to anchored keys: that was no longer needed after we introduced literal arguments
- backend
- Only verify inclusion of the verifying key into the vd_set if the pod is MainPod. A pod is not MainPod if the first statement is Intro.
- Reject intro pods that have non-intro statements
- Empty pod now returns an intro statement
- Don't insert a type statement automatically in MainPod and MockMainPod. We get rid of the type entry.
- Implement `SignedBy` operation, which uses the muxed table to store signature verifications
- Rename `PodId` to `statements_hash` or `sts_hash` for short. Now this is only used as a hash of the statements for the circuits public inputs.
- Refactor normalization of `self` statements:
- Before: replace values that contain `SELF` by the given pod_id
- After: place the verifying key hash into the Intro predicates
185 lines
5.3 KiB
Rust
185 lines
5.3 KiB
Rust
pub mod basetypes;
|
|
pub mod circuits;
|
|
pub mod emptypod;
|
|
mod error;
|
|
pub mod mainpod;
|
|
pub mod mock;
|
|
pub mod primitives;
|
|
pub mod recursion;
|
|
pub mod serialization;
|
|
pub mod signer;
|
|
|
|
use std::iter;
|
|
|
|
use base64::{prelude::BASE64_STANDARD, Engine};
|
|
pub use error::*;
|
|
use plonky2::{
|
|
field::{
|
|
extension::quadratic::QuadraticExtension,
|
|
types::{Field, Field64},
|
|
},
|
|
hash::hash_types::HashOut,
|
|
plonk::vars::EvaluationVars,
|
|
util::serialization::{Buffer, Read},
|
|
};
|
|
use rand::prelude::*;
|
|
use rand_chacha::ChaCha20Rng;
|
|
use serde::{ser, Deserialize, Serialize};
|
|
use sha2::{Digest, Sha256};
|
|
|
|
use crate::{
|
|
backends::plonky2::{
|
|
basetypes::{CommonCircuitData, Proof, VerifierOnlyCircuitData, F},
|
|
circuits::mainpod::{MainPodVerifyTarget, NUM_PUBLIC_INPUTS},
|
|
recursion::RecursiveCircuit,
|
|
serialization::{CommonCircuitDataSerializer, Pod2GateSerializer},
|
|
},
|
|
cache::{self, CacheEntry},
|
|
middleware::Params,
|
|
timed,
|
|
};
|
|
|
|
pub fn cache_get_standard_rec_main_pod_common_circuit_data(
|
|
) -> CacheEntry<CommonCircuitDataSerializer> {
|
|
let params = Params::default();
|
|
cache::get(
|
|
"standard_rec_main_pod_common_circuit_data",
|
|
¶ms,
|
|
|params| {
|
|
let circuit_data = timed!(
|
|
"recursive MainPod circuit_data",
|
|
RecursiveCircuit::<MainPodVerifyTarget>::target_and_circuit_data(
|
|
params.max_input_pods,
|
|
NUM_PUBLIC_INPUTS,
|
|
params
|
|
)
|
|
.expect("calculate circuit_data")
|
|
);
|
|
CommonCircuitDataSerializer(circuit_data.1.common)
|
|
},
|
|
)
|
|
.expect("cache ok")
|
|
}
|
|
|
|
pub fn serialize_bytes(bytes: &[u8]) -> String {
|
|
BASE64_STANDARD.encode(bytes)
|
|
}
|
|
|
|
pub fn deserialize_bytes(data: &str) -> Result<Vec<u8>> {
|
|
BASE64_STANDARD.decode(data).map_err(|e| {
|
|
Error::custom(format!(
|
|
"Failed to decode data from base64: {}. Value: {}",
|
|
e, data
|
|
))
|
|
})
|
|
}
|
|
|
|
pub fn deserialize_proof(common: &CommonCircuitData, proof: &str) -> Result<Proof> {
|
|
let decoded = deserialize_bytes(proof)?;
|
|
let mut buf = Buffer::new(&decoded);
|
|
let proof = buf.read_proof(common).map_err(|e| {
|
|
Error::custom(format!(
|
|
"Failed to read proof from buffer: {}. Value: {}",
|
|
e, proof
|
|
))
|
|
})?;
|
|
|
|
Ok(proof)
|
|
}
|
|
|
|
pub fn serialize_verifier_only(verifier_only: &VerifierOnlyCircuitData) -> String {
|
|
let bytes = verifier_only.to_bytes().expect("write to Vec");
|
|
serialize_bytes(&bytes)
|
|
}
|
|
|
|
pub fn deserialize_verifier_only(verifier_only: &str) -> Result<VerifierOnlyCircuitData> {
|
|
let decoded = deserialize_bytes(verifier_only)?;
|
|
let verifier_only = VerifierOnlyCircuitData::from_bytes(&decoded).map_err(|e| {
|
|
Error::custom(format!(
|
|
"Failed to read VerifierOnlyCircuitData from buffer: {}. Value: {}",
|
|
e, verifier_only
|
|
))
|
|
})?;
|
|
|
|
Ok(verifier_only)
|
|
}
|
|
|
|
pub fn serialize_proof(proof: &Proof) -> String {
|
|
let mut buffer = Vec::new();
|
|
use plonky2::util::serialization::Write;
|
|
buffer.write_proof(proof).unwrap();
|
|
serialize_bytes(&buffer)
|
|
}
|
|
|
|
fn rand_vec(rng: &mut impl RngCore, len: usize) -> Vec<F> {
|
|
iter::repeat_with(|| rng.next_u64())
|
|
.filter(|v| *v < F::ORDER)
|
|
.map(F::from_canonical_u64)
|
|
.take(len)
|
|
.collect()
|
|
}
|
|
|
|
fn base(r: F, xs: &[F]) -> F {
|
|
let mut res = F::ZERO;
|
|
for x in xs.iter().rev() {
|
|
res *= r;
|
|
res += *x;
|
|
}
|
|
res
|
|
}
|
|
|
|
fn gate_fingerprints(common: &CommonCircuitData) -> Vec<(String, F)> {
|
|
type Ext = QuadraticExtension<F>;
|
|
let config = &common.config;
|
|
let mut rng = ChaCha20Rng::seed_from_u64(42);
|
|
let r = rand_vec(&mut rng, 1)[0];
|
|
let local_constants: Vec<Ext> = rand_vec(&mut rng, config.num_constants)
|
|
.into_iter()
|
|
.map(Ext::from)
|
|
.collect();
|
|
let local_wires: Vec<Ext> = rand_vec(&mut rng, config.num_wires)
|
|
.into_iter()
|
|
.map(Ext::from)
|
|
.collect();
|
|
let public_inputs_hash = HashOut::from_vec(rand_vec(&mut rng, 4));
|
|
let vars = EvaluationVars {
|
|
local_constants: &local_constants,
|
|
local_wires: &local_wires,
|
|
public_inputs_hash: &public_inputs_hash,
|
|
};
|
|
let mut fingerprints = Vec::new();
|
|
for gate in &common.gates {
|
|
let eval: Vec<F> = gate
|
|
.0
|
|
.eval_unfiltered(vars)
|
|
.into_iter()
|
|
.map(|e| e.0[0])
|
|
.collect();
|
|
fingerprints.push((gate.0.id(), base(r, &eval)));
|
|
}
|
|
fingerprints
|
|
}
|
|
|
|
pub fn hash_common_data(common: &CommonCircuitData) -> serde_json::Result<String> {
|
|
#[derive(Serialize, Deserialize)]
|
|
pub struct CommonFingerprintData {
|
|
common: String,
|
|
gate_fingerprints: Vec<(String, F)>,
|
|
}
|
|
|
|
let gate_serializer = Pod2GateSerializer {};
|
|
let bytes = common
|
|
.to_bytes(&gate_serializer)
|
|
.map_err(ser::Error::custom)?;
|
|
let gate_fingerprints = gate_fingerprints(common);
|
|
let data = CommonFingerprintData {
|
|
common: serialize_bytes(&bytes),
|
|
gate_fingerprints,
|
|
};
|
|
|
|
let json = serde_json::to_string(&data)?;
|
|
let json_hash = Sha256::digest(&json);
|
|
let json_hash_str_long = format!("{:x}", json_hash);
|
|
let json_hash_str = json_hash_str_long[..32].to_string();
|
|
Ok(json_hash_str)
|
|
}
|