Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zcash_primitives: Refactor Transaction to permit omitting protocol-specific bundles #1388

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions devtools/src/bin/inspect/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
legacy::{keys::pubkey_to_address, Script, TransparentAddress},
memo::{Memo, MemoBytes},
transaction::{
components::{amount::NonNegativeAmount, sapling as sapling_serialization, transparent},
components::{
amount::NonNegativeAmount, sapling as sapling_serialization, transparent, AllBundles,
},
sighash::{signature_hash, SignableInput, TransparentAuthorizingContext},
txid::TxIdDigester,
Authorization, Transaction, TransactionData, TxId, TxVersion,
Expand Down Expand Up @@ -207,7 +209,7 @@
tx.write(&mut buf).unwrap();
let tx = Transaction::read(&buf[..], tx.consensus_branch_id()).unwrap();

let tx: TransactionData<PrecomputedAuth> = tx.into_data().map_authorization(
let tx: TransactionData<AllBundles<PrecomputedAuth>> = tx.into_data().map_authorization(

Check warning on line 212 in devtools/src/bin/inspect/transaction.rs

View check run for this annotation

Codecov / codecov/patch

devtools/src/bin/inspect/transaction.rs#L212

Added line #L212 was not covered by tests
f_transparent,
(),
(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ mod tests {
use tempfile::NamedTempFile;
use zcash_primitives::{
legacy::{Script, TransparentAddress},
transaction::{components::transparent, Authorized, TransactionData, TxVersion},
transaction::{components::{transparent, AllBundles}, Authorized, TransactionData, TxVersion},
};
use zcash_protocol::{
consensus::{BranchId, Network},
Expand Down Expand Up @@ -185,7 +185,7 @@ mod tests {
.unwrap();

// Add transactions to the wallet that exercise the data migration.
let add_tx_to_wallet = |tx: TransactionData<Authorized>| {
let add_tx_to_wallet = |tx: TransactionData<AllBundles<Authorized>>| {
let tx = tx.freeze().unwrap();
let txid = tx.txid();
let mut raw_tx = vec![];
Expand Down
11 changes: 6 additions & 5 deletions zcash_primitives/src/transaction/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::{
components::{
amount::{Amount, BalanceError},
transparent::{self, builder::TransparentBuilder, TxOut},
AllBundles,
},
fees::{
transparent::{InputView, OutputView},
Expand Down Expand Up @@ -288,7 +289,7 @@ pub struct Builder<'a, P, U: sapling::builder::ProverProgress> {
sapling_asks: Vec<sapling::keys::SpendAuthorizingKey>,
orchard_saks: Vec<orchard::keys::SpendAuthorizingKey>,
#[cfg(zcash_unstable = "zfuture")]
tze_builder: TzeBuilder<'a, TransactionData<Unauthorized>>,
tze_builder: TzeBuilder<'a, TransactionData<AllBundles<Unauthorized>>>,
#[cfg(not(zcash_unstable = "zfuture"))]
tze_builder: std::marker::PhantomData<&'a ()>,
progress_notifier: U,
Expand Down Expand Up @@ -746,7 +747,7 @@ impl<'a, P: consensus::Parameters, U: sapling::builder::ProverProgress> Builder<
#[cfg(zcash_unstable = "zfuture")]
let (tze_bundle, tze_signers) = self.tze_builder.build();

let unauthed_tx: TransactionData<Unauthorized> = TransactionData {
let unauthed_tx: TransactionData<AllBundles<Unauthorized>> = TransactionData {
version,
consensus_branch_id: BranchId::for_height(&self.params, self.target_height),
lock_time: 0,
Expand All @@ -765,7 +766,7 @@ impl<'a, P: consensus::Parameters, U: sapling::builder::ProverProgress> Builder<
let txid_parts = unauthed_tx.digest(TxIdDigester);

let transparent_bundle = unauthed_tx.transparent_bundle.clone().map(|b| {
b.apply_signatures(
b.apply_signatures::<_, _, AllBundles<Unauthorized>>(
#[cfg(feature = "transparent-inputs")]
&unauthed_tx,
#[cfg(feature = "transparent-inputs")]
Expand Down Expand Up @@ -841,7 +842,7 @@ impl<'a, P: consensus::Parameters, U: sapling::builder::ProverProgress> Builder<
impl<'a, P: consensus::Parameters, U: sapling::builder::ProverProgress> ExtensionTxBuilder<'a>
for Builder<'a, P, U>
{
type BuildCtx = TransactionData<Unauthorized>;
type BuildCtx = TransactionData<AllBundles<Unauthorized>>;
type BuildError = tze::builder::Error;

fn add_tze_input<WBuilder, W: ToPayload>(
Expand Down Expand Up @@ -950,7 +951,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
227 changes: 226 additions & 1 deletion zcash_primitives/src/transaction/components.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
//! Structs representing the components within Zcash transactions.
//! Types representing the components within Zcash transactions.

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

use zcash_protocol::value::BalanceError;

pub mod amount {
pub use zcash_protocol::value::{
BalanceError, ZatBalance as Amount, Zatoshis as NonNegativeAmount, COIN,
Expand Down Expand Up @@ -29,5 +35,224 @@
#[cfg(zcash_unstable = "zfuture")]
pub use self::tze::{TzeIn, TzeOut};

use super::Authorization;

// π_A + π_B + π_C
pub const GROTH_PROOF_SIZE: usize = 48 + 96 + 48;

/// The protocol-specific bundles of data within a transaction.
pub trait Bundles {
type Transparent: TransparentPart;
type Sprout: SproutPart;
type Sapling: SaplingPart;
type Orchard: OrchardPart;

#[cfg(zcash_unstable = "zfuture")]
type Tze: TzePart;
}

/// Marker type for a transaction that may contain payments within any Zcash protocol.
#[derive(Debug)]
pub struct AllBundles<A: Authorization> {
_auth: PhantomData<A>,
}

impl<A: Authorization> Bundles for AllBundles<A> {
type Transparent = Transparent<A::TransparentAuth>;
type Sprout = Sprout;
type Sapling = Sapling<A::SaplingAuth>;
type Orchard = Orchard<A::OrchardAuth>;

#[cfg(zcash_unstable = "zfuture")]
type Tze = Tze<A::TzeAuth>;
}

/// The protocol-agnostic parts of a shielded bundle.
///
/// The trait methods can be implemented without any knowledge of protocol-specific
/// details, only requiring the ability to parse the general bundle structure within a
/// transaction.
pub trait ShieldedBundle {
fn value_balance(&self) -> Amount;
}

impl ShieldedBundle for sprout::Bundle {
fn value_balance(&self) -> Amount {

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

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components.rs#L80

Added line #L80 was not covered by tests
// We don't support building Sprout bundles in Rust.
self.value_balance()

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

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components.rs#L82

Added line #L82 was not covered by tests
.expect("Sprout bundles are all checked by consensus")
}
}

impl<A: ::sapling::bundle::Authorization> ShieldedBundle for ::sapling::Bundle<A, Amount> {
fn value_balance(&self) -> Amount {
*self.value_balance()
}
}

impl<A: ::orchard::bundle::Authorization> ShieldedBundle for ::orchard::Bundle<A, Amount> {
fn value_balance(&self) -> Amount {
*self.value_balance()

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

View check run for this annotation

Codecov / codecov/patch

zcash_primitives/src/transaction/components.rs#L94-L95

Added lines #L94 - L95 were not covered by tests
}
}

/// The transparent part of a transaction.
pub trait TransparentPart {
type Bundle;

fn value_balance<E, F>(bundle: &Self::Bundle, get_prevout_value: F) -> Result<Amount, E>
where
E: From<BalanceError>,
F: FnMut(&OutPoint) -> Result<Amount, E>;
}

#[derive(Debug)]
pub struct Transparent<A: transparent::Authorization> {
_auth: PhantomData<A>,
}

impl<A: transparent::Authorization> TransparentPart for Transparent<A> {
type Bundle = transparent::Bundle<A>;

fn value_balance<E, F>(bundle: &Self::Bundle, get_prevout_value: F) -> Result<Amount, E>
where
E: From<BalanceError>,
F: FnMut(&OutPoint) -> Result<Amount, E>,
{
bundle.value_balance(get_prevout_value)
}
}

/// The Sprout part of a transaction.
pub trait SproutPart {
type Bundle: ShieldedBundle;
}

/// Marker type for a transaction that may contain a Sprout part.
#[derive(Debug)]
pub struct Sprout;

impl SproutPart for Sprout {
type Bundle = sprout::Bundle;
}

/// The Sapling part of a transaction.
pub trait SaplingPart {
type Bundle: ShieldedBundle;
}

/// Marker type for a transaction that may contain a Sapling part.
#[derive(Debug)]
pub struct Sapling<A> {
_auth: PhantomData<A>,
}

impl<A: ::sapling::bundle::Authorization> SaplingPart for Sapling<A> {
type Bundle = ::sapling::Bundle<A, Amount>;
}

/// The Orchard part of a transaction.
pub trait OrchardPart {
type Bundle: ShieldedBundle;
}

/// Marker type for a transaction that may contain an Orchard part.
#[derive(Debug)]
pub struct Orchard<A> {
_auth: PhantomData<A>,
}

impl<A: ::orchard::bundle::Authorization> OrchardPart for Orchard<A> {
type Bundle = ::orchard::bundle::Bundle<A, Amount>;
}

/// The TZE part of a transaction.
#[cfg(zcash_unstable = "zfuture")]
pub trait TzePart {
type Bundle;
}

/// Marker type for a transaction that may contain a TZE part.
#[cfg(zcash_unstable = "zfuture")]
#[derive(Debug)]
pub struct Tze<A: tze::Authorization> {
_auth: PhantomData<A>,
}

#[cfg(zcash_unstable = "zfuture")]
impl<A: tze::Authorization> TzePart for Tze<A> {
type Bundle = tze::Bundle<A>;
}

/// The Transparent part of an authorized transaction.
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 {
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 {
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 {
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 {
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<()>;
}
12 changes: 11 additions & 1 deletion zcash_primitives/src/transaction/components/orchard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use orchard::{
};
use zcash_encoding::{Array, CompactSize, Vector};

use super::Amount;
use super::{Amount, AuthorizedOrchardPart, Orchard};
use crate::transaction::Transaction;

pub const FLAG_SPENDS_ENABLED: u8 = 0b0000_0001;
Expand Down Expand Up @@ -44,6 +44,16 @@ impl MapAuth<Authorized, Authorized> for () {
}
}

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>(
mut reader: R,
Expand Down
Loading
Loading