1717//!
1818//! This module provides types and functions to compute CIDs for raw data or
1919//! DAG blocks using supported hashing algorithms and codecs.
20+ //!
21+ //! See [`CidData`].
2022
2123use crate :: LOG_TARGET ;
24+ use alloc:: vec:: Vec ;
2225use cid:: { multihash:: Multihash , CidGeneric } ;
2326use codec:: { Decode , DecodeWithMemTracking , Encode , MaxEncodedLen } ;
2427use polkadot_sdk_frame:: deps:: sp_io;
@@ -66,12 +69,12 @@ impl HashingAlgorithm {
6669 /// Return the multihash code corresponding to this hashing algorithm.
6770 ///
6871 /// These codes follow the [multihash table](https://github.com/multiformats/multicodec/blob/master/table.csv):
69- /// - Blake2b-256 = 0xb2 (178)
70- /// - SHA2-256 = 0x12 (18)
71- /// - Keccak-256 = 0x1b (27)
72+ /// - Blake2b-256 = 0xb220
73+ /// - SHA2-256 = 0x12
74+ /// - Keccak-256 = 0x1b
7275 pub fn multihash_code ( & self ) -> u64 {
7376 match self {
74- HashingAlgorithm :: Blake2b256 => 0xb2 ,
77+ HashingAlgorithm :: Blake2b256 => 0xb220 ,
7578 HashingAlgorithm :: Sha2_256 => 0x12 ,
7679 HashingAlgorithm :: Keccak256 => 0x1b ,
7780 }
@@ -98,8 +101,12 @@ pub struct CidConfig {
98101}
99102
100103/// Representation of a generated CID.
104+ #[ derive( Debug , PartialEq , Eq ) ]
101105pub struct CidData {
102106 /// 32-byte content hash of the input data.
107+ ///
108+ /// Note: This is used for indexing transactions and retrieving
109+ /// `self.client.indexed_transaction(hash)`. Note: This is equal to `cid.hash().digest()`.
103110 pub content_hash : ContentHash ,
104111 /// Hashing algorithm used.
105112 pub hashing : HashingAlgorithm ,
@@ -146,5 +153,92 @@ pub fn calculate_cid(data: &[u8], config: Option<CidConfig>) -> Result<CidData,
146153 Ok ( CidData { content_hash, hashing, codec, cid : cid_bytes } )
147154}
148155
149- // TODO: add here more tests for compatibility.
156+ #[ cfg( test) ]
157+ mod tests {
158+ use super :: { calculate_cid, CidConfig , HashingAlgorithm } ;
159+ use cid:: {
160+ multibase:: { encode as to_base32, Base } ,
161+ CidGeneric ,
162+ } ;
163+ use core:: str:: FromStr ;
164+ use polkadot_sdk_frame:: deps:: sp_io;
165+
166+ #[ test]
167+ fn test_cid_raw_blake2b_256_roundtrip_works ( ) {
168+ // Prepare data.
169+ let data = "Hello, Bulletin with PAPI - Fri Nov 21 2025 11:09:18 GMT+0000" ;
170+ let expected_content_hash = sp_io:: hashing:: blake2_256 ( data. as_bytes ( ) ) ;
171+
172+ // Expected raw CID calculated for the same data with `examples/common.js`.
173+ let expected_cid_base32 = "bafk2bzacedvk4eijklisgdjijnxky24pmkg7jgk5vsct4mwndj3nmx7plzz7m" ;
174+ let expected_cid = CidGeneric :: < 32 > :: from_str ( expected_cid_base32) . expect ( "valid_cid" ) ;
175+ assert_eq ! ( expected_cid. codec( ) , 0x55 ) ;
176+ assert_eq ! ( expected_cid. hash( ) . code( ) , 0xb220 ) ;
177+ assert_eq ! ( expected_cid. hash( ) . size( ) , 0x20 ) ;
178+ assert_eq ! ( expected_cid. hash( ) . digest( ) , expected_content_hash) ;
179+
180+ // Calculate CIDv1 with default raw codec and blake2b-256.
181+ let cid_raw = calculate_cid ( data. as_ref ( ) , None ) . expect ( "valid_cid" ) ;
182+ let cid_blake2b_256_raw = calculate_cid (
183+ data. as_ref ( ) ,
184+ Some ( CidConfig { codec : 0x55 , hashing : HashingAlgorithm :: Blake2b256 } ) ,
185+ )
186+ . expect ( "valid_cid" ) ;
187+ assert_eq ! ( cid_raw. cid, expected_cid. to_bytes( ) ) ;
188+ assert_eq ! ( to_base32( Base :: Base32Lower , & cid_raw. cid) , expected_cid_base32) ;
189+ assert_eq ! ( cid_raw. codec, expected_cid. codec( ) ) ;
190+ assert_eq ! ( cid_raw. hashing. multihash_code( ) , expected_cid. hash( ) . code( ) ) ;
191+ assert_eq ! ( cid_raw. content_hash, expected_cid. hash( ) . digest( ) ) ;
192+ assert_eq ! ( cid_raw, cid_blake2b_256_raw) ;
193+ }
194+
195+ /// Return the HashingAlgorithm corresponding to a multihash code.
196+ pub fn from_multihash_code ( code : u64 ) -> HashingAlgorithm {
197+ match code {
198+ 0xb220 => HashingAlgorithm :: Blake2b256 ,
199+ 0x12 => HashingAlgorithm :: Sha2_256 ,
200+ 0x1b => HashingAlgorithm :: Keccak256 ,
201+ code @ _ => panic ! ( "{code} is not supported" ) ,
202+ }
203+ }
150204
205+ #[ test]
206+ fn test_cid_various_codecs_and_hashes ( ) {
207+ let data = "Hello, Bulletin with PAPI - Fri Nov 21 2025 11:09:18 GMT+0000" ;
208+
209+ // Expected results from `examples/common.js`.
210+ let expected_cids = vec ! [
211+ // raw + blake2b_256
212+ ( "bafk2bzacedvk4eijklisgdjijnxky24pmkg7jgk5vsct4mwndj3nmx7plzz7m" , 0x55 , 0xb220 ) ,
213+ // DAG-PB + blake2b_256
214+ ( "bafykbzacedvk4eijklisgdjijnxky24pmkg7jgk5vsct4mwndj3nmx7plzz7m" , 0x70 , 0xb220 ) ,
215+ // Raw + sha2_256
216+ ( "bafkreig5pw2of63kmkldboh6utfovo3o3czig4yj7eb2ragxwca4c4jlke" , 0x55 , 0x12 ) ,
217+ // DAG-PB + sha2_256
218+ ( "bafybeig5pw2of63kmkldboh6utfovo3o3czig4yj7eb2ragxwca4c4jlke" , 0x70 , 0x12 ) ,
219+ // Raw + keccak_256
220+ ( "bafkrwifr4p73tsatchlyp3hivjee4prqqpcqayikzen46bqldwmt5mzd6e" , 0x55 , 0x1b ) ,
221+ // DAG-PB + keccak_256
222+ ( "bafybwifr4p73tsatchlyp3hivjee4prqqpcqayikzen46bqldwmt5mzd6e" , 0x70 , 0x1b ) ,
223+ ] ;
224+
225+ for ( expected_cid_str, codec, mh_code) in expected_cids {
226+ let cid = CidGeneric :: < 32 > :: from_str ( expected_cid_str) . expect ( "valid CID" ) ;
227+ // Check codec and multihash code
228+ assert_eq ! ( cid. codec( ) , codec) ;
229+ assert_eq ! ( cid. hash( ) . code( ) , mh_code) ;
230+
231+ // Test `calculate_cid`
232+ let calculated = calculate_cid (
233+ data. as_ref ( ) ,
234+ Some ( CidConfig { codec, hashing : from_multihash_code ( mh_code) } ) ,
235+ )
236+ . expect ( "calculate_cid succeeded" ) ;
237+
238+ assert_eq ! ( to_base32( Base :: Base32Lower , & calculated. cid) , expected_cid_str) ;
239+ assert_eq ! ( calculated. codec, codec) ;
240+ assert_eq ! ( calculated. hashing. multihash_code( ) , mh_code) ;
241+ assert_eq ! ( calculated. content_hash, cid. hash( ) . digest( ) ) ;
242+ }
243+ }
244+ }
0 commit comments