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:
parent
0bfd1f7544
commit
7e23b96a03
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue