From 35a76f03b8bb6e48bf9b0a23266aee2db4e41068 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 21 Jun 2022 02:39:34 +0000 Subject: [PATCH 1/3] Add `orchard::bundle::BatchValidator` Adapted from the `BatchValidator` in `zcashd`, that only handles RedPallas signatures. --- CHANGELOG.md | 3 ++ Cargo.toml | 3 ++ src/bundle.rs | 3 ++ src/bundle/batch.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 src/bundle/batch.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index e7dc93fd..b527d10c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- `orchard::bundle::BatchValidator` + ### Changed - Migrated to `halo2_proofs 0.2`. diff --git a/Cargo.toml b/Cargo.toml index 6dc303ba..7f1cd8f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,9 @@ subtle = "2.3" zcash_note_encryption = "0.1" incrementalmerkletree = "0.3" +# Logging +tracing = "0.1" + # Developer tooling dependencies plotters = { version = "0.3.0", optional = true } diff --git a/src/bundle.rs b/src/bundle.rs index 5421dadc..c70ca635 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -1,7 +1,10 @@ //! Structs related to bundles of Orchard actions. +mod batch; pub mod commitments; +pub use batch::BatchValidator; + use core::fmt; use blake2b_simd::Hash as Blake2bHash; diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs new file mode 100644 index 00000000..5bb462ac --- /dev/null +++ b/src/bundle/batch.rs @@ -0,0 +1,74 @@ +use rand::{CryptoRng, RngCore}; +use tracing::debug; + +use super::{Authorized, Bundle}; +use crate::primitives::redpallas::{self, Binding, SpendAuth}; + +/// A signature within an authorized Orchard bundle. +#[derive(Debug)] +struct BundleSignature { + /// The signature item for validation. + signature: redpallas::batch::Item, +} + +/// Batch validation context for Orchard. +/// +/// This batch-validates RedPallas signatures. +#[derive(Debug, Default)] +pub struct BatchValidator { + signatures: Vec, +} + +impl BatchValidator { + /// Constructs a new batch validation context. + pub fn new() -> Self { + BatchValidator { signatures: vec![] } + } + + /// Adds the RedPallas signatures from the given bundle to the validator. + pub fn add_bundle>( + &mut self, + bundle: &Bundle, + sighash: [u8; 32], + ) { + for action in bundle.actions().iter() { + self.signatures.push(BundleSignature { + signature: action + .rk() + .create_batch_item(action.authorization().clone(), &sighash), + }); + } + + self.signatures.push(BundleSignature { + signature: bundle + .binding_validating_key() + .create_batch_item(bundle.authorization().binding_signature().clone(), &sighash), + }); + } + + /// Batch-validates the accumulated bundles. + /// + /// Returns `true` if every signature in every bundle added to the batch validator is + /// valid, or `false` if one or more are invalid. No attempt is made to figure out + /// which of the accumulated bundles might be invalid; if that information is desired, + /// construct separate [`BatchValidator`]s for sub-batches of the bundles. + pub fn validate(&self, rng: R) -> bool { + if self.signatures.is_empty() { + // An empty batch is always valid, but is not free to run; skip it. + return true; + } + + let mut validator = redpallas::batch::Verifier::new(); + for sig in self.signatures.iter() { + validator.queue(sig.signature.clone()); + } + + match validator.verify(rng) { + Ok(()) => true, + Err(e) => { + debug!("RedPallas batch validation failed: {}", e); + false + } + } + } +} From 81626b3b288ddba3a65a3006ab4e5b678f55e085 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 21 Jun 2022 15:07:31 +0000 Subject: [PATCH 2/3] Add batch-verification of proofs to `orchard::bundle::BatchValidator` --- src/bundle/batch.rs | 35 +++++++++++++++++++++++++---------- src/circuit.rs | 26 ++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs index 5bb462ac..4fee9590 100644 --- a/src/bundle/batch.rs +++ b/src/bundle/batch.rs @@ -1,8 +1,13 @@ +use halo2_proofs::plonk; +use pasta_curves::vesta; use rand::{CryptoRng, RngCore}; use tracing::debug; use super::{Authorized, Bundle}; -use crate::primitives::redpallas::{self, Binding, SpendAuth}; +use crate::{ + circuit::VerifyingKey, + primitives::redpallas::{self, Binding, SpendAuth}, +}; /// A signature within an authorized Orchard bundle. #[derive(Debug)] @@ -13,19 +18,23 @@ struct BundleSignature { /// Batch validation context for Orchard. /// -/// This batch-validates RedPallas signatures. +/// This batch-validates proofs and RedPallas signatures. #[derive(Debug, Default)] pub struct BatchValidator { + proofs: plonk::BatchVerifier, signatures: Vec, } impl BatchValidator { /// Constructs a new batch validation context. pub fn new() -> Self { - BatchValidator { signatures: vec![] } + BatchValidator { + proofs: plonk::BatchVerifier::new(), + signatures: vec![], + } } - /// Adds the RedPallas signatures from the given bundle to the validator. + /// Adds the proof and RedPallas signatures from the given bundle to the validator. pub fn add_bundle>( &mut self, bundle: &Bundle, @@ -44,15 +53,20 @@ impl BatchValidator { .binding_validating_key() .create_batch_item(bundle.authorization().binding_signature().clone(), &sighash), }); + + bundle + .authorization() + .proof() + .add_to_batch(&mut self.proofs, bundle.to_instances()); } /// Batch-validates the accumulated bundles. /// - /// Returns `true` if every signature in every bundle added to the batch validator is - /// valid, or `false` if one or more are invalid. No attempt is made to figure out - /// which of the accumulated bundles might be invalid; if that information is desired, - /// construct separate [`BatchValidator`]s for sub-batches of the bundles. - pub fn validate(&self, rng: R) -> bool { + /// Returns `true` if every proof and signature in every bundle added to the batch + /// validator is valid, or `false` if one or more are invalid. No attempt is made to + /// figure out which of the accumulated bundles might be invalid; if that information + /// is desired, construct separate [`BatchValidator`]s for sub-batches of the bundles. + pub fn validate(self, vk: &VerifyingKey, rng: R) -> bool { if self.signatures.is_empty() { // An empty batch is always valid, but is not free to run; skip it. return true; @@ -64,7 +78,8 @@ impl BatchValidator { } match validator.verify(rng) { - Ok(()) => true, + // If signatures are valid, check the proofs. + Ok(()) => self.proofs.finalize(&vk.params, &vk.vk), Err(e) => { debug!("RedPallas batch validation failed: {}", e); false diff --git a/src/circuit.rs b/src/circuit.rs index 7bcc31d6..f40a34a0 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -6,8 +6,8 @@ use group::{Curve, GroupEncoding}; use halo2_proofs::{ circuit::{floor_planner, Layouter, Value}, plonk::{ - self, Advice, Column, Constraints, Expression, Instance as InstanceColumn, Selector, - SingleVerifier, + self, Advice, BatchVerifier, Column, Constraints, Expression, Instance as InstanceColumn, + Selector, SingleVerifier, }, poly::Rotation, transcript::{Blake2bRead, Blake2bWrite}, @@ -690,8 +690,8 @@ impl plonk::Circuit for Circuit { /// The verifying key for the Orchard Action circuit. #[derive(Debug)] pub struct VerifyingKey { - params: halo2_proofs::poly::commitment::Params, - vk: plonk::VerifyingKey, + pub(crate) params: halo2_proofs::poly::commitment::Params, + pub(crate) vk: plonk::VerifyingKey, } impl VerifyingKey { @@ -866,6 +866,24 @@ impl Proof { plonk::verify_proof(&vk.params, &vk.vk, strategy, &instances, &mut transcript) } + pub(crate) fn add_to_batch( + &self, + batch: &mut BatchVerifier, + instances: Vec, + ) { + let instances = instances + .iter() + .map(|i| { + i.to_halo2_instance() + .into_iter() + .map(|c| c.into_iter().collect()) + .collect() + }) + .collect(); + + batch.add_proof(instances, self.0.clone()); + } + /// Constructs a new Proof value. pub fn new(bytes: Vec) -> Self { Proof(bytes) From 4bed67218aaeb0e28bc4e40910f27cc64fc68f42 Mon Sep 17 00:00:00 2001 From: str4d Date: Thu, 23 Jun 2022 22:33:40 +0100 Subject: [PATCH 3/3] Add note about relationship between signatures and proofs Co-authored-by: Daira Hopwood --- src/bundle/batch.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs index 4fee9590..c60d0cd5 100644 --- a/src/bundle/batch.rs +++ b/src/bundle/batch.rs @@ -69,6 +69,8 @@ impl BatchValidator { pub fn validate(self, vk: &VerifyingKey, rng: R) -> bool { if self.signatures.is_empty() { // An empty batch is always valid, but is not free to run; skip it. + // Note that a transaction has at least a binding signature, so if + // there are no signatures, there are also no proofs. return true; }