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 11, 2024
1 parent e3bb22f commit eeeacc8
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 3 deletions.
170 changes: 170 additions & 0 deletions zcash_primitives/src/transaction/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::io;
use std::marker::PhantomData;

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

pub mod amount {
Expand Down Expand Up @@ -42,6 +43,12 @@ pub trait ShieldedValueBalance {
fn value_balance(&self) -> Amount;
}

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

impl<A: ::sapling::bundle::Authorization> ShieldedValueBalance for ::sapling::Bundle<A, Amount> {
fn value_balance(&self) -> Amount {
*self.value_balance()
Expand All @@ -64,6 +71,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).
type Bundle = ();
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 @@ -89,6 +113,15 @@ 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).
type Bundle = ();
type Authorization = ::sapling::bundle::Authorized;
}

#[derive(Debug)]
pub struct Sapling<A: ::sapling::bundle::Authorization> {
_auth: PhantomData<A>,
Expand All @@ -104,6 +137,15 @@ 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).
type Bundle = ();
type Authorization = ::orchard::bundle::Authorized;
}

#[derive(Debug)]
pub struct Orchard<A: ::orchard::bundle::Authorization> {
_auth: PhantomData<A>,
Expand All @@ -122,6 +164,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 SaplingEnc:
SaplingPart<Authorization = ::sapling::bundle::Authorized>
{
Expand Down Expand Up @@ -155,10 +220,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)
}
}
44 changes: 43 additions & 1 deletion zcash_primitives/src/transaction/sighash_v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use super::{
sapling as sapling_serialization,
sprout::JsDescription,
transparent::{self, TxIn, TxOut},
OrchardPart, Sapling, SaplingPart, ShieldedValueBalance, Transparent, TransparentPart,
NoSapling, NoTransparent, OrchardPart, Sapling, SaplingPart, ShieldedValueBalance,
Transparent, TransparentPart,
},
sighash::{SignableInput, SIGHASH_ANYONECANPAY, SIGHASH_MASK, SIGHASH_NONE, SIGHASH_SINGLE},
TransactionData,
Expand Down Expand Up @@ -57,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 @@ -190,6 +222,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
11 changes: 10 additions & 1 deletion zcash_primitives/src/transaction/sighash_v5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use zcash_encoding::Array;
use crate::transaction::{
components::{
transparent::{self, TxOut},
OrchardPart, SaplingPart, Transparent, TransparentPart,
NoTransparent, OrchardPart, SaplingPart, Transparent, TransparentPart,
},
sighash::{
SignableInput, TransparentAuthorizingContext, SIGHASH_ANYONECANPAY, SIGHASH_MASK,
Expand Down Expand Up @@ -47,6 +47,15 @@ pub trait TransparentSigDigester: TransparentPart {
) -> Blake2bHash;
}

impl TransparentSigDigester for NoTransparent {
fn digest(
_: Option<(&Self::Bundle, &TransparentDigests<Blake2bHash>)>,
_: &SignableInput<'_>,
) -> Blake2bHash {
hash_transparent_txid_data(None)
}
}

impl<A: TransparentAuthorizingContext> TransparentSigDigester for Transparent<A> {
fn digest(
tx_data: Option<(&Self::Bundle, &TransparentDigests<Blake2bHash>)>,
Expand Down
21 changes: 20 additions & 1 deletion zcash_primitives/src/transaction/txid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ use super::{
components::{
amount::Amount,
transparent::{self, TxIn, TxOut},
Orchard, OrchardPart, Sapling, SaplingPart, Transparent, TransparentPart,
NoOrchard, NoSapling, NoTransparent, Orchard, OrchardPart, Sapling, SaplingPart,
Transparent, TransparentPart,
},
TransactionDigest, TransparentDigests, TxDigests, TxId, TxVersion,
};
Expand Down Expand Up @@ -204,6 +205,12 @@ trait TransparentDigester: TransparentPart {
-> Option<TransparentDigests<Blake2bHash>>;
}

impl TransparentDigester for NoTransparent {
fn digest(_: Option<&Self::Bundle>) -> Option<TransparentDigests<Blake2bHash>> {
None
}
}

impl<A: transparent::Authorization> TransparentDigester for Transparent<A> {
fn digest(
transparent_bundle: Option<&Self::Bundle>,
Expand Down Expand Up @@ -272,6 +279,12 @@ trait SaplingDigester: SaplingPart {
fn digest(sapling_bundle: Option<&Self::Bundle>) -> Option<Blake2bHash>;
}

impl SaplingDigester for NoSapling {
fn digest(_: Option<&Self::Bundle>) -> Option<Blake2bHash> {
None
}
}

impl<A: sapling::bundle::Authorization> SaplingDigester for Sapling<A> {
fn digest(sapling_bundle: Option<&Self::Bundle>) -> Option<Blake2bHash> {
sapling_bundle.map(hash_sapling_txid_data)
Expand Down Expand Up @@ -303,6 +316,12 @@ trait OrchardDigester: OrchardPart {
fn digest(orchard_bundle: Option<&Self::Bundle>) -> Option<Blake2bHash>;
}

impl OrchardDigester for NoOrchard {
fn digest(_: Option<&Self::Bundle>) -> Option<Blake2bHash> {
None
}
}

impl<A: orchard::Authorization> OrchardDigester for Orchard<A> {
fn digest(orchard_bundle: Option<&Self::Bundle>) -> Option<Blake2bHash> {
orchard_bundle.map(|b| b.commitment().0)
Expand Down

0 comments on commit eeeacc8

Please sign in to comment.