Skip to content

Commit

Permalink
zcash_primitives: Extract bundle-specific parts of tx serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Nov 23, 2024
1 parent b807ea0 commit 52bb40d
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 143 deletions.
2 changes: 1 addition & 1 deletion zcash_primitives/src/transaction/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ mod tests {
#[cfg(feature = "transparent-inputs")]
use crate::{
legacy::keys::{AccountPrivKey, IncomingViewingKey},
transaction::{builder::DEFAULT_TX_EXPIRY_DELTA, OutPoint, TxOut},
transaction::{builder::DEFAULT_TX_EXPIRY_DELTA, transparent::TxOut, OutPoint},
zip32::AccountId,
};

Expand Down
68 changes: 63 additions & 5 deletions zcash_primitives/src/transaction/components.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Types representing the components within Zcash transactions.

use std::io;
use std::marker::PhantomData;

use zcash_protocol::value::BalanceError;
Expand Down Expand Up @@ -184,17 +185,74 @@ impl<A: tze::Authorization> TzePart for Tze<A> {
}

/// The Transparent part of an authorized transaction.
pub trait AuthorizedTransparentPart: TransparentPart {}
pub trait AuthorizedTransparentPart: TransparentPart {
fn read_bundle<R: io::Read>(reader: R) -> io::Result<Option<Self::Bundle>>;

fn write_bundle<W: io::Write>(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()>;
}

/// The Sprout part of an authorized transaction.
pub trait AuthorizedSproutPart: SproutPart {}
pub trait AuthorizedSproutPart: SproutPart {
fn read_v4_bundle<R: io::Read>(
reader: R,
tx_has_sprout: bool,
use_groth: bool,
) -> io::Result<Option<Self::Bundle>>;

fn write_v4_bundle<W: io::Write>(
bundle: Option<&Self::Bundle>,
writer: W,
tx_has_sprout: bool,
) -> io::Result<()>;
}

/// The Sapling part of an authorized transaction.
pub trait AuthorizedSaplingPart: SaplingPart {}
pub trait AuthorizedSaplingPart: SaplingPart {
type V4Components;

fn read_v4_components<R: io::Read>(
reader: R,
tx_has_sapling: bool,
) -> io::Result<Self::V4Components>;

fn read_v4_binding_sig<R: io::Read>(
reader: R,
tx_has_sapling: bool,
components: Self::V4Components,
) -> io::Result<Option<Self::Bundle>>;

fn write_v4_components<W: io::Write>(
bundle: Option<&Self::Bundle>,
writer: W,
tx_has_sapling: bool,
) -> io::Result<()>;

fn write_v4_binding_sig<W: io::Write>(
bundle: Option<&Self::Bundle>,
writer: W,
tx_has_sapling: bool,
) -> io::Result<()>;

fn read_v5_bundle<R: io::Read>(reader: R) -> io::Result<Option<Self::Bundle>>;

fn write_v5_bundle<W: io::Write>(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()>;
}

/// The Orchard part of an authorized transaction.
pub trait AuthorizedOrchardPart: OrchardPart {}
pub trait AuthorizedOrchardPart: OrchardPart {
fn read_v5_bundle<R: io::Read>(reader: R) -> io::Result<Option<Self::Bundle>>;

fn write_v5_bundle<W: io::Write>(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()>;
}

/// The TZE part of an authorized transaction.
#[cfg(zcash_unstable = "zfuture")]
pub trait AuthorizedTzePart: TzePart {}
pub trait AuthorizedTzePart: TzePart {
fn read_bundle<R: io::Read>(reader: R, tx_has_tze: bool) -> io::Result<Option<Self::Bundle>>;

fn write_bundle<W: io::Write>(
bundle: Option<&Self::Bundle>,
writer: W,
tx_has_tze: bool,
) -> io::Result<()>;
}
10 changes: 9 additions & 1 deletion zcash_primitives/src/transaction/components/orchard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,15 @@ impl MapAuth<Authorized, Authorized> for () {
}
}

impl AuthorizedOrchardPart for Orchard<orchard::bundle::Authorized> {}
impl AuthorizedOrchardPart for Orchard<orchard::bundle::Authorized> {
fn read_v5_bundle<R: Read>(reader: R) -> io::Result<Option<Self::Bundle>> {
read_v5_bundle(reader)
}

fn write_v5_bundle<W: Write>(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()> {
write_v5_bundle(bundle, writer)
}
}

/// Reads an [`orchard::Bundle`] from a v5 transaction format.
pub fn read_v5_bundle<R: Read>(
Expand Down
69 changes: 68 additions & 1 deletion zcash_primitives/src/transaction/components/sapling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,74 @@ impl MapAuth<Authorized, Authorized> for () {
}
}

impl AuthorizedSaplingPart for Sapling<sapling::bundle::Authorized> {}
impl AuthorizedSaplingPart for Sapling<sapling::bundle::Authorized> {
type V4Components = (
Amount,
Vec<sapling::bundle::SpendDescription<sapling::bundle::Authorized>>,
Vec<sapling::bundle::OutputDescription<sapling::bundle::GrothProofBytes>>,
);

fn read_v4_components<R: Read>(
reader: R,
tx_has_sapling: bool,
) -> io::Result<Self::V4Components> {
read_v4_components(reader, tx_has_sapling)
}

fn read_v4_binding_sig<R: Read>(
mut reader: R,
tx_has_sapling: bool,
(value_balance, shielded_spends, shielded_outputs): Self::V4Components,
) -> io::Result<Option<Self::Bundle>> {
let binding_sig =
if tx_has_sapling && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) {
let mut sig = [0; 64];
reader.read_exact(&mut sig)?;
Some(redjubjub::Signature::from(sig))
} else {
None
};

Ok(binding_sig.and_then(|binding_sig| {
sapling::Bundle::from_parts(
shielded_spends,
shielded_outputs,
value_balance,
sapling::bundle::Authorized { binding_sig },
)
}))
}

fn write_v4_components<W: Write>(
bundle: Option<&Self::Bundle>,
writer: W,
tx_has_sapling: bool,
) -> io::Result<()> {
write_v4_components(writer, bundle, tx_has_sapling)
}

fn write_v4_binding_sig<W: Write>(
bundle: Option<&Self::Bundle>,
mut writer: W,
tx_has_sapling: bool,
) -> io::Result<()> {
if tx_has_sapling {
if let Some(bundle) = bundle {
writer.write_all(&<[u8; 64]>::from(bundle.authorization().binding_sig))?;

Check warning on line 140 in zcash_primitives/src/transaction/components/sapling.rs

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components/sapling.rs#L140

Added line #L140 was not covered by tests
}
}

Ok(())
}

fn read_v5_bundle<R: Read>(reader: R) -> io::Result<Option<Self::Bundle>> {
read_v5_bundle(reader)
}

fn write_v5_bundle<W: Write>(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()> {
write_v5_bundle(writer, bundle)
}
}

/// Consensus rules (§4.4) & (§4.5):
/// - Canonical encoding is enforced here.
Expand Down
47 changes: 46 additions & 1 deletion zcash_primitives/src/transaction/components/sprout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

use std::io::{self, Read, Write};

use zcash_encoding::{CompactSize, Vector};

use super::{amount::Amount, AuthorizedSproutPart, Sprout, GROTH_PROOF_SIZE};

// π_A + π_A' + π_B + π_B' + π_C + π_C' + π_K + π_H
Expand Down Expand Up @@ -29,7 +31,50 @@ impl Bundle {
}
}

impl AuthorizedSproutPart for Sprout {}
impl AuthorizedSproutPart for Sprout {
fn read_v4_bundle<R: io::Read>(
mut reader: R,
tx_has_sprout: bool,
use_groth: bool,
) -> io::Result<Option<Self::Bundle>> {
if tx_has_sprout {
let joinsplits = Vector::read(&mut reader, |r| JsDescription::read(r, use_groth))?;

if !joinsplits.is_empty() {

Check warning on line 43 in zcash_primitives/src/transaction/components/sprout.rs

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components/sprout.rs#L43

Added line #L43 was not covered by tests
let mut bundle = Bundle {
joinsplits,
joinsplit_pubkey: [0; 32],
joinsplit_sig: [0; 64],
};
reader.read_exact(&mut bundle.joinsplit_pubkey)?;
reader.read_exact(&mut bundle.joinsplit_sig)?;
Ok(Some(bundle))
} else {
Ok(None)
}
} else {
Ok(None)

Check warning on line 56 in zcash_primitives/src/transaction/components/sprout.rs

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components/sprout.rs#L56

Added line #L56 was not covered by tests
}
}

fn write_v4_bundle<W: io::Write>(
bundle: Option<&Self::Bundle>,
mut writer: W,
tx_has_sprout: bool,
) -> io::Result<()> {
if tx_has_sprout {
if let Some(bundle) = bundle {
Vector::write(&mut writer, &bundle.joinsplits, |w, e| e.write(w))?;
writer.write_all(&bundle.joinsplit_pubkey)?;
writer.write_all(&bundle.joinsplit_sig)?;
} else {
CompactSize::write(&mut writer, 0)?;
}
}

Ok(())
}
}

#[derive(Clone)]
#[allow(clippy::upper_case_acronyms)]
Expand Down
29 changes: 28 additions & 1 deletion zcash_primitives/src/transaction/components/transparent.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Structs representing the components within Zcash transactions.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use zcash_encoding::{CompactSize, Vector};

use std::fmt::Debug;
use std::io::{self, Read, Write};
Expand Down Expand Up @@ -216,7 +217,33 @@ impl TxOut {
}
}

impl AuthorizedTransparentPart for Transparent<Authorized> {}
impl AuthorizedTransparentPart for Transparent<Authorized> {
fn read_bundle<R: Read>(mut reader: R) -> io::Result<Option<Self::Bundle>> {
let vin = Vector::read(&mut reader, TxIn::read)?;
let vout = Vector::read(&mut reader, TxOut::read)?;
Ok(if vin.is_empty() && vout.is_empty() {
None
} else {
Some(Bundle {
vin,
vout,
authorization: Authorized,
})
})
}

fn write_bundle<W: Write>(bundle: Option<&Self::Bundle>, mut writer: W) -> io::Result<()> {
if let Some(bundle) = bundle {
Vector::write(&mut writer, &bundle.vin, |w, e| e.write(w))?;
Vector::write(&mut writer, &bundle.vout, |w, e| e.write(w))?;
} else {
CompactSize::write(&mut writer, 0)?;
CompactSize::write(&mut writer, 0)?;
}

Ok(())
}
}

#[cfg(any(test, feature = "test-dependencies"))]
pub mod testing {
Expand Down
38 changes: 37 additions & 1 deletion zcash_primitives/src/transaction/components/tze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,43 @@ impl TzeOut {
}
}

impl AuthorizedTzePart for Tze<Authorized> {}
impl AuthorizedTzePart for Tze<Authorized> {
fn read_bundle<R: Read>(mut reader: R, tx_has_tze: bool) -> io::Result<Option<Self::Bundle>> {
if tx_has_tze {
let vin = Vector::read(&mut reader, TzeIn::read)?;
let vout = Vector::read(&mut reader, TzeOut::read)?;
Ok(if vin.is_empty() && vout.is_empty() {
None

Check warning on line 239 in zcash_primitives/src/transaction/components/tze.rs

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components/tze.rs#L234-L239

Added lines #L234 - L239 were not covered by tests
} else {
Some(Bundle {
vin,
vout,
authorization: Authorized,

Check warning on line 244 in zcash_primitives/src/transaction/components/tze.rs

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components/tze.rs#L241-L244

Added lines #L241 - L244 were not covered by tests
})
})
} else {
Ok(None)

Check warning on line 248 in zcash_primitives/src/transaction/components/tze.rs

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components/tze.rs#L248

Added line #L248 was not covered by tests
}
}

fn write_bundle<W: Write>(
bundle: Option<&Self::Bundle>,
mut writer: W,
tx_has_tze: bool,
) -> io::Result<()> {
if tx_has_tze {
if let Some(bundle) = bundle {
Vector::write(&mut writer, &bundle.vin, |w, e| e.write(w))?;
Vector::write(&mut writer, &bundle.vout, |w, e| e.write(w))?;

Check warning on line 260 in zcash_primitives/src/transaction/components/tze.rs

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components/tze.rs#L257-L260

Added lines #L257 - L260 were not covered by tests
} else {
CompactSize::write(&mut writer, 0)?;
CompactSize::write(&mut writer, 0)?;

Check warning on line 263 in zcash_primitives/src/transaction/components/tze.rs

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components/tze.rs#L262-L263

Added lines #L262 - L263 were not covered by tests
}
}

Ok(())

Check warning on line 267 in zcash_primitives/src/transaction/components/tze.rs

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components/tze.rs#L267

Added line #L267 was not covered by tests
}
}

#[cfg(any(test, feature = "test-dependencies"))]
pub mod testing {
Expand Down
Loading

0 comments on commit 52bb40d

Please sign in to comment.