pod2/src/backends/plonky2/mod.rs
Eduard S. 0e2f7b756e
No Pod IDs (#394)
- 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
2025-08-27 13:19:40 +02:00

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",
&params,
|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)
}