Skip to content

Commit

Permalink
zcash_primitives: Add marker types for omitting transaction parts
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed May 16, 2024
1 parent eeab4dd commit 5777a3e
Show file tree
Hide file tree
Showing 4 changed files with 374 additions and 6 deletions.
281 changes: 281 additions & 0 deletions zcash_primitives/src/transaction/components.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! Structs representing the components within Zcash transactions.
use std::convert::Infallible;
use std::io;
use std::marker::PhantomData;

use zcash_encoding::CompactSize;
use zcash_protocol::value::BalanceError;

pub mod amount {
Expand Down Expand Up @@ -65,10 +67,35 @@ impl<A: Authorization> Bundles for AllBundles<A> {
type Tze = Tze<A::TzeAuth>;
}

pub struct SomeBundles<T: TransparentPart, Sp: SproutPart, Sa: SaplingPart, O: OrchardPart> {
_transparent: PhantomData<T>,
_sprout: PhantomData<Sp>,
_sapling: PhantomData<Sa>,
_orchard: PhantomData<O>,
}

impl<T: TransparentPart, Sp: SproutPart, Sa: SaplingPart, O: OrchardPart> Bundles
for SomeBundles<T, Sp, Sa, O>
{
type Transparent = T;
type Sprout = Sp;
type Sapling = Sa;
type Orchard = O;

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

pub trait ShieldedValueBalance {
fn value_balance(&self) -> Amount;
}

impl ShieldedValueBalance for Infallible {
fn value_balance(&self) -> Amount {
Amount::zero()
}
}

impl ShieldedValueBalance for sprout::Bundle {
fn value_balance(&self) -> Amount {
// We don't support building Sprout bundles in Rust.
Expand Down Expand Up @@ -98,6 +125,23 @@ pub trait TransparentPart {
F: FnMut(&OutPoint) -> Result<Amount, E>;
}

pub enum NoTransparent {}

impl TransparentPart for NoTransparent {
/// Values of this type are not exposed in the public API (the transaction method is
/// gated off). We also make the type uninhabitable, so that `Option<Self::Bundle>`
/// can only ever be `None`.
type Bundle = Infallible;

fn value_balance<E, F>(_: &Self::Bundle, _: F) -> Result<Amount, E>
where
E: From<BalanceError>,
F: FnMut(&OutPoint) -> Result<Amount, E>,
{
Ok(Amount::zero())
}
}

#[derive(Debug)]
pub struct Transparent<A: transparent::Authorization> {
_auth: PhantomData<A>,
Expand All @@ -119,6 +163,15 @@ pub trait SproutPart {
type Bundle: ShieldedValueBalance;
}

pub enum NoSprout {}

impl SproutPart for NoSprout {
/// Values of this type are not exposed in the public API (the transaction method is
/// gated off). We also make the type uninhabitable, so that `Option<Self::Bundle>`
/// can only ever be `None`.
type Bundle = Infallible;
}

#[derive(Debug)]
pub struct Sprout;

Expand All @@ -130,6 +183,15 @@ pub trait SaplingPart {
type Bundle: ShieldedValueBalance;
}

pub enum NoSapling {}

impl SaplingPart for NoSapling {
/// Values of this type are not exposed in the public API (the transaction method is
/// gated off). We also make the type uninhabitable, so that `Option<Self::Bundle>`
/// can only ever be `None`.
type Bundle = Infallible;
}

#[derive(Debug)]
pub struct Sapling<A: ::sapling::bundle::Authorization> {
_auth: PhantomData<A>,
Expand All @@ -143,6 +205,15 @@ pub trait OrchardPart {
type Bundle: ShieldedValueBalance;
}

pub enum NoOrchard {}

impl OrchardPart for NoOrchard {
/// Values of this type are not exposed in the public API (the transaction method is
/// gated off). We also make the type uninhabitable, so that `Option<Self::Bundle>`
/// can only ever be `None`.
type Bundle = Infallible;
}

#[derive(Debug)]
pub struct Orchard<A: ::orchard::bundle::Authorization> {
_auth: PhantomData<A>,
Expand All @@ -157,6 +228,17 @@ pub trait TzePart {
type Bundle;
}

#[cfg(zcash_unstable = "zfuture")]
pub enum NoTze {}

#[cfg(zcash_unstable = "zfuture")]
impl TzePart for NoTze {
/// Values of this type are not exposed in the public API (the transaction method is
/// gated off). We also make the type uninhabitable, so that `Option<Self::Bundle>`
/// can only ever be `None`.
type Bundle = Infallible;
}

#[cfg(zcash_unstable = "zfuture")]
#[derive(Debug)]
pub struct Tze<A: tze::Authorization> {
Expand All @@ -174,6 +256,29 @@ pub trait AuthorizedTransparentPart: TransparentPart {
fn write_bundle<W: io::Write>(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()>;
}

impl AuthorizedTransparentPart for NoTransparent {
fn read_bundle<R: io::Read>(mut reader: R) -> io::Result<Option<Self::Bundle>> {
match CompactSize::read(&mut reader)? {
0 => match CompactSize::read(&mut reader)? {
0 => Ok(None),
n => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected no transparent outputs, found {}", n),
)),
},
n => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected no transparent inputs, found {}", n),
)),
}
}

fn write_bundle<W: io::Write>(_: Option<&Self::Bundle>, mut writer: W) -> io::Result<()> {
CompactSize::write(&mut writer, 0)?;
CompactSize::write(&mut writer, 0)
}
}

pub trait AuthorizedSproutPart: SproutPart {
fn read_v4_bundle<R: io::Read>(
reader: R,
Expand All @@ -188,6 +293,38 @@ pub trait AuthorizedSproutPart: SproutPart {
) -> io::Result<()>;
}

impl AuthorizedSproutPart for NoSprout {
fn read_v4_bundle<R: io::Read>(
reader: R,
tx_has_sprout: bool,
_: bool,
) -> io::Result<Option<Self::Bundle>> {
if tx_has_sprout {
match CompactSize::read(reader)? {
0 => Ok(None),
n => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected no Sprout JoinSplits, found {}", n),
)),
}
} else {
Ok(None)
}
}

fn write_v4_bundle<W: io::Write>(
_: Option<&Self::Bundle>,
writer: W,
tx_has_sprout: bool,
) -> io::Result<()> {
if tx_has_sprout {
CompactSize::write(writer, 0)?;
}

Ok(())
}
}

pub trait AuthorizedSaplingPart: SaplingPart {
type V4Components;

Expand Down Expand Up @@ -219,12 +356,117 @@ pub trait AuthorizedSaplingPart: SaplingPart {
fn write_v5_bundle<W: io::Write>(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()>;
}

impl AuthorizedSaplingPart for NoSapling {
type V4Components = ();

fn read_v4_components<R: io::Read>(
mut reader: R,
tx_has_sapling: bool,
) -> io::Result<Self::V4Components> {
if tx_has_sapling {
const ZERO: Amount = Amount::zero();

match super::Transaction::read_amount(&mut reader)? {
ZERO => match CompactSize::read(&mut reader)? {
0 => match CompactSize::read(&mut reader)? {
0 => Ok(()),
n => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected no Sapling Outputs, found {}", n),
)),
},
n => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected no Sapling Spends, found {}", n),
)),
},
vb => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Expected no Sapling valueBalance, found {} zatoshis",
i64::from(vb)
),
)),
}
} else {
Ok(())
}
}

fn read_v4_binding_sig<R: io::Read>(
_: R,
_: bool,
_: Self::V4Components,
) -> io::Result<Option<Self::Bundle>> {
Ok(None)
}

fn write_v4_components<W: io::Write>(
_: Option<&Self::Bundle>,
mut writer: W,
tx_has_sapling: bool,
) -> io::Result<()> {
if tx_has_sapling {
writer.write_all(&Amount::zero().to_i64_le_bytes())?;
CompactSize::write(&mut writer, 0)?;
CompactSize::write(&mut writer, 0)?;
}

Ok(())
}

fn write_v4_binding_sig<W: io::Write>(
_: Option<&Self::Bundle>,
_: W,
_: bool,
) -> io::Result<()> {
Ok(())
}

fn read_v5_bundle<R: io::Read>(mut reader: R) -> io::Result<Option<Self::Bundle>> {
match CompactSize::read(&mut reader)? {
0 => match CompactSize::read(&mut reader)? {
0 => Ok(None),
n => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected no Sapling Outputs, found {}", n),
)),
},
n => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected no Sapling Spends, found {}", n),
)),
}
}

fn write_v5_bundle<W: io::Write>(_: Option<&Self::Bundle>, mut writer: W) -> io::Result<()> {
CompactSize::write(&mut writer, 0)?;
CompactSize::write(&mut writer, 0)
}
}

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<()>;
}

impl AuthorizedOrchardPart for NoOrchard {
fn read_v5_bundle<R: io::Read>(reader: R) -> io::Result<Option<Self::Bundle>> {
match CompactSize::read(reader)? {
0 => Ok(None),
n => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected no Orchard Actions, found {}", n),
)),
}
}

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

#[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>>;
Expand All @@ -235,3 +477,42 @@ pub trait AuthorizedTzePart: TzePart {
tx_has_tze: bool,
) -> io::Result<()>;
}

#[cfg(zcash_unstable = "zfuture")]
impl AuthorizedTzePart for NoTze {
fn read_bundle<R: io::Read>(
mut reader: R,
tx_has_tze: bool,
) -> io::Result<Option<Self::Bundle>> {
if tx_has_tze {
match CompactSize::read(&mut reader)? {
0 => match CompactSize::read(&mut reader)? {
0 => Ok(None),
n => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected no TZE outputs, found {}", n),
)),
},
n => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Expected no TZE inputs, found {}", n),
)),
}
} else {
Ok(None)
}
}

fn write_bundle<W: io::Write>(
_: Option<&Self::Bundle>,
mut writer: W,
tx_has_tze: bool,
) -> io::Result<()> {
if tx_has_tze {
CompactSize::write(&mut writer, 0)?;
CompactSize::write(&mut writer, 0)?;
}

Ok(())
}
}
Loading

0 comments on commit 5777a3e

Please sign in to comment.