Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol.
wisp.place
1use jacquard_common::types::cid::IpldCid;
2use sha2::{Digest, Sha256};
3
4/// Compute CID (Content Identifier) for blob content
5/// Uses the same algorithm as AT Protocol: CIDv1 with raw codec (0x55) and SHA-256
6///
7/// CRITICAL: This must be called on BASE64-ENCODED GZIPPED content, not just gzipped content
8///
9/// Based on @atproto/common/src/ipld.ts sha256RawToCid implementation
10pub fn compute_cid(content: &[u8]) -> String {
11 // Use node crypto to compute sha256 hash (same as AT Protocol)
12 let hash = Sha256::digest(content);
13
14 // Create multihash (code 0x12 = sha2-256)
15 let multihash = multihash::Multihash::wrap(0x12, &hash)
16 .expect("SHA-256 hash should always fit in multihash");
17
18 // Create CIDv1 with raw codec (0x55)
19 let cid = IpldCid::new_v1(0x55, multihash);
20
21 // Convert to base32 string representation
22 cid.to_string_of_base(multibase::Base::Base32Lower)
23 .unwrap_or_else(|_| cid.to_string())
24}
25
26#[cfg(test)]
27mod tests {
28 use super::*;
29 use base64::Engine;
30
31 #[test]
32 fn test_compute_cid() {
33 // Test with a simple string: "hello"
34 let content = b"hello";
35 let cid = compute_cid(content);
36
37 // CID should start with 'baf' for raw codec base32
38 assert!(cid.starts_with("baf"));
39 }
40
41 #[test]
42 fn test_compute_cid_base64_encoded() {
43 // Simulate the actual use case: gzipped then base64 encoded
44 use flate2::write::GzEncoder;
45 use flate2::Compression;
46 use std::io::Write;
47
48 let original = b"hello world";
49
50 // Gzip compress
51 let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
52 encoder.write_all(original).unwrap();
53 let gzipped = encoder.finish().unwrap();
54
55 // Base64 encode the gzipped data
56 let base64_bytes = base64::prelude::BASE64_STANDARD.encode(&gzipped).into_bytes();
57
58 // Compute CID on the base64 bytes
59 let cid = compute_cid(&base64_bytes);
60
61 // Should be a valid CID
62 assert!(cid.starts_with("baf"));
63 assert!(cid.len() > 10);
64 }
65}
66