ipfs_hasher/
lib.rs

1use ipfs_unixfs::file::adder::Chunker;
2use ipfs_unixfs::file::adder::FileAdder;
3
4/// Provide the bytes of your content and IpfsHasher
5/// will return the IPFS hash.
6///
7/// Calculation the IPFS hash of a file is not as trivial as
8/// passing all its bytes into a Hasher. It first requires to
9/// split the bytes in a chunks of a given size, turning those
10/// into blocks, giving 1..n CIDs then return the root CID.
11/// This is what this crate does.
12/// If you need more control, check out the `cid` and the `ipfs-unixfs``
13/// crates.
14/// This crate procudes CidV0.
15#[derive(Debug)]
16pub struct IpfsHasher {
17	chunk_size: Option<usize>,
18}
19
20impl Default for IpfsHasher {
21	fn default() -> Self {
22		Self { chunk_size: None }
23	}
24}
25
26impl IpfsHasher {
27	#[cfg(test)]
28	pub fn new(chunk_size: usize) -> Self {
29		Self { chunk_size: Some(chunk_size) }
30	}
31
32	/// Compute and return the IPFS Hash (cid) as String
33	pub fn compute(&self, content: &[u8]) -> String {
34		let mut adder = match self.chunk_size {
35			None => FileAdder::default(),
36			Some(size) => FileAdder::builder().with_chunker(Chunker::Size(size)).build(),
37		};
38
39		let mut written = 0;
40
41		while written < content.len() {
42			let slice = &content[written..];
43			let (_blocks, pushed) = adder.push(slice);
44			written += pushed;
45		}
46
47		let res = adder.finish();
48		let (cid, _data) = res.last().unwrap();
49
50		cid.to_string()
51	}
52}
53
54#[cfg(test)]
55mod tests {
56	use super::IpfsHasher;
57	use wasm_loader::{OnchainBlock, Source, WasmLoader};
58
59	#[test]
60	fn it_works_with_single_block() {
61		let hasher = IpfsHasher::default();
62		let ipfs = hasher.compute(b"foobar\n");
63		assert!(ipfs.to_string() == "QmRgutAxd8t7oGkSm4wmeuByG6M51wcTso6cubDdQtuEfL");
64	}
65
66	#[test]
67	fn it_works_with_multiple_blocks() {
68		let hasher = IpfsHasher::new(2);
69		let ipfs = hasher.compute(b"foobar\n");
70		assert!(ipfs.to_string() == "QmRJHYTNvC3hmd9gJQARxLR1QMEincccBV53bBw524yyq6");
71	}
72
73	#[test]
74	#[ignore = "Onchain data..."]
75	fn it_computes_a_runtime_ipfs_hash() {
76		const POLKADOT_BLOCK20: &str = "0x4d6a0bca208b85d41833a7f35cf73d1ae6974f4bad8ab576e2c3f751d691fe6c"; // Polkadot Block #20
77
78		let ocb = OnchainBlock::new("wss://rpc.polkadot.io", Some(POLKADOT_BLOCK20.to_string()));
79		let loader = WasmLoader::load_from_source(&Source::Chain(ocb)).unwrap();
80		let hasher = IpfsHasher::default();
81		let cid = hasher.compute(loader.bytes());
82		assert!(cid == "QmevKMGkRViXfQMSZ38DBdcJ1cXcXf9sXdfXie8Jkc7ZGs");
83	}
84}