@@ -17,7 +17,9 @@ use namada_core::address::{self, Address};
1717use namada_core:: arith:: { CheckedAdd , CheckedSub , checked} ;
1818use namada_core:: booleans:: BoolResultUnitExt ;
1919use namada_core:: collections:: HashSet ;
20- use namada_core:: masp:: { MaspEpoch , TAddrData , addr_taddr, encode_asset_type} ;
20+ use namada_core:: masp:: {
21+ FlagCiphertext , MaspEpoch , TAddrData , addr_taddr, encode_asset_type,
22+ } ;
2123use namada_core:: storage:: Key ;
2224use namada_core:: token;
2325use namada_core:: token:: { Amount , MaspDigitPos } ;
@@ -435,12 +437,13 @@ where
435437 . data ( batched_tx. cmt )
436438 . ok_or_err_msg ( "No transaction data" ) ?;
437439 let actions = ctx. read_actions ( ) ?;
438- // Try to get the Transaction object from the tx first (IBC) and from
439- // the actions afterwards
440- let shielded_tx = if let Some ( tx) =
441- Ibc :: try_extract_masp_tx_from_envelope :: < Transfer > ( & tx_data) ?
440+
441+ // Try to get the Transaction object and FMD flag ciphertexts
442+ // from the tx first (IBC) and from the actions afterwards
443+ let ( shielded_tx, fmd_flags) = if let Some ( shielding_data) =
444+ Ibc :: try_extract_shielding_data_from_envelope :: < Transfer > ( & tx_data) ?
442445 {
443- tx
446+ shielding_data
444447 } else {
445448 let masp_section_ref =
446449 namada_tx:: action:: get_masp_section_ref ( & actions)
@@ -450,14 +453,33 @@ where
450453 "Missing MASP section reference in action" ,
451454 )
452455 } ) ?;
456+ let flag_ciphertexts_ref =
457+ namada_tx:: action:: get_fmd_flag_ciphertexts_ref ( & actions)
458+ . map_err ( Error :: new_const) ?
459+ . ok_or_else ( || {
460+ Error :: new_const (
461+ "Missing FMD flag ciphertexts reference in action" ,
462+ )
463+ } ) ?;
453464
454- batched_tx
465+ let masp_tx = batched_tx
455466 . tx
456467 . get_masp_section ( & masp_section_ref)
457468 . cloned ( )
458469 . ok_or_else ( || {
459470 Error :: new_const ( "Missing MASP section in transaction" )
460- } ) ?
471+ } ) ?;
472+ let fmd_flags = batched_tx
473+ . tx
474+ . get_fmd_flag_ciphertexts ( & flag_ciphertexts_ref)
475+ . map_err ( Error :: new) ?
476+ . ok_or_else ( || {
477+ Error :: new_const (
478+ "Missing FMD flag ciphertexts in transaction" ,
479+ )
480+ } ) ?;
481+
482+ ( masp_tx, fmd_flags)
461483 } ;
462484
463485 if u64:: from ( ctx. get_block_height ( ) ?)
@@ -468,6 +490,8 @@ where
468490 return Err ( error) ;
469491 }
470492
493+ validate_flag_ciphertexts ( & shielded_tx, fmd_flags) ?;
494+
471495 // Check the validity of the keys and get the transfer data
472496 let changed_balances = Self :: validate_state_and_get_transfer_data (
473497 ctx,
@@ -949,6 +973,39 @@ fn verify_sapling_balancing_value(
949973 }
950974}
951975
976+ /// Check if the flag ciphertexts included in the tx are valid.
977+ fn validate_flag_ciphertexts (
978+ masp_tx : & Transaction ,
979+ fmd_flags : Vec < FlagCiphertext > ,
980+ ) -> Result < ( ) > {
981+ let shielded_outputs_len = masp_tx
982+ . sapling_bundle ( )
983+ . map_or ( 0 , |bundle| bundle. shielded_outputs . len ( ) ) ;
984+
985+ if shielded_outputs_len != fmd_flags. len ( ) {
986+ let error = Error :: new ( format ! (
987+ "The number of shielded outputs in the MASP tx ({}) does not \
988+ match the number of FMD flag ciphertexts ({})",
989+ shielded_outputs_len,
990+ fmd_flags. len( )
991+ ) ) ;
992+ tracing:: debug!( "{error}" ) ;
993+ return Err ( error) ;
994+ }
995+
996+ fmd_flags
997+ . iter ( )
998+ . all ( FlagCiphertext :: is_valid)
999+ . ok_or_else ( || {
1000+ let error = Error :: new_const (
1001+ "Not all FMD flag ciphertexts in the MASP tx were considered \
1002+ valid, either because of invalid gamma or tampered bits",
1003+ ) ;
1004+ tracing:: debug!( "{error}" ) ;
1005+ error
1006+ } )
1007+ }
1008+
9521009#[ cfg( test) ]
9531010mod shielded_token_tests {
9541011 use std:: cell:: RefCell ;
0 commit comments