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::convert::TryFrom;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
|
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||||
|
use nonempty::NonEmpty;
|
||||||
use orchard::{
|
use orchard::{
|
||||||
bundle::{Action, Authorization, Authorized, Flags},
|
bundle::{Action, Authorization, Authorized, Flags},
|
||||||
note::{ExtractedNoteCommitment, Nullifier, TransmittedNoteCiphertext},
|
note::{ExtractedNoteCommitment, Nullifier, TransmittedNoteCiphertext},
|
||||||
|
@ -10,10 +12,50 @@ use orchard::{
|
||||||
Anchor,
|
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_SPENDS_ENABLED: u8 = 0b0000_0001;
|
||||||
pub const FLAG_OUTPUTS_ENABLED: u8 = 0b0000_0010;
|
pub const FLAG_OUTPUTS_ENABLED: u8 = 0b0000_0010;
|
||||||
pub const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED);
|
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> {
|
pub fn read_value_commitment<R: Read>(mut reader: R) -> io::Result<ValueCommitment> {
|
||||||
let mut bytes = [0u8; 32];
|
let mut bytes = [0u8; 32];
|
||||||
reader.read_exact(&mut bytes)?;
|
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))
|
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<()> {
|
pub fn write_value_commitment<W: Write>(mut writer: W, cv: &ValueCommitment) -> io::Result<()> {
|
||||||
writer.write_all(&cv.to_bytes())
|
writer.write_all(&cv.to_bytes())
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,15 +13,12 @@ mod tests;
|
||||||
use blake2b_simd::Hash as Blake2bHash;
|
use blake2b_simd::Hash as Blake2bHash;
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
use nonempty::NonEmpty;
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use orchard::{self, primitives::redpallas};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consensus::{BlockHeight, BranchId},
|
consensus::{BlockHeight, BranchId},
|
||||||
sapling::redjubjub,
|
sapling::redjubjub,
|
||||||
|
@ -637,7 +634,7 @@ impl Transaction {
|
||||||
Self::read_v5_header_fragment(&mut reader)?;
|
Self::read_v5_header_fragment(&mut reader)?;
|
||||||
let transparent_bundle = Self::read_transparent(&mut reader)?;
|
let transparent_bundle = Self::read_transparent(&mut reader)?;
|
||||||
let sapling_bundle = Self::read_v5_sapling(&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")]
|
#[cfg(feature = "zfuture")]
|
||||||
let tze_bundle = if version.has_tze() {
|
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")]
|
#[cfg(feature = "zfuture")]
|
||||||
fn read_tze<R: Read>(mut reader: &mut R) -> io::Result<Option<tze::Bundle<tze::Authorized>>> {
|
fn read_tze<R: Read>(mut reader: &mut R) -> io::Result<Option<tze::Bundle<tze::Authorized>>> {
|
||||||
let vin = Vector::read(&mut reader, TzeIn::read)?;
|
let vin = Vector::read(&mut reader, TzeIn::read)?;
|
||||||
|
@ -891,7 +844,7 @@ impl Transaction {
|
||||||
self.write_v5_header(&mut writer)?;
|
self.write_v5_header(&mut writer)?;
|
||||||
self.write_transparent(&mut writer)?;
|
self.write_transparent(&mut writer)?;
|
||||||
self.write_v5_sapling(&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")]
|
#[cfg(feature = "zfuture")]
|
||||||
self.write_tze(&mut writer)?;
|
self.write_tze(&mut writer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -950,35 +903,6 @@ impl Transaction {
|
||||||
Ok(())
|
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")]
|
#[cfg(feature = "zfuture")]
|
||||||
pub fn write_tze<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
pub fn write_tze<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
if let Some(bundle) = &self.tze_bundle {
|
if let Some(bundle) = &self.tze_bundle {
|
||||||
|
|
Loading…
Reference in New Issue