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 13, 2024
1 parent 63852a1 commit 8dde110
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 4 deletions.
231 changes: 231 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 @@ -59,10 +61,32 @@ impl<A: Authorization> Bundles for AllBundles<A> {
type Orchard = Orchard<A::OrchardAuth>;
}

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

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 @@ -93,6 +117,24 @@ 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;
type Authorization = transparent::Authorized;

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 @@ -115,6 +157,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 @@ -129,6 +180,16 @@ pub trait SaplingPart {
type Authorization: ::sapling::bundle::Authorization;
}

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;
type Authorization = ::sapling::bundle::Authorized;
}

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

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;
type Authorization = ::orchard::bundle::Authorized;
}

#[derive(Debug)]
pub struct Orchard<A: ::orchard::bundle::Authorization> {
_auth: PhantomData<A>,
Expand All @@ -162,6 +233,29 @@ pub(super) trait TransparentEnc:
fn write<W: io::Write>(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()>;
}

impl TransparentEnc for NoTransparent {
fn read<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<W: io::Write>(_: Option<&Self::Bundle>, mut writer: W) -> io::Result<()> {
CompactSize::write(&mut writer, 0)?;
CompactSize::write(&mut writer, 0)
}
}

pub(super) trait SproutEnc: SproutPart {
fn read_v4_bundle<R: io::Read>(
reader: R,
Expand All @@ -176,6 +270,38 @@ pub(super) trait SproutEnc: SproutPart {
) -> io::Result<()>;
}

impl SproutEnc 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(super) trait SaplingEnc:
SaplingPart<Authorization = ::sapling::bundle::Authorized>
{
Expand Down Expand Up @@ -209,10 +335,115 @@ pub(super) trait SaplingEnc:
fn write_v5_bundle<W: io::Write>(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()>;
}

impl SaplingEnc 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(super) trait OrchardEnc:
OrchardPart<Authorization = ::orchard::bundle::Authorized>
{
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 OrchardEnc 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)
}
}
51 changes: 49 additions & 2 deletions zcash_primitives/src/transaction/sighash_v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use super::{
sapling as sapling_serialization,
sprout::JsDescription,
transparent::{self, TxIn, TxOut},
Bundles, Sapling, SaplingPart, ShieldedValueBalance, Sprout, SproutPart, Transparent,
TransparentPart,
Bundles, NoSapling, NoSprout, NoTransparent, Sapling, SaplingPart, ShieldedValueBalance,
Sprout, SproutPart, Transparent, TransparentPart,
},
sighash::{SignableInput, SIGHASH_ANYONECANPAY, SIGHASH_MASK, SIGHASH_NONE, SIGHASH_SINGLE},
TransactionData,
Expand Down Expand Up @@ -58,6 +58,37 @@ pub trait TransparentSigDigester: TransparentPart {
) -> Vec<u8>;
}

impl TransparentSigDigester for NoTransparent {
fn digest_prevout(_: Option<&Self::Bundle>) -> Blake2bHash {
Blake2bParams::new()
.hash_length(32)
.personal(ZCASH_PREVOUTS_HASH_PERSONALIZATION)
.hash(&[])
}

fn digest_sequence(_: Option<&Self::Bundle>) -> Blake2bHash {
Blake2bParams::new()
.hash_length(32)
.personal(ZCASH_SEQUENCE_HASH_PERSONALIZATION)
.hash(&[])
}

fn digest_outputs(_: Option<&Self::Bundle>) -> Blake2bHash {
Blake2bParams::new()
.hash_length(32)
.personal(ZCASH_OUTPUTS_HASH_PERSONALIZATION)
.hash(&[])
}

fn digest_single_output(_: Option<&Self::Bundle>, _: &SignableInput<'_>) -> [u8; 32] {
[0; 32]
}

fn digest_signable_input(_: Option<&Self::Bundle>, _: &SignableInput<'_>) -> Vec<u8> {
panic!("A request has been made to sign a transparent input, but none are present.");
}
}

impl<A: transparent::Authorization> TransparentSigDigester for Transparent<A> {
fn digest_prevout(transparent_bundle: Option<&Self::Bundle>) -> Blake2bHash {
prevout_hash(transparent_bundle.map_or(&[], |b| b.vin.as_slice()))
Expand Down Expand Up @@ -171,6 +202,12 @@ pub trait SproutSigDigester: SproutPart {
) -> [u8; 32];
}

impl SproutSigDigester for NoSprout {
fn digest_joinsplits(_: BranchId, _: Option<&Self::Bundle>) -> [u8; 32] {
[0; 32]
}
}

impl SproutSigDigester for Sprout {
fn digest_joinsplits(
consensus_branch_id: BranchId,
Expand Down Expand Up @@ -223,6 +260,16 @@ pub trait SaplingSigDigester: SaplingPart {
fn digest_outputs(sapling_bundle: Option<&Self::Bundle>) -> [u8; 32];
}

impl SaplingSigDigester for NoSapling {
fn digest_spends(_: Option<&Self::Bundle>) -> [u8; 32] {
[0; 32]
}

fn digest_outputs(_: Option<&Self::Bundle>) -> [u8; 32] {
[0; 32]
}
}

impl<A> SaplingSigDigester for Sapling<A>
where
A: sapling::bundle::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
Expand Down
Loading

0 comments on commit 8dde110

Please sign in to comment.