Extract ZIP 225 Orchard bundle parsing and serialization into helpers

These will be used by `zcashd` for handling the Orchard component of v5
transactions.
This commit is contained in:
Jack Grigg 2021-06-08 16:53:16 +01:00
parent 0bfd1f7544
commit 7e23b96a03
2 changed files with 77 additions and 78 deletions

View File

@ -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<R: Read>(
mut reader: R,
) -> io::Result<Option<orchard::Bundle<Authorized, Amount>>> {
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::<Result<Vec<_>, _>>()?,
)
.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<R: Read>(mut reader: R) -> io::Result<ValueCommitment> {
let mut bytes = [0u8; 32];
reader.read_exact(&mut bytes)?;
@ -125,6 +167,39 @@ pub fn read_signature<R: Read, T: SigType>(mut reader: R) -> io::Result<Signatur
Ok(Signature::from(bytes))
}
/// Writes an [`orchard::Bundle`] in the v5 transaction format.
pub fn write_v5_bundle<W: Write>(
bundle: Option<&orchard::Bundle<Authorized, Amount>>,
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<W: Write>(mut writer: W, cv: &ValueCommitment) -> io::Result<()> {
writer.write_all(&cv.to_bytes())
}

View File

@ -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<R: Read>(
mut reader: R,
) -> io::Result<Option<orchard::Bundle<orchard::bundle::Authorized, Amount>>> {
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::<Result<Vec<_>, _>>()?,
)
.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<R: Read>(mut reader: &mut R) -> io::Result<Option<tze::Bundle<tze::Authorized>>> {
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<W: Write>(&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<W: Write>(&self, mut writer: W) -> io::Result<()> {
if let Some(bundle) = &self.tze_bundle {