use group::Curve; use halo2::{ plonk, poly::{EvaluationDomain, LagrangeCoeff, Polynomial, Rotation}, transcript::{Blake2bRead, Blake2bWrite}, }; use pasta_curves::{pallas, vesta}; use crate::{ note::{nullifier::Nullifier, ExtractedNoteCommitment}, primitives::redpallas::{SpendAuth, VerificationKey}, tree::Anchor, value::ValueCommitment, }; pub(crate) mod gadget; /// Size of the Orchard circuit. const K: u32 = 11; /// The Orchard Action circuit. #[derive(Debug)] pub struct Circuit {} impl plonk::Circuit for Circuit { type Config = (); fn configure(meta: &mut plonk::ConstraintSystem) -> Self::Config { // Placeholder so the proving key is correctly built. meta.instance_column(); // Placeholder gate so there is something for the prover to operate on. let advice = meta.advice_column(); meta.create_gate("TODO", |meta| { vec![meta.query_advice(advice, Rotation::cur())] }); } fn synthesize( &self, _cs: &mut impl plonk::Assignment, _config: Self::Config, ) -> Result<(), plonk::Error> { Ok(()) } } /// The verifying key for the Orchard Action circuit. #[derive(Debug)] pub struct VerifyingKey { params: halo2::poly::commitment::Params, vk: plonk::VerifyingKey, } impl VerifyingKey { /// Builds the verifying key. pub fn build() -> Self { let params = halo2::poly::commitment::Params::new(K); let circuit = Circuit {}; // TODO let vk = plonk::keygen_vk(¶ms, &circuit).unwrap(); VerifyingKey { params, vk } } } /// The proving key for the Orchard Action circuit. #[derive(Debug)] pub struct ProvingKey { params: halo2::poly::commitment::Params, pk: plonk::ProvingKey, } impl ProvingKey { /// Builds the proving key. pub fn build() -> Self { let params = halo2::poly::commitment::Params::new(K); let circuit = Circuit {}; // TODO let vk = plonk::keygen_vk(¶ms, &circuit).unwrap(); let pk = plonk::keygen_pk(¶ms, vk, &circuit).unwrap(); ProvingKey { params, pk } } } /// Public inputs to the Orchard Action circuit. #[derive(Debug)] pub struct Instance { pub(crate) anchor: Anchor, pub(crate) cv_net: ValueCommitment, pub(crate) nf_old: Nullifier, pub(crate) rk: VerificationKey, pub(crate) cmx: ExtractedNoteCommitment, pub(crate) enable_spend: bool, pub(crate) enable_output: bool, } impl Instance { fn to_halo2_instance( &self, domain: &EvaluationDomain, ) -> [Polynomial; 1] { // TODO [domain.empty_lagrange()] } fn to_halo2_instance_commitments(&self, vk: &VerifyingKey) -> [vesta::Affine; 1] { [vk.params .commit_lagrange( &self.to_halo2_instance(vk.vk.get_domain())[0], Default::default(), ) .to_affine()] } } /// A proof of the validity of an Orchard [`Bundle`]. /// /// [`Bundle`]: crate::bundle::Bundle #[derive(Debug, Clone)] pub struct Proof(Vec); impl AsRef<[u8]> for Proof { fn as_ref(&self) -> &[u8] { &self.0 } } impl Proof { /// Creates a proof for the given circuits and instances. pub fn create( pk: &ProvingKey, circuits: &[Circuit], instances: &[Instance], ) -> Result { let instances: Vec<_> = instances .iter() .map(|i| i.to_halo2_instance(pk.pk.get_vk().get_domain())) .collect(); let instances: Vec<_> = instances.iter().map(|i| &i[..]).collect(); let mut transcript = Blake2bWrite::<_, vesta::Affine, _>::init(vec![]); plonk::create_proof(&pk.params, &pk.pk, circuits, &instances, &mut transcript)?; Ok(Proof(transcript.finalize())) } /// Verifies this proof with the given instances. pub fn verify(&self, vk: &VerifyingKey, instances: &[Instance]) -> Result<(), plonk::Error> { let instances: Vec<_> = instances .iter() .map(|i| i.to_halo2_instance_commitments(vk)) .collect(); let instances: Vec<_> = instances.iter().map(|i| &i[..]).collect(); let msm = vk.params.empty_msm(); let mut transcript = Blake2bRead::init(&self.0[..]); let guard = plonk::verify_proof(&vk.params, &vk.vk, msm, &instances, &mut transcript)?; let msm = guard.clone().use_challenges(); if msm.eval() { Ok(()) } else { Err(plonk::Error::ConstraintSystemFailure) } } /// Constructs a new Proof value. pub fn new(bytes: Vec) -> Self { Proof(bytes) } } #[cfg(test)] mod tests { use ff::Field; use halo2::dev::MockProver; use pasta_curves::pallas; use rand::rngs::OsRng; use std::iter; use super::{Circuit, Instance, Proof, ProvingKey, VerifyingKey, K}; use crate::{ keys::SpendValidatingKey, note::Note, value::{ValueCommitTrapdoor, ValueCommitment}, }; // TODO: recast as a proptest #[test] fn round_trip() { let mut rng = OsRng; let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(()) .map(|()| { let (_, fvk, spent_note) = Note::dummy(&mut rng, None); let nf_old = spent_note.nullifier(&fvk); let ak: SpendValidatingKey = fvk.into(); let alpha = pallas::Scalar::random(&mut rng); let rk = ak.randomize(&alpha); let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old)); let cmx = output_note.commitment().into(); let value = spent_note.value() - output_note.value(); let cv_net = ValueCommitment::derive(value.unwrap(), ValueCommitTrapdoor::zero()); ( Circuit {}, Instance { anchor: pallas::Base::zero().into(), cv_net, nf_old, rk, cmx, enable_spend: true, enable_output: true, }, ) }) .unzip(); let vk = VerifyingKey::build(); for (circuit, instance) in circuits.iter().zip(instances.iter()) { assert_eq!( MockProver::run( K, circuit, instance .to_halo2_instance(vk.vk.get_domain()) .iter() .map(|p| p.iter().cloned().collect()) .collect() ) .unwrap() .verify(), Ok(()) ); } let pk = ProvingKey::build(); let proof = Proof::create(&pk, &circuits, &instances).unwrap(); assert!(proof.verify(&vk, &instances).is_ok()); } }