@@ -436,18 +436,20 @@ impl Compact for EvTxType {
436436
437437 /// Decodes `EvTxType` from compact format.
438438 ///
439+ /// Static files store EIP-7702 as `0x04`, not `0x76`.
440+ ///
439441 /// # Panics
440- /// Panics if an unknown transaction type identifier is encountered. This indicates
441- /// database corruption or a version mismatch - the node should not continue .
442+ ///
443+ /// Panics on an unknown extended identifier or a truncated buffer .
442444 fn from_compact ( mut buf : & [ u8 ] , identifier : usize ) -> ( Self , & [ u8 ] ) {
443445 match identifier {
444446 COMPACT_EXTENDED_IDENTIFIER_FLAG => {
445- let extended_identifier = buf . get_u8 ( ) ;
446- match extended_identifier {
447- EVNODE_TX_TYPE_ID => ( Self :: EvNode , buf) ,
448- _ => panic ! (
449- "failed to decode EvTxType from database: unknown identifier {extended_identifier:#x}"
450- ) ,
447+ if buf . first ( ) == Some ( & EVNODE_TX_TYPE_ID ) {
448+ buf . advance ( 1 ) ;
449+ ( Self :: EvNode , buf)
450+ } else {
451+ let ( inner , buf ) = alloy_consensus :: TxType :: from_compact ( buf , identifier) ;
452+ ( Self :: Ethereum ( inner ) , buf )
451453 }
452454 }
453455 v => {
@@ -531,11 +533,50 @@ impl Compress for EvTxEnvelope {
531533
532534impl Decompress for EvTxEnvelope {
533535 fn decompress ( value : & [ u8 ] ) -> Result < Self , DecompressError > {
536+ ensure_supported_compact_tx_type ( value) ?;
534537 let ( obj, _) = Compact :: from_compact ( value, value. len ( ) ) ;
535538 Ok ( obj)
536539 }
537540}
538541
542+ /// Rejects unknown uncompressed extended identifiers so `Decompress` returns an error instead of
543+ /// panicking in `Compact`. Compressed payloads skip this check to avoid a second zstd pass.
544+ fn ensure_supported_compact_tx_type ( value : & [ u8 ] ) -> Result < ( ) , DecompressError > {
545+ const COMPACT_HEADER_LEN : usize = 1 + 64 ;
546+
547+ let flags = * value
548+ . first ( )
549+ . ok_or_else ( || compact_decode_error ( "missing compact header" ) ) ?;
550+ let identifier = ( flags & 0b110 ) >> 1 ;
551+ if identifier != COMPACT_EXTENDED_IDENTIFIER_FLAG as u8 {
552+ return Ok ( ( ) ) ;
553+ }
554+
555+ if flags >> 3 != 0 {
556+ return Ok ( ( ) ) ;
557+ }
558+
559+ let tx_type = * value
560+ . get ( COMPACT_HEADER_LEN )
561+ . ok_or_else ( || compact_decode_error ( "missing extended transaction identifier" ) ) ?;
562+
563+ match tx_type {
564+ alloy_consensus:: constants:: EIP4844_TX_TYPE_ID
565+ | alloy_consensus:: constants:: EIP7702_TX_TYPE_ID
566+ | EVNODE_TX_TYPE_ID => Ok ( ( ) ) ,
567+ _ => Err ( compact_decode_error ( format ! (
568+ "unsupported compact transaction identifier {tx_type:#x}"
569+ ) ) ) ,
570+ }
571+ }
572+
573+ fn compact_decode_error ( message : impl Into < String > ) -> DecompressError {
574+ DecompressError :: new ( std:: io:: Error :: new (
575+ std:: io:: ErrorKind :: InvalidData ,
576+ message. into ( ) ,
577+ ) )
578+ }
579+
539580fn optional_signature_length ( value : Option < & Signature > ) -> usize {
540581 match value {
541582 Some ( sig) => sig. as_bytes ( ) . as_slice ( ) . length ( ) ,
@@ -566,6 +607,7 @@ fn decode_optional_signature(buf: &mut &[u8]) -> alloy_rlp::Result<Option<Signat
566607#[ cfg( test) ]
567608mod tests {
568609 use super :: * ;
610+ use alloy_consensus:: TxEip7702 ;
569611 use alloy_eips:: eip2930:: AccessList ;
570612
571613 fn sample_signature ( ) -> Signature {
@@ -591,6 +633,38 @@ mod tests {
591633 }
592634 }
593635
636+ fn sample_eip7702_envelope ( input : Bytes ) -> EvTxEnvelope {
637+ let transaction = TxEip7702 {
638+ chain_id : 1 ,
639+ nonce : 1 ,
640+ gas_limit : 30_000 ,
641+ max_fee_per_gas : 2 ,
642+ max_priority_fee_per_gas : 1 ,
643+ to : Address :: ZERO ,
644+ value : U256 :: ZERO ,
645+ access_list : AccessList :: default ( ) ,
646+ authorization_list : Vec :: new ( ) ,
647+ input,
648+ } ;
649+ let signed = alloy_consensus:: Signed :: new_unhashed ( transaction, sample_signature ( ) ) ;
650+ EvTxEnvelope :: Ethereum ( signed. into ( ) )
651+ }
652+
653+ fn assert_eip7702_static_file_roundtrip ( input : Bytes , zstd : bool ) {
654+ let encoded = sample_eip7702_envelope ( input) . compress ( ) ;
655+ assert_eq ! (
656+ encoded[ 0 ] >> 3 != 0 ,
657+ zstd,
658+ "compact zstd flag should match calldata size"
659+ ) ;
660+ let decoded = EvTxEnvelope :: decompress ( & encoded) . expect ( "decode static-file transaction" ) ;
661+ assert ! ( matches!(
662+ decoded,
663+ EvTxEnvelope :: Ethereum ( ref tx)
664+ if tx. tx_type( ) == alloy_consensus:: TxType :: Eip7702
665+ ) ) ;
666+ }
667+
594668 #[ test]
595669 fn executor_signing_hash_ignores_sponsor_fields ( ) {
596670 let mut tx = sample_tx ( ) ;
@@ -635,4 +709,54 @@ mod tests {
635709 let err = decode_optional_signature ( & mut buf) . expect_err ( "invalid length" ) ;
636710 assert_eq ! ( err, alloy_rlp:: Error :: UnexpectedLength ) ;
637711 }
712+
713+ #[ test]
714+ fn compact_eip7702_type_roundtrip ( ) {
715+ let mut encoded = Vec :: new ( ) ;
716+ let identifier =
717+ EvTxType :: Ethereum ( alloy_consensus:: TxType :: Eip7702 ) . to_compact ( & mut encoded) ;
718+
719+ assert_eq ! ( identifier, COMPACT_EXTENDED_IDENTIFIER_FLAG ) ;
720+ assert_eq ! ( encoded, [ alloy_consensus:: constants:: EIP7702_TX_TYPE_ID ] ) ;
721+
722+ let ( decoded, remaining) = EvTxType :: from_compact ( & encoded, identifier) ;
723+ assert_eq ! (
724+ decoded,
725+ EvTxType :: Ethereum ( alloy_consensus:: TxType :: Eip7702 )
726+ ) ;
727+ assert ! ( remaining. is_empty( ) ) ;
728+ }
729+
730+ #[ test]
731+ fn static_file_compact_eip7702_transaction_roundtrip ( ) {
732+ assert_eip7702_static_file_roundtrip ( Bytes :: new ( ) , false ) ;
733+ }
734+
735+ #[ test]
736+ fn static_file_compact_eip7702_compressed_transaction_roundtrip ( ) {
737+ // CompactEnvelope sets the zstd bit at 32 bytes of calldata.
738+ assert_eip7702_static_file_roundtrip ( Bytes :: from ( vec ! [ 0 ; 32 ] ) , true ) ;
739+ }
740+
741+ #[ test]
742+ fn static_file_unknown_extended_type_returns_error ( ) {
743+ let mut encoded = vec ! [ ( COMPACT_EXTENDED_IDENTIFIER_FLAG as u8 ) << 1 ] ;
744+ encoded. extend_from_slice ( & [ 0 ; 64 ] ) ;
745+ encoded. push ( 0x7f ) ;
746+
747+ let err = EvTxEnvelope :: decompress ( & encoded) . expect_err ( "unsupported type must fail" ) ;
748+ assert ! ( err
749+ . to_string( )
750+ . contains( "unsupported compact transaction identifier 0x7f" ) ) ;
751+ }
752+
753+ #[ test]
754+ fn static_file_compressed_unknown_extended_type_is_skipped ( ) {
755+ let mut encoded = vec ! [ ( ( COMPACT_EXTENDED_IDENTIFIER_FLAG as u8 ) << 1 ) | ( 1 << 3 ) ] ;
756+ encoded. extend_from_slice ( & [ 0 ; 64 ] ) ;
757+ encoded. push ( 0x7f ) ;
758+
759+ ensure_supported_compact_tx_type ( & encoded)
760+ . expect ( "compressed identifiers are left to Compact" ) ;
761+ }
638762}
0 commit comments