From 7e23b96a033ff29de40efa6434bfdd2476036067 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 8 Jun 2021 16:53:16 +0100 Subject: [PATCH] Extract ZIP 225 Orchard bundle parsing and serialization into helpers These will be used by `zcashd` for handling the Orchard component of v5 transactions. --- .../src/transaction/components/orchard.rs | 75 +++++++++++++++++ zcash_primitives/src/transaction/mod.rs | 80 +------------------ 2 files changed, 77 insertions(+), 78 deletions(-) diff --git a/zcash_primitives/src/transaction/components/orchard.rs b/zcash_primitives/src/transaction/components/orchard.rs index a6d274f05..37b3f6996 100644 --- a/zcash_primitives/src/transaction/components/orchard.rs +++ b/zcash_primitives/src/transaction/components/orchard.rs @@ -2,6 +2,8 @@ use std::convert::TryFrom; use std::io::{self, Read, Write}; +use byteorder::{ReadBytesExt, WriteBytesExt}; +use nonempty::NonEmpty; use orchard::{ bundle::{Action, Authorization, Authorized, Flags}, note::{ExtractedNoteCommitment, Nullifier, TransmittedNoteCiphertext}, @@ -10,10 +12,50 @@ use orchard::{ Anchor, }; +use super::Amount; +use crate::serialize::{Array, CompactSize}; +use crate::{serialize::Vector, transaction::Transaction}; + pub const FLAG_SPENDS_ENABLED: u8 = 0b0000_0001; pub const FLAG_OUTPUTS_ENABLED: u8 = 0b0000_0010; pub const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED); +/// Reads an [`orchard::Bundle`] from a v5 transaction format. +pub fn read_v5_bundle( + mut reader: R, +) -> io::Result>> { + let actions_without_auth = Vector::read(&mut reader, |r| read_action_without_auth(r))?; + if actions_without_auth.is_empty() { + Ok(None) + } else { + let flags = read_flags(&mut reader)?; + let value_balance = Transaction::read_amount(&mut reader)?; + let anchor = read_anchor(&mut reader)?; + let proof_bytes = Vector::read(&mut reader, |r| r.read_u8())?; + let actions = NonEmpty::from_vec( + actions_without_auth + .into_iter() + .map(|act| act.try_map(|_| read_signature::<_, redpallas::SpendAuth>(&mut reader))) + .collect::, _>>()?, + ) + .expect("A nonzero number of actions was read from the transaction data."); + let binding_signature = read_signature::<_, redpallas::Binding>(&mut reader)?; + + let authorization = orchard::bundle::Authorized::from_parts( + orchard::Proof::new(proof_bytes), + binding_signature, + ); + + Ok(Some(orchard::Bundle::from_parts( + actions, + flags, + value_balance, + anchor, + authorization, + ))) + } +} + pub fn read_value_commitment(mut reader: R) -> io::Result { let mut bytes = [0u8; 32]; reader.read_exact(&mut bytes)?; @@ -125,6 +167,39 @@ pub fn read_signature(mut reader: R) -> io::Result( + bundle: Option<&orchard::Bundle>, + mut writer: W, +) -> io::Result<()> { + if let Some(bundle) = &bundle { + Vector::write_nonempty(&mut writer, bundle.actions(), |w, a| { + write_action_without_auth(w, a) + })?; + + write_flags(&mut writer, &bundle.flags())?; + writer.write_all(&bundle.value_balance().to_i64_le_bytes())?; + write_anchor(&mut writer, bundle.anchor())?; + Vector::write( + &mut writer, + bundle.authorization().proof().as_ref(), + |w, b| w.write_u8(*b), + )?; + Array::write( + &mut writer, + bundle.actions().iter().map(|a| a.authorization()), + |w, auth| w.write_all(&<[u8; 64]>::from(*auth)), + )?; + writer.write_all(&<[u8; 64]>::from( + bundle.authorization().binding_signature(), + ))?; + } else { + CompactSize::write(&mut writer, 0)?; + } + + Ok(()) +} + pub fn write_value_commitment(mut writer: W, cv: &ValueCommitment) -> io::Result<()> { writer.write_all(&cv.to_bytes()) } diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 05bababd3..67cb213e3 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -13,15 +13,12 @@ mod tests; use blake2b_simd::Hash as Blake2bHash; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use ff::PrimeField; -use nonempty::NonEmpty; use std::convert::TryFrom; use std::fmt; use std::fmt::Debug; use std::io::{self, Read, Write}; use std::ops::Deref; -use orchard::{self, primitives::redpallas}; - use crate::{ consensus::{BlockHeight, BranchId}, sapling::redjubjub, @@ -637,7 +634,7 @@ impl Transaction { Self::read_v5_header_fragment(&mut reader)?; let transparent_bundle = Self::read_transparent(&mut reader)?; let sapling_bundle = Self::read_v5_sapling(&mut reader)?; - let orchard_bundle = Self::read_v5_orchard(&mut reader)?; + let orchard_bundle = orchard_serialization::read_v5_bundle(&mut reader)?; #[cfg(feature = "zfuture")] let tze_bundle = if version.has_tze() { @@ -735,50 +732,6 @@ impl Transaction { })) } - fn read_v5_orchard( - mut reader: R, - ) -> io::Result>> { - let actions_without_auth = Vector::read(&mut reader, |r| { - orchard_serialization::read_action_without_auth(r) - })?; - if actions_without_auth.is_empty() { - Ok(None) - } else { - let flags = orchard_serialization::read_flags(&mut reader)?; - let value_balance = Self::read_amount(&mut reader)?; - let anchor = orchard_serialization::read_anchor(&mut reader)?; - let proof_bytes = Vector::read(&mut reader, |r| r.read_u8())?; - let actions = NonEmpty::from_vec( - actions_without_auth - .into_iter() - .map(|act| { - act.try_map(|_| { - orchard_serialization::read_signature::<_, redpallas::SpendAuth>( - &mut reader, - ) - }) - }) - .collect::, _>>()?, - ) - .expect("A nonzero number of actions was read from the transaction data."); - let binding_signature = - orchard_serialization::read_signature::<_, redpallas::Binding>(&mut reader)?; - - let authorization = orchard::bundle::Authorized::from_parts( - orchard::Proof::new(proof_bytes), - binding_signature, - ); - - Ok(Some(orchard::Bundle::from_parts( - actions, - flags, - value_balance, - anchor, - authorization, - ))) - } - } - #[cfg(feature = "zfuture")] fn read_tze(mut reader: &mut R) -> io::Result>> { let vin = Vector::read(&mut reader, TzeIn::read)?; @@ -891,7 +844,7 @@ impl Transaction { self.write_v5_header(&mut writer)?; self.write_transparent(&mut writer)?; self.write_v5_sapling(&mut writer)?; - self.write_v5_orchard(&mut writer)?; + orchard_serialization::write_v5_bundle(self.orchard_bundle.as_ref(), &mut writer)?; #[cfg(feature = "zfuture")] self.write_tze(&mut writer)?; Ok(()) @@ -950,35 +903,6 @@ impl Transaction { Ok(()) } - pub fn write_v5_orchard(&self, mut writer: W) -> io::Result<()> { - if let Some(bundle) = &self.orchard_bundle { - Vector::write_nonempty(&mut writer, bundle.actions(), |w, a| { - orchard_serialization::write_action_without_auth(w, a) - })?; - - orchard_serialization::write_flags(&mut writer, &bundle.flags())?; - writer.write_all(&bundle.value_balance().to_i64_le_bytes())?; - orchard_serialization::write_anchor(&mut writer, bundle.anchor())?; - Vector::write( - &mut writer, - bundle.authorization().proof().as_ref(), - |w, b| w.write_u8(*b), - )?; - Array::write( - &mut writer, - bundle.actions().iter().map(|a| a.authorization()), - |w, auth| w.write_all(&<[u8; 64]>::from(*auth)), - )?; - writer.write_all(&<[u8; 64]>::from( - bundle.authorization().binding_signature(), - ))?; - } else { - CompactSize::write(&mut writer, 0)?; - } - - Ok(()) - } - #[cfg(feature = "zfuture")] pub fn write_tze(&self, mut writer: W) -> io::Result<()> { if let Some(bundle) = &self.tze_bundle {