@@ -52,6 +52,7 @@ use std::ops::Deref;
5252use std:: str:: FromStr ;
5353
5454use bitcoin_hashes:: hex;
55+ use bitcoin_hashes:: hex:: DisplayHex ;
5556use bitcoin_hashes:: sha256;
5657use bitcoin_hashes:: sha512_256;
5758use bitcoin_hashes:: Hash ;
@@ -77,6 +78,51 @@ pub trait AccumulatorHash:
7778 R : std:: io:: Read ;
7879}
7980
81+ /// (de)serialize as hex if the (de)serializer is human readable.
82+ #[ cfg( feature = "with-serde" ) ]
83+ mod serde_hex {
84+ pub fn serialize < S , T > ( data : T , serializer : S ) -> Result < S :: Ok , S :: Error >
85+ where
86+ S : serde:: Serializer ,
87+ T : serde:: Serialize + bitcoin_hashes:: hex:: DisplayHex ,
88+ {
89+ if serializer. is_human_readable ( ) {
90+ serializer. collect_str ( & format_args ! ( "{:x}" , data. as_hex( ) ) )
91+ } else {
92+ data. serialize ( serializer)
93+ }
94+ }
95+
96+ pub fn deserialize < ' de , D , T > ( deserializer : D ) -> Result < T , D :: Error >
97+ where
98+ D : serde:: Deserializer < ' de > ,
99+ T : serde:: Deserialize < ' de > + bitcoin_hashes:: hex:: FromHex ,
100+ {
101+ struct HexVisitor < T > ( std:: marker:: PhantomData < T > ) ;
102+
103+ impl < ' de , T > serde:: de:: Visitor < ' de > for HexVisitor < T >
104+ where
105+ T : bitcoin_hashes:: hex:: FromHex ,
106+ {
107+ type Value = T ;
108+
109+ fn expecting ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
110+ f. write_str ( "an ASCII hex string" )
111+ }
112+
113+ fn visit_str < E : serde:: de:: Error > ( self , data : & str ) -> Result < Self :: Value , E > {
114+ T :: from_hex ( data) . map_err ( serde:: de:: Error :: custom)
115+ }
116+ }
117+
118+ if deserializer. is_human_readable ( ) {
119+ deserializer. deserialize_str ( HexVisitor ( std:: marker:: PhantomData ) )
120+ } else {
121+ T :: deserialize ( deserializer)
122+ }
123+ }
124+ }
125+
80126#[ derive( Eq , PartialEq , Copy , Clone , Hash , PartialOrd , Ord ) ]
81127#[ cfg_attr( feature = "with-serde" , derive( Serialize , Deserialize ) ) ]
82128/// AccumulatorHash is a wrapper around a 32 byte array that represents a hash of a node in the tree.
@@ -94,7 +140,7 @@ pub enum BitcoinNodeHash {
94140 #[ default]
95141 Empty ,
96142 Placeholder ,
97- Some ( [ u8 ; 32 ] ) ,
143+ Some ( # [ cfg_attr ( feature = "with-serde" , serde ( with = "serde_hex" ) ) ] [ u8 ; 32 ] ) ,
98144}
99145
100146#[ deprecated( since = "0.4.0" , note = "Please use BitcoinNodeHash instead." ) ]
@@ -114,11 +160,7 @@ impl Deref for BitcoinNodeHash {
114160impl Display for BitcoinNodeHash {
115161 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: result:: Result < ( ) , std:: fmt:: Error > {
116162 if let BitcoinNodeHash :: Some ( ref inner) = self {
117- let mut s = String :: new ( ) ;
118- for byte in inner. iter ( ) {
119- s. push_str ( & format ! ( "{:02x}" , byte) ) ;
120- }
121- write ! ( f, "{}" , s)
163+ Display :: fmt ( & inner. as_hex ( ) , f)
122164 } else {
123165 write ! ( f, "empty" )
124166 }
@@ -130,13 +172,7 @@ impl Debug for BitcoinNodeHash {
130172 match self {
131173 BitcoinNodeHash :: Empty => write ! ( f, "empty" ) ,
132174 BitcoinNodeHash :: Placeholder => write ! ( f, "placeholder" ) ,
133- BitcoinNodeHash :: Some ( ref inner) => {
134- let mut s = String :: new ( ) ;
135- for byte in inner. iter ( ) {
136- s. push_str ( & format ! ( "{:02x}" , byte) ) ;
137- }
138- write ! ( f, "{}" , s)
139- }
175+ BitcoinNodeHash :: Some ( ref inner) => Debug :: fmt ( & inner. as_hex ( ) , f) ,
140176 }
141177 }
142178}
@@ -351,4 +387,78 @@ mod test {
351387 . unwrap ( ) ;
352388 assert_eq ! ( hash, AccumulatorHash :: empty( ) ) ;
353389 }
390+
391+ #[ cfg( feature = "with-serde" ) ]
392+ fn test_serde_json_roundtrip (
393+ node_hash : BitcoinNodeHash ,
394+ expected_serialized : serde_json:: Value ,
395+ ) -> Result < ( ) , serde_json:: Error > {
396+ let serialized = serde_json:: to_value ( node_hash) ?;
397+ assert_eq ! ( serialized, expected_serialized) ;
398+ let deserialized = serde_json:: from_value ( serialized) ?;
399+ assert_eq ! ( node_hash, deserialized) ;
400+ Ok ( ( ) )
401+ }
402+
403+ #[ cfg( feature = "with-serde" ) ]
404+ #[ test]
405+ fn test_serde_human_readable_impls ( ) -> Result < ( ) , serde_json:: Error > {
406+ let empty = BitcoinNodeHash :: Empty ;
407+ let placeholder = BitcoinNodeHash :: Placeholder ;
408+ let hash = {
409+ let mut bytes = [ 0u8 ; 32 ] ;
410+ for ( idx, byte) in bytes. iter_mut ( ) . enumerate ( ) {
411+ * byte = idx as u8 ;
412+ }
413+ BitcoinNodeHash :: Some ( bytes)
414+ } ;
415+ let ( ) = test_serde_json_roundtrip ( empty, serde_json:: Value :: String ( "Empty" . to_owned ( ) ) ) ?;
416+ let ( ) = test_serde_json_roundtrip (
417+ placeholder,
418+ serde_json:: Value :: String ( "Placeholder" . to_owned ( ) ) ,
419+ ) ?;
420+ let ( ) = test_serde_json_roundtrip (
421+ hash,
422+ serde_json:: json!( {
423+ "Some" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
424+ } ) ,
425+ ) ?;
426+ Ok ( ( ) )
427+ }
428+
429+ #[ cfg( feature = "with-serde" ) ]
430+ fn test_postcard_roundtrip (
431+ node_hash : BitcoinNodeHash ,
432+ expected_serialized : & [ u8 ] ,
433+ ) -> Result < ( ) , postcard:: Error > {
434+ let serialized = postcard:: to_allocvec ( & node_hash) ?;
435+ assert_eq ! ( & serialized, expected_serialized) ;
436+ let deserialized = postcard:: from_bytes ( & serialized) ?;
437+ assert_eq ! ( node_hash, deserialized) ;
438+ Ok ( ( ) )
439+ }
440+
441+ #[ cfg( feature = "with-serde" ) ]
442+ #[ test]
443+ fn test_serde_non_human_readable_impls ( ) -> Result < ( ) , postcard:: Error > {
444+ let empty = BitcoinNodeHash :: Empty ;
445+ let placeholder = BitcoinNodeHash :: Placeholder ;
446+ let hash = {
447+ let mut bytes = [ 0u8 ; 32 ] ;
448+ for ( idx, byte) in bytes. iter_mut ( ) . enumerate ( ) {
449+ * byte = idx as u8 ;
450+ }
451+ BitcoinNodeHash :: Some ( bytes)
452+ } ;
453+ let ( ) = test_postcard_roundtrip ( empty, & [ 0u8 ] ) ?;
454+ let ( ) = test_postcard_roundtrip ( placeholder, & [ 1u8 ] ) ?;
455+ let ( ) = test_postcard_roundtrip ( hash, & {
456+ let mut bytes = [ 2u8 ; 33 ] ;
457+ for ( idx, byte) in bytes. iter_mut ( ) . skip ( 1 ) . enumerate ( ) {
458+ * byte = idx as u8 ;
459+ }
460+ bytes
461+ } ) ?;
462+ Ok ( ( ) )
463+ }
354464}
0 commit comments