Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add orchard::bundle::BatchValidator #338

Merged
merged 3 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand Down
3 changes: 3 additions & 0 deletions src/bundle.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
91 changes: 91 additions & 0 deletions src/bundle/batch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use halo2_proofs::plonk;
use pasta_curves::vesta;
use rand::{CryptoRng, RngCore};
use tracing::debug;

use super::{Authorized, Bundle};
use crate::{
circuit::VerifyingKey,
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<SpendAuth, Binding>,
}

/// Batch validation context for Orchard.
///
/// This batch-validates proofs and RedPallas signatures.
#[derive(Debug, Default)]
pub struct BatchValidator {
proofs: plonk::BatchVerifier<vesta::Affine>,
signatures: Vec<BundleSignature>,
}

impl BatchValidator {
/// Constructs a new batch validation context.
pub fn new() -> Self {
BatchValidator {
proofs: plonk::BatchVerifier::new(),
signatures: vec![],
}
}

/// Adds the proof and RedPallas signatures from the given bundle to the validator.
pub fn add_bundle<V: Copy + Into<i64>>(
&mut self,
bundle: &Bundle<Authorized, V>,
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),
});

bundle
.authorization()
.proof()
.add_to_batch(&mut self.proofs, bundle.to_instances());
}

/// Batch-validates the accumulated bundles.
///
/// 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<R: RngCore + CryptoRng>(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.
str4d marked this conversation as resolved.
Show resolved Hide resolved
// Note that a transaction has at least a binding signature, so if
// there are no signatures, there are also no proofs.
Copy link
Contributor

@daira daira Jun 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this should have more precisely said "A transaction has at least an Orchard binding signature if it has any Orchard components, so if there are no Orchard signatures, there are also no Orchard proofs."

return true;
}

let mut validator = redpallas::batch::Verifier::new();
for sig in self.signatures.iter() {
validator.queue(sig.signature.clone());
}

match validator.verify(rng) {
// If signatures are valid, check the proofs.
Ok(()) => self.proofs.finalize(&vk.params, &vk.vk),
Err(e) => {
debug!("RedPallas batch validation failed: {}", e);
false
}
}
}
}
26 changes: 22 additions & 4 deletions src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -690,8 +690,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
/// The verifying key for the Orchard Action circuit.
#[derive(Debug)]
pub struct VerifyingKey {
params: halo2_proofs::poly::commitment::Params<vesta::Affine>,
vk: plonk::VerifyingKey<vesta::Affine>,
pub(crate) params: halo2_proofs::poly::commitment::Params<vesta::Affine>,
pub(crate) vk: plonk::VerifyingKey<vesta::Affine>,
}

impl VerifyingKey {
Expand Down Expand Up @@ -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<vesta::Affine>,
instances: Vec<Instance>,
) {
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<u8>) -> Self {
Proof(bytes)
Expand Down