diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 9fff7e3a3f..2394cc5686 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -340,7 +340,7 @@ mod tests { consensus::{ self, BlockHeight, BranchId, Network, NetworkConstants, NetworkUpgrade, Parameters, }, - transaction::{TransactionData, TxVersion}, + transaction::{Transaction, TransactionData, TxVersion}, zip32::AccountId, }; @@ -1266,7 +1266,7 @@ mod tests { [], )?; - let tx = TransactionData::from_parts( + let tx: Transaction = TransactionData::from_parts( TxVersion::Sapling, BranchId::Canopy, 0, diff --git a/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs b/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs index a84f99a05b..08174aa0f5 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs @@ -295,7 +295,7 @@ mod tests { transparent::{self, Authorized, OutPoint}, Amount, TxIn, TxOut, }, - TransactionData, TxVersion, + Transaction, TransactionData, TxVersion, }, }, }; @@ -413,7 +413,7 @@ mod tests { .unwrap(); // create a UTXO to spend - let tx = TransactionData::from_parts( + let tx: Transaction = TransactionData::from_parts( TxVersion::Sapling, BranchId::Canopy, 0, diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index 8b8eed400f..4aa4a87f25 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -225,9 +225,7 @@ impl OrchardPart for Orchard { type Authorization = A; } -pub(super) trait TransparentEnc: - TransparentPart -{ +pub trait TransparentEnc: TransparentPart { fn read(reader: R) -> io::Result>; fn write(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()>; @@ -256,7 +254,7 @@ impl TransparentEnc for NoTransparent { } } -pub(super) trait SproutEnc: SproutPart { +pub trait SproutEnc: SproutPart { fn read_v4_bundle( reader: R, tx_has_sprout: bool, @@ -302,9 +300,7 @@ impl SproutEnc for NoSprout { } } -pub(super) trait SaplingEnc: - SaplingPart -{ +pub trait SaplingEnc: SaplingPart { type V4Components; fn read_v4_components( @@ -424,9 +420,7 @@ impl SaplingEnc for NoSapling { } } -pub(super) trait OrchardEnc: - OrchardPart -{ +pub trait OrchardEnc: OrchardPart { fn read_v5_bundle(reader: R) -> io::Result>; fn write_v5_bundle(bundle: Option<&Self::Bundle>, writer: W) -> io::Result<()>; diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 7231fb7369..b600dac36b 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -299,22 +299,69 @@ impl Authorization for Unauthorized { } /// A Zcash transaction. -#[derive(Debug)] -pub struct Transaction { +pub type Transaction = TransactionWith>; + +/// An authorized Zcash transaction with configurable support in the type system for the +/// various transparent and shielded pools. +/// +/// If a particular pool is disabled (by using e.g. [`components::NoTransparent`]): +/// - The pool-specific APIs will be inaccessible. +/// - The serialization APIs will encode an empty bundle for that pool. +/// - The parsing APIs will return an error when parsing a transaction with spends or +/// outputs in that pool. +pub struct TransactionWith +where + B: Bundles, + B::Transparent: TransparentPart, + B::Sapling: SaplingPart, + B::Orchard: OrchardPart, +{ txid: TxId, - data: TransactionData>, + data: TransactionData, } -impl Deref for Transaction { - type Target = TransactionData>; +impl fmt::Debug for TransactionWith +where + B: Bundles + fmt::Debug, + B::Transparent: TransparentPart + fmt::Debug, + B::Sprout: SproutPart + fmt::Debug, + B::Sapling: SaplingPart + fmt::Debug, + B::Orchard: OrchardPart + fmt::Debug, + ::Bundle: fmt::Debug, + ::Bundle: fmt::Debug, + ::Bundle: fmt::Debug, + ::Bundle: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TransactionWith") + .field("txid", &self.txid) + .field("data", &self.data) + .finish() + } +} + +impl Deref for TransactionWith +where + B: Bundles, + B::Transparent: TransparentPart, + B::Sapling: SaplingPart, + B::Orchard: OrchardPart, +{ + type Target = TransactionData; - fn deref(&self) -> &TransactionData> { + fn deref(&self) -> &TransactionData { &self.data } } -impl PartialEq for Transaction { - fn eq(&self, other: &Transaction) -> bool { +impl PartialEq for TransactionWith +where + B: Bundles, + B::Transparent: TransparentPart, + B::Sapling: SaplingPart, + B::Orchard: OrchardPart, +{ + fn eq(&self, other: &Self) -> bool { self.txid == other.txid } } @@ -596,14 +643,28 @@ impl>> Tran } } -impl TransactionData> { - pub fn freeze(self) -> io::Result { - Transaction::from_data(self) +impl TransactionData +where + B: Bundles, + B::Transparent: TransparentEnc + txid::TransparentDigester, + B::Sprout: SproutEnc, + B::Sapling: SaplingEnc + txid::SaplingDigester, + B::Orchard: OrchardEnc + txid::OrchardDigester, +{ + pub fn freeze(self) -> io::Result> { + TransactionWith::from_data(self) } } -impl Transaction { - fn from_data(data: TransactionData>) -> io::Result { +impl TransactionWith +where + B: Bundles, + B::Transparent: TransparentEnc + txid::TransparentDigester, + B::Sprout: SproutEnc, + B::Sapling: SaplingEnc + txid::SaplingDigester, + B::Orchard: OrchardEnc + txid::OrchardDigester, +{ + fn from_data(data: TransactionData) -> io::Result { match data.version { TxVersion::Sprout(_) | TxVersion::Overwinter | TxVersion::Sapling => { Self::from_data_v4(data) @@ -614,8 +675,8 @@ impl Transaction { } } - fn from_data_v4(data: TransactionData>) -> io::Result { - let mut tx = Transaction { + fn from_data_v4(data: TransactionData) -> io::Result { + let mut tx = TransactionWith { txid: TxId([0; 32]), data, }; @@ -625,17 +686,17 @@ impl Transaction { Ok(tx) } - fn from_data_v5(data: TransactionData>) -> Self { + fn from_data_v5(data: TransactionData) -> Self { let txid = to_txid( data.version, data.consensus_branch_id, &data.digest(TxIdDigester), ); - Transaction { txid, data } + TransactionWith { txid, data } } - pub fn into_data(self) -> TransactionData> { + pub fn into_data(self) -> TransactionData { self.data } @@ -663,7 +724,7 @@ impl Transaction { version: TxVersion, consensus_branch_id: BranchId, ) -> io::Result { - let transparent_bundle = Transparent::read(&mut reader)?; + let transparent_bundle = B::Transparent::read(&mut reader)?; let lock_time = reader.read_u32::()?; let expiry_height: BlockHeight = if version.has_overwinter() { @@ -672,19 +733,19 @@ impl Transaction { 0u32.into() }; - let components = Sapling::read_v4_components(&mut reader, version.has_sapling())?; + let components = B::Sapling::read_v4_components(&mut reader, version.has_sapling())?; let sprout_bundle = - Sprout::read_v4_bundle(&mut reader, version.has_sprout(), version.has_sapling())?; + B::Sprout::read_v4_bundle(&mut reader, version.has_sprout(), version.has_sapling())?; let sapling_bundle = - Sapling::read_v4_binding_sig(&mut reader, version.has_sapling(), components)?; + B::Sapling::read_v4_binding_sig(&mut reader, version.has_sapling(), components)?; let mut txid = [0; 32]; let hash_bytes = reader.into_hash(); txid.copy_from_slice(&hash_bytes); - Ok(Transaction { + Ok(TransactionWith { txid: TxId(txid), data: TransactionData { version, @@ -711,9 +772,9 @@ impl Transaction { fn read_v5(mut reader: R, version: TxVersion) -> io::Result { let (consensus_branch_id, lock_time, expiry_height) = Self::read_v5_header_fragment(&mut reader)?; - let transparent_bundle = Transparent::read(&mut reader)?; - let sapling_bundle = Sapling::read_v5_bundle(&mut reader)?; - let orchard_bundle = Orchard::read_v5_bundle(&mut reader)?; + let transparent_bundle = B::Transparent::read(&mut reader)?; + let sapling_bundle = B::Sapling::read_v5_bundle(&mut reader)?; + let orchard_bundle = B::Orchard::read_v5_bundle(&mut reader)?; #[cfg(zcash_unstable = "zfuture")] let tze_bundle = if version.has_tze() { @@ -794,19 +855,19 @@ impl Transaction { writer.write_u32::(u32::from(self.expiry_height))?; } - Sapling::write_v4_components( + B::Sapling::write_v4_components( self.sapling_bundle.as_ref(), &mut writer, self.version.has_sapling(), )?; - Sprout::write_v4_bundle( + B::Sprout::write_v4_bundle( self.sprout_bundle.as_ref(), &mut writer, self.version.has_sprout(), )?; - Sapling::write_v4_binding_sig( + B::Sapling::write_v4_binding_sig( self.sapling_bundle.as_ref(), &mut writer, self.version.has_sapling(), @@ -823,7 +884,7 @@ impl Transaction { } pub fn write_transparent(&self, writer: W) -> io::Result<()> { - Transparent::write(self.transparent_bundle.as_ref(), writer) + B::Transparent::write(self.transparent_bundle.as_ref(), writer) } pub fn write_v5(&self, mut writer: W) -> io::Result<()> { @@ -836,7 +897,7 @@ impl Transaction { self.write_v5_header(&mut writer)?; self.write_transparent(&mut writer)?; self.write_v5_sapling(&mut writer)?; - Orchard::write_v5_bundle(self.orchard_bundle.as_ref(), &mut writer)?; + B::Orchard::write_v5_bundle(self.orchard_bundle.as_ref(), &mut writer)?; #[cfg(zcash_unstable = "zfuture")] self.write_tze(&mut writer)?; Ok(()) @@ -859,7 +920,7 @@ impl Transaction { } pub fn write_v5_sapling(&self, writer: W) -> io::Result<()> { - Sapling::write_v5_bundle(self.sapling_bundle.as_ref(), writer) + B::Sapling::write_v5_bundle(self.sapling_bundle.as_ref(), writer) } #[cfg(zcash_unstable = "zfuture")] @@ -874,7 +935,15 @@ impl Transaction { Ok(()) } +} +impl TransactionWith +where + B: Bundles, + B::Transparent: txid::TransparentWitnessDigester, + B::Sapling: txid::SaplingWitnessDigester, + B::Orchard: txid::OrchardWitnessDigester, +{ // TODO: should this be moved to `from_data` and stored? pub fn auth_commitment(&self) -> Blake2bHash { self.data.digest(BlockTxCommitmentDigester) diff --git a/zcash_primitives/src/transaction/txid.rs b/zcash_primitives/src/transaction/txid.rs index d9c1d0b6fe..cb35e9bd62 100644 --- a/zcash_primitives/src/transaction/txid.rs +++ b/zcash_primitives/src/transaction/txid.rs @@ -200,7 +200,7 @@ pub(crate) fn hash_sapling_outputs(shielded_outputs: &[OutputDescription]) h.finalize() } -trait TransparentDigester: TransparentPart { +pub trait TransparentDigester: TransparentPart { fn digest(transparent_bundle: Option<&Self::Bundle>) -> Option>; } @@ -275,7 +275,7 @@ pub(crate) fn hash_transparent_txid_data( h.finalize() } -trait SaplingDigester: SaplingPart { +pub trait SaplingDigester: SaplingPart { fn digest(sapling_bundle: Option<&Self::Bundle>) -> Option; } @@ -312,7 +312,7 @@ fn hash_sapling_txid_empty() -> Blake2bHash { hasher(ZCASH_SAPLING_HASH_PERSONALIZATION).finalize() } -trait OrchardDigester: OrchardPart { +pub trait OrchardDigester: OrchardPart { fn digest(orchard_bundle: Option<&Self::Bundle>) -> Option; } @@ -481,7 +481,9 @@ pub fn to_txid( TxId(<[u8; 32]>::try_from(txid_digest.as_bytes()).unwrap()) } -trait TransparentWitnessDigester: TransparentPart { +pub trait TransparentWitnessDigester: + TransparentPart +{ fn witness_digest(transparent_bundle: Option<&Self::Bundle>) -> Blake2bHash; } @@ -497,7 +499,7 @@ impl TransparentWitnessDigester for Transparent { } } -trait SaplingWitnessDigester: SaplingPart { +pub trait SaplingWitnessDigester: SaplingPart { fn witness_digest(sapling_bundle: Option<&Self::Bundle>) -> Blake2bHash; } @@ -525,7 +527,7 @@ impl SaplingWitnessDigester for Sapling { } } -trait OrchardWitnessDigester: OrchardPart { +pub trait OrchardWitnessDigester: OrchardPart { fn witness_digest(orchard_bundle: Option<&Self::Bundle>) -> Blake2bHash; }