From fe51ec9f25de703ee464b8066799da5706df618c Mon Sep 17 00:00:00 2001 From: Dmitry Demin Date: Wed, 11 Oct 2023 16:30:01 +0200 Subject: [PATCH] Porting Batch Validator changes on top of the latest version of zsa1 branch (after PR #81 was merged) --- src/bundle/batch.rs | 18 +++++++++++++-- src/issuance.rs | 53 +++++++++++++++++++++++++++++++++++++++++++ src/issuance/batch.rs | 36 +++++++++++++++++++++++++++++ src/keys.rs | 13 +++++++++++ src/note.rs | 13 +++++++++++ 5 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 src/issuance/batch.rs diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs index c60d0cd55..5c4a12a42 100644 --- a/src/bundle/batch.rs +++ b/src/bundle/batch.rs @@ -3,9 +3,10 @@ use pasta_curves::vesta; use rand::{CryptoRng, RngCore}; use tracing::debug; -use super::{Authorized, Bundle}; +use super::{burn_validation::validate_bundle_burn, Authorized, Bundle}; use crate::{ circuit::VerifyingKey, + note::AssetBase, primitives::redpallas::{self, Binding, SpendAuth}, }; @@ -23,6 +24,7 @@ struct BundleSignature { pub struct BatchValidator { proofs: plonk::BatchVerifier, signatures: Vec, + burns: Vec<(AssetBase, i64)>, } impl BatchValidator { @@ -31,10 +33,11 @@ impl BatchValidator { BatchValidator { proofs: plonk::BatchVerifier::new(), signatures: vec![], + burns: vec![], } } - /// Adds the proof and RedPallas signatures from the given bundle to the validator. + /// Adds the proof, RedPallas signatures and burn mechanism values from the given bundle to the validator. pub fn add_bundle>( &mut self, bundle: &Bundle, @@ -58,6 +61,13 @@ impl BatchValidator { .authorization() .proof() .add_to_batch(&mut self.proofs, bundle.to_instances()); + + self.burns.extend( + bundle + .burn + .iter() + .map(|(asset, amount)| (*asset, (*amount).into())), + ); } /// Batch-validates the accumulated bundles. @@ -67,6 +77,10 @@ impl BatchValidator { /// 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 validate_bundle_burn(&self.burns).is_err() { + return false; + } + 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 diff --git a/src/issuance.rs b/src/issuance.rs index 29a95fda3..f599e15cb 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -1,6 +1,7 @@ //! Structs related to issuance bundles and the associated logic. use blake2b_simd::Hash as Blake2bHash; use group::Group; +use memuse::DynamicUsage; use nonempty::NonEmpty; use rand::{CryptoRng, RngCore}; use std::collections::HashSet; @@ -25,6 +26,10 @@ use crate::{ use crate::supply_info::{AssetSupply, SupplyInfo}; +mod batch; + +pub use batch::BatchValidator; + /// A bundle of actions to be applied to the ledger. #[derive(Debug, Clone)] pub struct IssueBundle { @@ -49,6 +54,24 @@ pub struct IssueAction { finalize: bool, } +impl DynamicUsage for IssueAction { + #[inline(always)] + fn dynamic_usage(&self) -> usize { + self.asset_desc.dynamic_usage() + self.notes.dynamic_usage() + } + + #[inline(always)] + fn dynamic_usage_bounds(&self) -> (usize, Option) { + let asset_desc_bounds = self.asset_desc.dynamic_usage_bounds(); + let note_bounds = self.notes.dynamic_usage_bounds(); + + ( + asset_desc_bounds.0 + note_bounds.0, + asset_desc_bounds.1.zip(note_bounds.1).map(|(a, b)| a + b), + ) + } +} + /// The parameters required to add a Note into an IssueAction. #[derive(Debug)] pub struct IssueInfo { @@ -202,6 +225,15 @@ impl IssueAuth for Unauthorized {} impl IssueAuth for Prepared {} impl IssueAuth for Signed {} +impl DynamicUsage for Signed { + fn dynamic_usage(&self) -> usize { + 0 + } + fn dynamic_usage_bounds(&self) -> (usize, Option) { + (0, Some(0)) + } +} + impl IssueBundle { /// Returns the issuer verification key for the bundle. pub fn ik(&self) -> &IssuanceValidatingKey { @@ -460,6 +492,27 @@ impl IssueBundle { } } +impl DynamicUsage for IssueBundle { + fn dynamic_usage(&self) -> usize { + self.actions.dynamic_usage() + self.ik.dynamic_usage() + self.authorization.dynamic_usage() + } + + fn dynamic_usage_bounds(&self) -> (usize, Option) { + let action_bounds = self.actions.dynamic_usage_bounds(); + let ik_bounds = self.ik.dynamic_usage_bounds(); + let authorization_bounds = self.authorization.dynamic_usage_bounds(); + + ( + action_bounds.0 + ik_bounds.0 + authorization_bounds.0, + action_bounds + .1 + .zip(ik_bounds.1) + .zip(authorization_bounds.1) + .map(|((a, b), c)| a + b + c), + ) + } +} + /// Validation for Orchard IssueBundles /// /// A set of previously finalized asset types must be provided in `finalized` argument. diff --git a/src/issuance/batch.rs b/src/issuance/batch.rs new file mode 100644 index 000000000..41794f694 --- /dev/null +++ b/src/issuance/batch.rs @@ -0,0 +1,36 @@ +use std::collections::HashSet; + +use super::{verify_issue_bundle, AssetBase, IssueBundle, Signed}; + +/// Batch validation context for Issuance. +/// +#[derive(Debug, Default)] +pub struct BatchValidator { + bundles: Vec<(IssueBundle, [u8; 32])>, +} + +impl BatchValidator { + /// Constructs a new batch validation context. + pub fn new() -> Self { + BatchValidator { bundles: vec![] } + } + + /// Adds bundle to the validator. + pub fn add_bundle(&mut self, bundle: &IssueBundle, sighash: [u8; 32]) { + self.bundles.push((bundle.clone(), sighash)) + } + + /// Batch-validates the accumulated bundles. + /// + /// Returns `true` if every bundle added to the batch validator is valid, or `false` + /// if one or more are invalid. + pub fn validate(self) -> bool { + // FIXME: take/save finalization set from/to the global state + let finalized = HashSet::::new(); + + // FIXME: process resulting supply_info + self.bundles + .into_iter() + .all(|(bundle, sighash)| verify_issue_bundle(&bundle, sighash, &finalized).is_ok()) + } +} diff --git a/src/keys.rs b/src/keys.rs index 7853d4920..7238b0684 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -11,6 +11,7 @@ use group::{ prime::PrimeCurveAffine, Curve, GroupEncoding, }; +use memuse::DynamicUsage; use pasta_curves::{pallas, pallas::Scalar}; use rand::{CryptoRng, RngCore}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -349,6 +350,18 @@ impl PartialEq for IssuanceValidatingKey { impl Eq for IssuanceValidatingKey {} +impl DynamicUsage for IssuanceValidatingKey { + #[inline(always)] + fn dynamic_usage(&self) -> usize { + 0 + } + + #[inline(always)] + fn dynamic_usage_bounds(&self) -> (usize, Option) { + (0, Some(0)) + } +} + impl IssuanceValidatingKey { /// Converts this spend validating key to its serialized form, /// I2LEOSP_256(ik). diff --git a/src/note.rs b/src/note.rs index 61a9f9a99..79cfbca8d 100644 --- a/src/note.rs +++ b/src/note.rs @@ -2,6 +2,7 @@ use core::fmt; use group::GroupEncoding; +use memuse::DynamicUsage; use pasta_curves::pallas; use rand::RngCore; use subtle::{Choice, ConditionallySelectable, CtOption}; @@ -306,6 +307,18 @@ impl Note { } } +impl DynamicUsage for Note { + #[inline(always)] + fn dynamic_usage(&self) -> usize { + 0 + } + + #[inline(always)] + fn dynamic_usage_bounds(&self) -> (usize, Option) { + (0, Some(0)) + } +} + /// An encrypted note. #[derive(Clone)] pub struct TransmittedNoteCiphertext {