@@ -9,7 +9,8 @@ use miniscript::bitcoin::{Transaction, VarInt};
99use std:: io:: Read ;
1010
1111pub use crate :: zcash:: transaction:: {
12- decode_zcash_transaction_meta, ZcashTransactionMeta , ZCASH_SAPLING_VERSION_GROUP_ID ,
12+ decode_zcash_transaction_meta, ZcashTransactionMeta , ZCASH_NU5_VERSION_GROUP_ID ,
13+ ZCASH_SAPLING_VERSION_GROUP_ID ,
1314} ;
1415
1516/// A Zcash-compatible PSBT that can handle overwintered transactions
@@ -135,15 +136,27 @@ impl ZcashBitGoPsbt {
135136 & self ,
136137 tx : & Transaction ,
137138 ) -> Result < Vec < u8 > , super :: DeserializeError > {
139+ let vgid = self
140+ . version_group_id
141+ . unwrap_or ( ZCASH_SAPLING_VERSION_GROUP_ID ) ;
142+ let consensus_branch_id = if vgid == ZCASH_NU5_VERSION_GROUP_ID {
143+ Some (
144+ super :: propkv:: get_zec_consensus_branch_id ( & self . psbt ) . ok_or_else ( || {
145+ super :: DeserializeError :: Network (
146+ "v5 transaction requires consensus_branch_id" . to_string ( ) ,
147+ )
148+ } ) ?,
149+ )
150+ } else {
151+ None
152+ } ;
138153 let parts = crate :: zcash:: transaction:: ZcashTransactionParts {
139154 transaction : tx. clone ( ) ,
140155 is_overwintered : true ,
141- version_group_id : Some (
142- self . version_group_id
143- . unwrap_or ( ZCASH_SAPLING_VERSION_GROUP_ID ) ,
144- ) ,
156+ version_group_id : Some ( vgid) ,
145157 expiry_height : Some ( self . expiry_height . unwrap_or ( 0 ) ) ,
146158 sapling_fields : self . sapling_fields . clone ( ) ,
159+ consensus_branch_id,
147160 } ;
148161 crate :: zcash:: transaction:: encode_zcash_transaction_parts ( & parts)
149162 . map_err ( super :: DeserializeError :: Network )
@@ -180,6 +193,7 @@ impl ZcashBitGoPsbt {
180193 . unwrap_or ( ZCASH_SAPLING_VERSION_GROUP_ID ) ;
181194 let expiry_height = self . expiry_height . unwrap_or ( 0 ) ;
182195 let sapling_fields = self . sapling_fields ;
196+ let stored_branch_id = super :: propkv:: get_zec_consensus_branch_id ( & self . psbt ) ;
183197
184198 let map_extract_err = |e : ExtractTxError | match e {
185199 ExtractTxError :: AbsurdFeeRate { .. } => {
@@ -203,21 +217,38 @@ impl ZcashBitGoPsbt {
203217 . map_err ( map_extract_err) ?,
204218 } ;
205219
220+ let consensus_branch_id = if version_group_id == ZCASH_NU5_VERSION_GROUP_ID {
221+ Some ( stored_branch_id. ok_or_else ( || {
222+ super :: DeserializeError :: Network (
223+ "v5 transaction requires consensus_branch_id" . to_string ( ) ,
224+ )
225+ } ) ?)
226+ } else {
227+ None
228+ } ;
206229 let parts = crate :: zcash:: transaction:: ZcashTransactionParts {
207230 transaction : tx,
208231 is_overwintered : true ,
209232 version_group_id : Some ( version_group_id) ,
210233 expiry_height : Some ( expiry_height) ,
211234 sapling_fields,
235+ consensus_branch_id,
212236 } ;
213237 crate :: zcash:: transaction:: encode_zcash_transaction_parts ( & parts)
214238 . map_err ( super :: DeserializeError :: Network )
215239 }
216240
217- /// Compute the transaction ID for the unsigned Zcash transaction
241+ /// Compute the transaction ID for the unsigned Zcash transaction.
218242 ///
219- /// The txid is the double SHA256 of the full Zcash transaction bytes.
243+ /// v4 (Sapling): double SHA-256 of the serialized transaction bytes.
244+ /// v5 (NU5/ZIP-225): ZIP-244 recursive BLAKE2b hash tree — not yet implemented.
220245 pub fn compute_txid ( & self ) -> Result < [ u8 ; 32 ] , super :: DeserializeError > {
246+ if self . version_group_id == Some ( ZCASH_NU5_VERSION_GROUP_ID ) {
247+ return Err ( super :: DeserializeError :: Network (
248+ "compute_txid for v5 transactions requires ZIP-244, which is not yet implemented"
249+ . to_string ( ) ,
250+ ) ) ;
251+ }
221252 use miniscript:: bitcoin:: hashes:: { sha256d, Hash } ;
222253 let tx_bytes = self . extract_unsigned_zcash_transaction ( ) ?;
223254 let hash = sha256d:: Hash :: hash ( & tx_bytes) ;
0 commit comments