From a3a61f5d9401c7ec4d7734b4665fb0887afbb1f8 Mon Sep 17 00:00:00 2001 From: Dmitry Demin Date: Wed, 28 Aug 2024 15:20:23 +0200 Subject: [PATCH 1/3] Add initial Transaction V6 support in zebra-chain crate. This commit introduces basic support for Transaction version 6 (Tx V6). This initial implementation treats Tx V6 as a simple copy of Tx V5, without yet integrating ZSA-specific features or the new transaction structure. - Added a new V6 variant to the Transaction enum in the zebra-chain crate. - Updated relevant code to handle the new V6 variant. Note: Tests and additional adjustments are still pending, and will be addressed in subsequent commits. --- zebra-chain/src/transaction.rs | 167 +++++++++++++++++++++++++++++---- 1 file changed, 148 insertions(+), 19 deletions(-) diff --git a/zebra-chain/src/transaction.rs b/zebra-chain/src/transaction.rs index 3df3edc8d53..ada329140f1 100644 --- a/zebra-chain/src/transaction.rs +++ b/zebra-chain/src/transaction.rs @@ -142,6 +142,28 @@ pub enum Transaction { /// The orchard data for this transaction, if any. orchard_shielded_data: Option, }, + // FIXME: implement V6 properly (now it's just a coipy of V5) + /// A `version = 6` transaction , which supports Orchard ZSA, Orchard Vanille, Sapling and + /// transparent, but not Sprout. + V6 { + /// The Network Upgrade for this transaction. + /// + /// Derived from the ConsensusBranchId field. + network_upgrade: NetworkUpgrade, + /// The earliest time or block height that this transaction can be added to the + /// chain. + lock_time: LockTime, + /// The latest block height that this transaction can be added to the chain. + expiry_height: block::Height, + /// The transparent inputs to the transaction. + inputs: Vec, + /// The transparent outputs from the transaction. + outputs: Vec, + /// The sapling shielded data for this transaction, if any. + sapling_shielded_data: Option>, + /// The orchard data for this transaction, if any. + orchard_shielded_data: Option, + }, } impl fmt::Display for Transaction { @@ -180,7 +202,7 @@ impl Transaction { /// Compute the hash (mined transaction ID) of this transaction. /// - /// The hash uniquely identifies mined v5 transactions, + /// The hash uniquely identifies mined v5/v6 transactions, /// and all v1-v4 transactions, whether mined or unmined. pub fn hash(&self) -> Hash { Hash::from(self) @@ -247,7 +269,7 @@ impl Transaction { | Transaction::V2 { .. } | Transaction::V3 { .. } | Transaction::V4 { .. } => None, - Transaction::V5 { .. } => Some(AuthDigest::from(self)), + Transaction::V5 { .. } | Transaction::V6 { .. } => Some(AuthDigest::from(self)), } } @@ -320,7 +342,10 @@ impl Transaction { pub fn is_overwintered(&self) -> bool { match self { Transaction::V1 { .. } | Transaction::V2 { .. } => false, - Transaction::V3 { .. } | Transaction::V4 { .. } | Transaction::V5 { .. } => true, + Transaction::V3 { .. } + | Transaction::V4 { .. } + | Transaction::V5 { .. } + | Transaction::V6 { .. } => true, } } @@ -332,6 +357,7 @@ impl Transaction { Transaction::V3 { .. } => 3, Transaction::V4 { .. } => 4, Transaction::V5 { .. } => 5, + Transaction::V6 { .. } => 6, } } @@ -342,7 +368,8 @@ impl Transaction { | Transaction::V2 { lock_time, .. } | Transaction::V3 { lock_time, .. } | Transaction::V4 { lock_time, .. } - | Transaction::V5 { lock_time, .. } => *lock_time, + | Transaction::V5 { lock_time, .. } + | Transaction::V6 { lock_time, .. } => *lock_time, }; // `zcashd` checks that the block height is greater than the lock height. @@ -389,7 +416,8 @@ impl Transaction { | Transaction::V2 { lock_time, .. } | Transaction::V3 { lock_time, .. } | Transaction::V4 { lock_time, .. } - | Transaction::V5 { lock_time, .. } => *lock_time, + | Transaction::V5 { lock_time, .. } + | Transaction::V6 { lock_time, .. } => *lock_time, }; let mut lock_time_bytes = Vec::new(); lock_time @@ -419,7 +447,8 @@ impl Transaction { Transaction::V1 { .. } | Transaction::V2 { .. } => None, Transaction::V3 { expiry_height, .. } | Transaction::V4 { expiry_height, .. } - | Transaction::V5 { expiry_height, .. } => match expiry_height { + | Transaction::V5 { expiry_height, .. } + | Transaction::V6 { expiry_height, .. } => match expiry_height { // Consensus rule: // > No limit: To set no limit on transactions (so that they do not expire), nExpiryHeight should be set to 0. // https://zips.z.cash/zip-0203#specification @@ -451,6 +480,10 @@ impl Transaction { | Transaction::V5 { ref mut expiry_height, .. + } + | Transaction::V6 { + ref mut expiry_height, + .. } => expiry_height, } } @@ -467,6 +500,9 @@ impl Transaction { | Transaction::V4 { .. } => None, Transaction::V5 { network_upgrade, .. + } + | Transaction::V6 { + network_upgrade, .. } => Some(*network_upgrade), } } @@ -481,6 +517,7 @@ impl Transaction { Transaction::V3 { ref inputs, .. } => inputs, Transaction::V4 { ref inputs, .. } => inputs, Transaction::V5 { ref inputs, .. } => inputs, + Transaction::V6 { ref inputs, .. } => inputs, } } @@ -493,6 +530,7 @@ impl Transaction { Transaction::V3 { ref mut inputs, .. } => inputs, Transaction::V4 { ref mut inputs, .. } => inputs, Transaction::V5 { ref mut inputs, .. } => inputs, + Transaction::V6 { ref mut inputs, .. } => inputs, } } @@ -511,6 +549,7 @@ impl Transaction { Transaction::V3 { ref outputs, .. } => outputs, Transaction::V4 { ref outputs, .. } => outputs, Transaction::V5 { ref outputs, .. } => outputs, + Transaction::V6 { ref outputs, .. } => outputs, } } @@ -533,6 +572,9 @@ impl Transaction { Transaction::V5 { ref mut outputs, .. } => outputs, + Transaction::V6 { + ref mut outputs, .. + } => outputs, } } @@ -580,7 +622,8 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | Transaction::V5 { .. } + | Transaction::V6 { .. } => Box::new(std::iter::empty()), } } @@ -615,7 +658,8 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => 0, + | Transaction::V5 { .. } + | Transaction::V6 { .. } => 0, } } @@ -654,7 +698,8 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | Transaction::V5 { .. } + | Transaction::V6 { .. } => Box::new(std::iter::empty()), } } @@ -690,7 +735,8 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => None, + | Transaction::V5 { .. } + | Transaction::V6 { .. } => None, } } @@ -698,7 +744,7 @@ impl Transaction { pub fn has_sprout_joinsplit_data(&self) -> bool { match self { // No JoinSplits - Transaction::V1 { .. } | Transaction::V5 { .. } => false, + Transaction::V1 { .. } | Transaction::V5 { .. } | Transaction::V6 { .. } => false, // JoinSplits-on-BCTV14 Transaction::V2 { joinsplit_data, .. } | Transaction::V3 { joinsplit_data, .. } => { @@ -745,7 +791,8 @@ impl Transaction { .. } | Transaction::V1 { .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | Transaction::V5 { .. } + | Transaction::V6 { .. } => Box::new(std::iter::empty()), } } @@ -767,6 +814,11 @@ impl Transaction { .. } => Box::new(sapling_shielded_data.anchors()), + Transaction::V6 { + sapling_shielded_data: Some(sapling_shielded_data), + .. + } => Box::new(sapling_shielded_data.anchors()), + // No Spends Transaction::V1 { .. } | Transaction::V2 { .. } @@ -778,6 +830,10 @@ impl Transaction { | Transaction::V5 { sapling_shielded_data: None, .. + } + | Transaction::V6 { + sapling_shielded_data: None, + .. } => Box::new(std::iter::empty()), } } @@ -786,8 +842,8 @@ impl Transaction { /// returning `Spend` regardless of the underlying /// transaction version. /// - /// Shared anchors in V5 transactions are copied into each sapling spend. - /// This allows the same code to validate spends from V4 and V5 transactions. + /// Shared anchors in V5/V6 transactions are copied into each sapling spend. + /// This allows the same code to validate spends from V4 and V5/V6 transactions. /// /// # Correctness /// @@ -804,6 +860,10 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.spends_per_anchor()), + Transaction::V6 { + sapling_shielded_data: Some(sapling_shielded_data), + .. + } => Box::new(sapling_shielded_data.spends_per_anchor()), // No Spends Transaction::V1 { .. } @@ -816,6 +876,10 @@ impl Transaction { | Transaction::V5 { sapling_shielded_data: None, .. + } + | Transaction::V6 { + sapling_shielded_data: None, + .. } => Box::new(std::iter::empty()), } } @@ -832,6 +896,10 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.outputs()), + Transaction::V6 { + sapling_shielded_data: Some(sapling_shielded_data), + .. + } => Box::new(sapling_shielded_data.outputs()), // No Outputs Transaction::V1 { .. } @@ -844,6 +912,10 @@ impl Transaction { | Transaction::V5 { sapling_shielded_data: None, .. + } + | Transaction::V6 { + sapling_shielded_data: None, + .. } => Box::new(std::iter::empty()), } } @@ -862,6 +934,10 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.nullifiers()), + Transaction::V6 { + sapling_shielded_data: Some(sapling_shielded_data), + .. + } => Box::new(sapling_shielded_data.nullifiers()), // No Spends Transaction::V1 { .. } @@ -874,6 +950,10 @@ impl Transaction { | Transaction::V5 { sapling_shielded_data: None, .. + } + | Transaction::V6 { + sapling_shielded_data: None, + .. } => Box::new(std::iter::empty()), } } @@ -892,6 +972,10 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.note_commitments()), + Transaction::V6 { + sapling_shielded_data: Some(sapling_shielded_data), + .. + } => Box::new(sapling_shielded_data.note_commitments()), // No Spends Transaction::V1 { .. } @@ -904,6 +988,10 @@ impl Transaction { | Transaction::V5 { sapling_shielded_data: None, .. + } + | Transaction::V6 { + sapling_shielded_data: None, + .. } => Box::new(std::iter::empty()), } } @@ -920,6 +1008,10 @@ impl Transaction { sapling_shielded_data, .. } => sapling_shielded_data.is_some(), + Transaction::V6 { + sapling_shielded_data, + .. + } => sapling_shielded_data.is_some(), } } @@ -935,6 +1027,12 @@ impl Transaction { .. } => orchard_shielded_data.as_ref(), + // FIXME: Support V6/OrchardZSA propetly. + Transaction::V6 { + orchard_shielded_data, + .. + } => orchard_shielded_data.as_ref(), + // No Orchard shielded data Transaction::V1 { .. } | Transaction::V2 { .. } @@ -953,6 +1051,12 @@ impl Transaction { .. } => Some(orchard_shielded_data), + // FIXME: Support V6/OrchardZSA propetly. + Transaction::V6 { + orchard_shielded_data: Some(orchard_shielded_data), + .. + } => Some(orchard_shielded_data), + Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } @@ -960,6 +1064,10 @@ impl Transaction { | Transaction::V5 { orchard_shielded_data: None, .. + } + | Transaction::V6 { + orchard_shielded_data: None, + .. } => None, } } @@ -1086,7 +1194,8 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | Transaction::V5 { .. } + | Transaction::V6 { .. } => Box::new(std::iter::empty()), } } @@ -1135,7 +1244,8 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | Transaction::V5 { .. } + | Transaction::V6 { .. } => Box::new(std::iter::empty()), } } @@ -1182,7 +1292,8 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | Transaction::V5 { .. } + | Transaction::V6 { .. } => Box::new(std::iter::empty()), } } @@ -1231,7 +1342,8 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | Transaction::V5 { .. } + | Transaction::V6 { .. } => Box::new(std::iter::empty()), } } @@ -1272,7 +1384,8 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(iter::empty()), + | Transaction::V5 { .. } + | Transaction::V6 { .. } => Box::new(iter::empty()), }; joinsplit_value_balances.map(ValueBalance::from_sprout_amount) @@ -1314,6 +1427,10 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => sapling_shielded_data.value_balance, + Transaction::V6 { + sapling_shielded_data: Some(sapling_shielded_data), + .. + } => sapling_shielded_data.value_balance, Transaction::V1 { .. } | Transaction::V2 { .. } @@ -1325,6 +1442,10 @@ impl Transaction { | Transaction::V5 { sapling_shielded_data: None, .. + } + | Transaction::V6 { + sapling_shielded_data: None, + .. } => Amount::zero(), }; @@ -1346,6 +1467,10 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => Some(&mut sapling_shielded_data.value_balance), + Transaction::V6 { + sapling_shielded_data: Some(sapling_shielded_data), + .. + } => Some(&mut sapling_shielded_data.value_balance), Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } @@ -1356,6 +1481,10 @@ impl Transaction { | Transaction::V5 { sapling_shielded_data: None, .. + } + | Transaction::V6 { + sapling_shielded_data: None, + .. } => None, } } From 83d981b0a1276eda743740740b0adb14d3c9f768 Mon Sep 17 00:00:00 2001 From: Dmitry Demin Date: Wed, 28 Aug 2024 21:49:08 +0200 Subject: [PATCH 2/3] Add initial Transaction V6 support in other crates and tests (just copy V5 behaviour for now) --- zebra-chain/src/parameters/transaction.rs | 7 ++ .../src/primitives/zcash_primitives.rs | 5 +- zebra-chain/src/transaction/arbitrary.rs | 16 +++ zebra-chain/src/transaction/serialize.rs | 107 +++++++++++++++++- zebra-chain/src/transaction/tests/vectors.rs | 4 + zebra-chain/src/transaction/txid.rs | 10 +- zebra-chain/src/transaction/unmined.rs | 2 +- zebra-consensus/src/transaction.rs | 8 +- zebra-state/src/service/check/utxo.rs | 1 + .../src/service/non_finalized_state/chain.rs | 28 +++++ 10 files changed, 181 insertions(+), 7 deletions(-) diff --git a/zebra-chain/src/parameters/transaction.rs b/zebra-chain/src/parameters/transaction.rs index bab59e794db..77b88abca44 100644 --- a/zebra-chain/src/parameters/transaction.rs +++ b/zebra-chain/src/parameters/transaction.rs @@ -11,3 +11,10 @@ pub const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085; /// Orchard transactions must use transaction version 5 and this version /// group ID. Sapling transactions can use v4 or v5 transactions. pub const TX_V5_VERSION_GROUP_ID: u32 = 0x26A7_270A; + +/// The version group ID for version 6 transactions. +/// +/// Orchard transactions must use transaction version 5 and this version +/// group ID. +// FIXME: use a proper value! +pub const TX_V6_VERSION_GROUP_ID: u32 = 0x26A7_270B; diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index 227742110ed..6fcaf7c38fe 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -148,13 +148,16 @@ impl TryFrom<&Transaction> for zp_tx::Transaction { /// /// # Panics /// - /// If the transaction is not V5. (Currently there is no need for this + /// If the transaction is not V5/V6. (Currently there is no need for this /// conversion for other versions.) #[allow(clippy::unwrap_in_result)] fn try_from(trans: &Transaction) -> Result { let network_upgrade = match trans { Transaction::V5 { network_upgrade, .. + } + | Transaction::V6 { + network_upgrade, .. } => network_upgrade, Transaction::V1 { .. } | Transaction::V2 { .. } diff --git a/zebra-chain/src/transaction/arbitrary.rs b/zebra-chain/src/transaction/arbitrary.rs index cf4aa7a9552..73aac10a23d 100644 --- a/zebra-chain/src/transaction/arbitrary.rs +++ b/zebra-chain/src/transaction/arbitrary.rs @@ -918,6 +918,22 @@ pub fn transaction_to_fake_v5( orchard_shielded_data: None, }, v5 @ V5 { .. } => v5.clone(), + V6 { + inputs, + outputs, + lock_time, + sapling_shielded_data, + orchard_shielded_data, + .. + } => V5 { + network_upgrade: block_nu, + inputs: inputs.clone(), + outputs: outputs.clone(), + lock_time: *lock_time, + expiry_height: height, + sapling_shielded_data: sapling_shielded_data.clone(), + orchard_shielded_data: orchard_shielded_data.clone(), + }, } } diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index ba65dd054b3..f37698d6118 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -11,7 +11,10 @@ use reddsa::{orchard::Binding, orchard::SpendAuth, Signature}; use crate::{ amount, block::MAX_BLOCK_BYTES, - parameters::{OVERWINTER_VERSION_GROUP_ID, SAPLING_VERSION_GROUP_ID, TX_V5_VERSION_GROUP_ID}, + parameters::{ + OVERWINTER_VERSION_GROUP_ID, SAPLING_VERSION_GROUP_ID, TX_V5_VERSION_GROUP_ID, + TX_V6_VERSION_GROUP_ID, + }, primitives::{Halo2Proof, ZkSnarkProof}, serialization::{ zcash_deserialize_external_count, zcash_serialize_empty_list, @@ -673,6 +676,54 @@ impl ZcashSerialize for Transaction { // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`. orchard_shielded_data.zcash_serialize(&mut writer)?; } + + // FIXME: implement a proper serialization for V6 + Transaction::V6 { + network_upgrade, + lock_time, + expiry_height, + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + } => { + // Transaction V6 spec: + // FIXME: specify a proper ref + // https://zips.z.cash/protocol/protocol.pdf#txnencoding + + // Denoted as `nVersionGroupId` in the spec. + writer.write_u32::(TX_V6_VERSION_GROUP_ID)?; + + // Denoted as `nConsensusBranchId` in the spec. + writer.write_u32::(u32::from( + network_upgrade + .branch_id() + .expect("valid transactions must have a network upgrade with a branch id"), + ))?; + + // Denoted as `lock_time` in the spec. + lock_time.zcash_serialize(&mut writer)?; + + // Denoted as `nExpiryHeight` in the spec. + writer.write_u32::(expiry_height.0)?; + + // Denoted as `tx_in_count` and `tx_in` in the spec. + inputs.zcash_serialize(&mut writer)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. + outputs.zcash_serialize(&mut writer)?; + + // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`, + // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`, + // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and + // `bindingSigSapling`. + sapling_shielded_data.zcash_serialize(&mut writer)?; + + // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`, + // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`, + // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`. + orchard_shielded_data.zcash_serialize(&mut writer)?; + } } Ok(()) } @@ -929,6 +980,60 @@ impl ZcashDeserialize for Transaction { orchard_shielded_data, }) } + // FIXME: implement a proper deserialization for V6 + (6, true) => { + // Transaction V6 spec: + // FIXME: specify a proper ref + // https://zips.z.cash/protocol/protocol.pdf#txnencoding + + // Denoted as `nVersionGroupId` in the spec. + let id = limited_reader.read_u32::()?; + if id != TX_V6_VERSION_GROUP_ID { + return Err(SerializationError::Parse("expected TX_V6_VERSION_GROUP_ID")); + } + // Denoted as `nConsensusBranchId` in the spec. + // Convert it to a NetworkUpgrade + let network_upgrade = + NetworkUpgrade::from_branch_id(limited_reader.read_u32::()?) + .ok_or_else(|| { + SerializationError::Parse( + "expected a valid network upgrade from the consensus branch id", + ) + })?; + + // Denoted as `lock_time` in the spec. + let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?; + + // Denoted as `nExpiryHeight` in the spec. + let expiry_height = block::Height(limited_reader.read_u32::()?); + + // Denoted as `tx_in_count` and `tx_in` in the spec. + let inputs = Vec::zcash_deserialize(&mut limited_reader)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. + let outputs = Vec::zcash_deserialize(&mut limited_reader)?; + + // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`, + // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`, + // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and + // `bindingSigSapling`. + let sapling_shielded_data = (&mut limited_reader).zcash_deserialize_into()?; + + // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`, + // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`, + // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`. + let orchard_shielded_data = (&mut limited_reader).zcash_deserialize_into()?; + + Ok(Transaction::V6 { + network_upgrade, + lock_time, + expiry_height, + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + }) + } (_, _) => Err(SerializationError::Parse("bad tx header")), } } diff --git a/zebra-chain/src/transaction/tests/vectors.rs b/zebra-chain/src/transaction/tests/vectors.rs index 66d5009ed05..f20bfaef455 100644 --- a/zebra-chain/src/transaction/tests/vectors.rs +++ b/zebra-chain/src/transaction/tests/vectors.rs @@ -991,6 +991,10 @@ fn binding_signatures_for_network(network: Network) { Transaction::V5 { sapling_shielded_data, .. + } + | Transaction::V6 { + sapling_shielded_data, + .. } => { if let Some(sapling_shielded_data) = sapling_shielded_data { let shielded_sighash = diff --git a/zebra-chain/src/transaction/txid.rs b/zebra-chain/src/transaction/txid.rs index f67f6dee58d..eb05fd6edb3 100644 --- a/zebra-chain/src/transaction/txid.rs +++ b/zebra-chain/src/transaction/txid.rs @@ -28,7 +28,7 @@ impl<'a> TxIdBuilder<'a> { | Transaction::V2 { .. } | Transaction::V3 { .. } | Transaction::V4 { .. } => self.txid_v1_to_v4(), - Transaction::V5 { .. } => self.txid_v5(), + Transaction::V5 { .. } | Transaction::V6 { .. } => self.txid_v5_v6(), } } @@ -43,10 +43,14 @@ impl<'a> TxIdBuilder<'a> { Ok(Hash(hash_writer.finish())) } - /// Compute the Transaction ID for a V5 transaction in the given network upgrade. + // FIXME: it looks like the updated zcash_primitives in librustzcash + // auto-detects the transaction version by the first byte, so the same function + // can be used here for both V5 and V6. + // FIXME: fix spec refs below for V6 + /// Compute the Transaction ID for a V5/V6 transaction in the given network upgrade. /// In this case it's the hash of a tree of hashes of specific parts of the /// transaction, as specified in ZIP-244 and ZIP-225. - fn txid_v5(self) -> Result { + fn txid_v5_v6(self) -> Result { // The v5 txid (from ZIP-244) is computed using librustzcash. Convert the zebra // transaction to a librustzcash transaction. let alt_tx: zcash_primitives::transaction::Transaction = self.trans.try_into()?; diff --git a/zebra-chain/src/transaction/unmined.rs b/zebra-chain/src/transaction/unmined.rs index da716573e8b..554e8c3f0e1 100644 --- a/zebra-chain/src/transaction/unmined.rs +++ b/zebra-chain/src/transaction/unmined.rs @@ -140,7 +140,7 @@ impl From<&Transaction> for UnminedTxId { fn from(transaction: &Transaction) -> Self { match transaction { V1 { .. } | V2 { .. } | V3 { .. } | V4 { .. } => Legacy(transaction.into()), - V5 { .. } => Witnessed(transaction.into()), + V5 { .. } | V6 { .. } => Witnessed(transaction.into()), } } } diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index 1c303003615..5c21ccd494c 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -405,7 +405,13 @@ where sapling_shielded_data, orchard_shielded_data, .. - } => Self::verify_v5_transaction( + } + // FIXME: implement proper V6 verification + | Transaction::V6 { + sapling_shielded_data, + orchard_shielded_data, + .. + }=> Self::verify_v5_transaction( &req, &network, script_verifier, diff --git a/zebra-state/src/service/check/utxo.rs b/zebra-state/src/service/check/utxo.rs index 324efa3c035..b856a616dd5 100644 --- a/zebra-state/src/service/check/utxo.rs +++ b/zebra-state/src/service/check/utxo.rs @@ -63,6 +63,7 @@ pub fn transparent_spend( finalized_state, )?; + // FIXME: what about v6? // The state service returns UTXOs from pending blocks, // which can be rejected by later contextual checks. // This is a particular issue for v5 transactions, diff --git a/zebra-state/src/service/non_finalized_state/chain.rs b/zebra-state/src/service/non_finalized_state/chain.rs index e25b1fd171b..233a1dbea63 100644 --- a/zebra-state/src/service/non_finalized_state/chain.rs +++ b/zebra-state/src/service/non_finalized_state/chain.rs @@ -1510,6 +1510,20 @@ impl Chain { sapling_shielded_data, orchard_shielded_data, ), + V6 { + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + .. + } => ( + inputs, + outputs, + &None, + &None, + sapling_shielded_data, + orchard_shielded_data, + ), V1 { .. } | V2 { .. } | V3 { .. } => unreachable!( "older transaction versions only exist in finalized blocks, because of the mandatory canopy checkpoint", ), @@ -1671,6 +1685,20 @@ impl UpdateWith for Chain { sapling_shielded_data, orchard_shielded_data, ), + V6 { + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + .. + } => ( + inputs, + outputs, + &None, + &None, + sapling_shielded_data, + orchard_shielded_data, + ), V1 { .. } | V2 { .. } | V3 { .. } => unreachable!( "older transaction versions only exist in finalized blocks, because of the mandatory canopy checkpoint", ), From c8fa7794502191f6980a3f70b85e10d62a1453b5 Mon Sep 17 00:00:00 2001 From: Dmitry Demin Date: Wed, 28 Aug 2024 23:07:00 +0200 Subject: [PATCH 3/3] Fix compilation errors in tests --- zebra-state/src/tests.rs | 1 + zebrad/src/components/mempool/storage/tests/prop.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/zebra-state/src/tests.rs b/zebra-state/src/tests.rs index 488ab4227bd..6b955480572 100644 --- a/zebra-state/src/tests.rs +++ b/zebra-state/src/tests.rs @@ -34,6 +34,7 @@ impl FakeChainHelper for Arc { Transaction::V3 { inputs, .. } => &mut inputs[0], Transaction::V4 { inputs, .. } => &mut inputs[0], Transaction::V5 { inputs, .. } => &mut inputs[0], + Transaction::V6 { inputs, .. } => &mut inputs[0], }; match input { diff --git a/zebrad/src/components/mempool/storage/tests/prop.rs b/zebrad/src/components/mempool/storage/tests/prop.rs index eca65935acb..98abdb2b8e8 100644 --- a/zebrad/src/components/mempool/storage/tests/prop.rs +++ b/zebrad/src/components/mempool/storage/tests/prop.rs @@ -446,6 +446,7 @@ enum SpendConflictTestInput { conflict: SpendConflictForTransactionV5, }, + // FIXME: add and use V6? } impl SpendConflictTestInput { @@ -567,7 +568,7 @@ impl SpendConflictTestInput { } // No JoinSplits - Transaction::V1 { .. } | Transaction::V5 { .. } => {} + Transaction::V1 { .. } | Transaction::V5 { .. } | Transaction::V6 { .. } => {} } } } @@ -634,6 +635,10 @@ impl SpendConflictTestInput { Transaction::V5 { sapling_shielded_data, .. + } + | Transaction::V6 { + sapling_shielded_data, + .. } => { Self::remove_sapling_transfers_with_conflicts(sapling_shielded_data, &conflicts) } @@ -707,6 +712,10 @@ impl SpendConflictTestInput { Transaction::V5 { orchard_shielded_data, .. + } + | Transaction::V6 { + orchard_shielded_data, + .. } => Self::remove_orchard_actions_with_conflicts(orchard_shielded_data, &conflicts), // No Spends