@@ -253,19 +253,27 @@ impl Transaction {
253253 Ok ( result)
254254 }
255255
256- /// Get transaction ID (Blake2-256 hash of signed transaction)
256+ /// Get transaction ID (Blake2-256 hash of transaction bytes)
257+ ///
258+ /// For signed transactions, hashes the fully serialized extrinsic.
259+ /// For unsigned transactions, hashes the raw bytes as a stable identifier.
257260 pub fn id ( & self ) -> Option < String > {
258261 use blake2:: { digest:: consts:: U32 , Blake2b , Digest } ;
259262
260- if self . is_signed && self . signature . is_some ( ) {
261- let bytes = self . to_bytes ( ) . ok ( ) ?;
262- let mut hasher = Blake2b :: < U32 > :: new ( ) ;
263- hasher. update ( & bytes) ;
264- let hash = hasher. finalize ( ) ;
265- Some ( format ! ( "0x{}" , hex:: encode( hash) ) )
263+ let bytes = if self . is_signed && self . signature . is_some ( ) {
264+ self . to_bytes ( ) . ok ( ) ?
266265 } else {
267- None
266+ self . raw_bytes . clone ( )
267+ } ;
268+
269+ if bytes. is_empty ( ) {
270+ return None ;
268271 }
272+
273+ let mut hasher = Blake2b :: < U32 > :: new ( ) ;
274+ hasher. update ( & bytes) ;
275+ let hash = hasher. finalize ( ) ;
276+ Some ( format ! ( "0x{}" , hex:: encode( hash) ) )
269277 }
270278
271279 /// Get the signable payload for this transaction
@@ -734,6 +742,25 @@ fn decode_era_bytes(bytes: &[u8]) -> Result<(Era, usize), WasmDotError> {
734742mod tests {
735743 use super :: * ;
736744
745+ #[ test]
746+ fn test_unsigned_tx_id_returns_blake2b_hash ( ) {
747+ // Minimal unsigned extrinsic: compact length + version 0x04 + era(immortal) + nonce(0) + tip(0) + call_data
748+ // length=6 (compact 0x18), version=0x04, era=0x00, nonce=0x00, tip=0x00, call=0xFF
749+ let raw = vec ! [ 0x18 , 0x04 , 0x00 , 0x00 , 0x00 , 0xFF ] ;
750+ let tx = Transaction :: from_bytes ( & raw , None , None ) . unwrap ( ) ;
751+
752+ assert ! ( !tx. is_signed( ) ) ;
753+ let id = tx. id ( ) ;
754+ assert ! ( id. is_some( ) , "unsigned tx should have an id" ) ;
755+ let id = id. unwrap ( ) ;
756+ assert ! ( id. starts_with( "0x" ) , "id should be 0x-prefixed hex" ) ;
757+ assert_eq ! ( id. len( ) , 66 , "blake2b-256 hash = 0x + 64 hex chars" ) ;
758+
759+ // Same bytes should produce the same hash
760+ let tx2 = Transaction :: from_bytes ( & raw , None , None ) . unwrap ( ) ;
761+ assert_eq ! ( tx. id( ) , tx2. id( ) , "same bytes should produce same id" ) ;
762+ }
763+
737764 #[ test]
738765 fn test_era_encoding_roundtrip ( ) {
739766 let immortal = Era :: Immortal ;
0 commit comments