@@ -30,6 +30,18 @@ lazy_static! {
3030 sapling_shielded_data: None ,
3131 orchard_shielded_data: None ,
3232 } ;
33+
34+ #[ cfg( feature = "tx-v6" ) ]
35+ pub static ref EMPTY_V6_TX : Transaction = Transaction :: V6 {
36+ network_upgrade: NetworkUpgrade :: Nu7 ,
37+ lock_time: LockTime :: min_lock_time_timestamp( ) ,
38+ expiry_height: block:: Height ( 0 ) ,
39+ inputs: Vec :: new( ) ,
40+ outputs: Vec :: new( ) ,
41+ sapling_shielded_data: None ,
42+ orchard_shielded_data: None ,
43+ orchard_zsa_issue_data: None
44+ } ;
3345}
3446
3547/// Build a mock output list for pre-V5 transactions, with (index+1)
@@ -257,18 +269,9 @@ fn deserialize_large_transaction() {
257269 . expect_err ( "transaction should not deserialize due to its size" ) ;
258270}
259271
260- // Transaction V5 test vectors
261-
262- /// An empty transaction v5, with no Orchard, Sapling, or Transparent data
263- ///
264- /// empty transaction are invalid, but Zebra only checks this rule in
265- /// zebra_consensus::transaction::Verifier
266- #[ test]
267- fn empty_v5_round_trip ( ) {
272+ fn tx_round_trip ( tx : & Transaction ) {
268273 let _init_guard = zebra_test:: init ( ) ;
269274
270- let tx: & Transaction = & EMPTY_V5_TX ;
271-
272275 let data = tx. zcash_serialize_to_vec ( ) . expect ( "tx should serialize" ) ;
273276 let tx2: & Transaction = & data
274277 . zcash_deserialize_into ( )
@@ -314,18 +317,47 @@ fn empty_v4_round_trip() {
314317 assert_eq ! ( data, data2, "data must be equal if structs are equal" ) ;
315318}
316319
317- /// Check if an empty V5 transaction can be deserialized by librustzcash too.
320+ /// An empty transaction v5, with no Orchard, Sapling, or Transparent data
321+ ///
322+ /// empty transaction are invalid, but Zebra only checks this rule in
323+ /// zebra_consensus::transaction::Verifier
318324#[ test]
319- fn empty_v5_librustzcash_round_trip ( ) {
325+ fn empty_v5_round_trip ( ) {
326+ tx_round_trip ( & EMPTY_V5_TX )
327+ }
328+
329+ #[ cfg( feature = "tx-v6" ) ]
330+ /// An empty transaction v6, with no Orchard/OrchardZSA, Sapling, or Transparent data
331+ ///
332+ /// empty transaction are invalid, but Zebra only checks this rule in
333+ /// zebra_consensus::transaction::Verifier
334+ #[ test]
335+ fn empty_v6_round_trip ( ) {
336+ tx_round_trip ( & EMPTY_V6_TX )
337+ }
338+
339+ fn tx_librustzcash_round_trip ( tx : & Transaction ) {
320340 let _init_guard = zebra_test:: init ( ) ;
321341
322- let tx: & Transaction = & EMPTY_V5_TX ;
323342 let _alt_tx: zcash_primitives:: transaction:: Transaction = tx. try_into ( ) . expect (
324343 "librustzcash deserialization might work for empty zebra serialized transactions. \
325344 Hint: if empty transactions fail, but other transactions work, delete this test",
326345 ) ;
327346}
328347
348+ /// Check if an empty V5 transaction can be deserialized by librustzcash too.
349+ #[ test]
350+ fn empty_v5_librustzcash_round_trip ( ) {
351+ tx_librustzcash_round_trip ( & EMPTY_V5_TX ) ;
352+ }
353+
354+ #[ cfg( feature = "tx-v6" ) ]
355+ /// Check if an empty V6 transaction can be deserialized by librustzcash too.
356+ #[ test]
357+ fn empty_v6_librustzcash_round_trip ( ) {
358+ tx_librustzcash_round_trip ( & EMPTY_V6_TX ) ;
359+ }
360+
329361/// Do a round-trip test on fake v5 transactions created from v4 transactions
330362/// in the block test vectors.
331363///
@@ -450,6 +482,54 @@ fn fake_v5_round_trip_for_network(network: Network) {
450482 }
451483}
452484
485+ #[ cfg( feature = "tx-v6" ) ]
486+ /// Do a serialization round-trip on OrchardZSA workflow blocks and their V6
487+ /// transactions.
488+ #[ test]
489+ fn v6_round_trip ( ) {
490+ use zebra_test:: vectors:: ORCHARD_ZSA_WORKFLOW_BLOCKS ;
491+
492+ let _init_guard = zebra_test:: init ( ) ;
493+
494+ for block_bytes in ORCHARD_ZSA_WORKFLOW_BLOCKS . iter ( ) {
495+ let block = block_bytes
496+ . zcash_deserialize_into :: < Block > ( )
497+ . expect ( "block is structurally valid" ) ;
498+
499+ // test full blocks
500+ let block_bytes2 = block
501+ . zcash_serialize_to_vec ( )
502+ . expect ( "vec serialization is infallible" ) ;
503+
504+ assert_eq ! (
505+ block_bytes, & block_bytes2,
506+ "data must be equal if structs are equal"
507+ ) ;
508+
509+ // test each transaction
510+ for tx in & block. transactions {
511+ let tx_bytes = tx
512+ . zcash_serialize_to_vec ( )
513+ . expect ( "vec serialization is infallible" ) ;
514+
515+ let tx2 = tx_bytes
516+ . zcash_deserialize_into :: < Transaction > ( )
517+ . expect ( "tx is structurally valid" ) ;
518+
519+ assert_eq ! ( tx. as_ref( ) , & tx2) ;
520+
521+ let tx_bytes2 = tx2
522+ . zcash_serialize_to_vec ( )
523+ . expect ( "vec serialization is infallible" ) ;
524+
525+ assert_eq ! (
526+ tx_bytes, tx_bytes2,
527+ "data must be equal if structs are equal"
528+ ) ;
529+ }
530+ }
531+ }
532+
453533#[ test]
454534fn invalid_orchard_nullifier ( ) {
455535 let _init_guard = zebra_test:: init ( ) ;
@@ -549,6 +629,34 @@ fn fake_v5_librustzcash_round_trip_for_network(network: Network) {
549629 }
550630}
551631
632+ #[ cfg( feature = "tx-v6" ) ]
633+ /// Confirms each V6 transaction in the OrchardZSA test blocks converts to librustzcash’s
634+ /// transaction type without error.
635+ #[ test]
636+ fn v6_librustzcash_tx_conversion ( ) {
637+ use zebra_test:: vectors:: ORCHARD_ZSA_WORKFLOW_BLOCKS ;
638+
639+ let _init_guard = zebra_test:: init ( ) ;
640+
641+ for block_bytes in ORCHARD_ZSA_WORKFLOW_BLOCKS . iter ( ) {
642+ let block = block_bytes
643+ . zcash_deserialize_into :: < Block > ( )
644+ . expect ( "block is structurally valid" ) ;
645+
646+ // Test each V6 transaction
647+ for tx in block
648+ . transactions
649+ . iter ( )
650+ . filter ( |tx| matches ! ( tx. as_ref( ) , & Transaction :: V6 { .. } ) )
651+ {
652+ let _alt_tx: zcash_primitives:: transaction:: Transaction = tx
653+ . as_ref ( )
654+ . try_into ( )
655+ . expect ( "librustzcash conversion must work for zebra transactions" ) ;
656+ }
657+ }
658+ }
659+
552660#[ test]
553661fn zip244_round_trip ( ) -> Result < ( ) > {
554662 let _init_guard = zebra_test:: init ( ) ;
0 commit comments