bundlr_sdk/
deep_hash.rs

1use std::pin::Pin;
2
3use async_recursion::async_recursion;
4use bytes::Bytes;
5use sha2::{Digest, Sha384};
6
7use crate::{
8    consts::{BLOB_AS_BUFFER, LIST_AS_BUFFER},
9    error::BundlrError,
10};
11use futures::{Stream, TryStream, TryStreamExt};
12
13pub enum DeepHashChunk<'a> {
14    Chunk(Bytes),
15    Stream(&'a mut Pin<Box<dyn Stream<Item = anyhow::Result<Bytes>>>>),
16    Chunks(Vec<DeepHashChunk<'a>>),
17}
18
19trait Foo: Stream<Item = anyhow::Result<Bytes>> + TryStream {}
20
21pub async fn deep_hash(chunk: DeepHashChunk<'_>) -> Result<Bytes, BundlrError> {
22    match chunk {
23        DeepHashChunk::Chunk(b) => {
24            let tag = [BLOB_AS_BUFFER, b.len().to_string().as_bytes()].concat();
25            let c = [sha384hash(tag.into()), sha384hash(b)].concat();
26            Ok(Bytes::copy_from_slice(&sha384hash(c.into())))
27        }
28        DeepHashChunk::Stream(s) => {
29            let mut hasher = Sha384::new();
30            let mut length = 0;
31            while let Some(chunk) = s
32                .as_mut()
33                .try_next()
34                .await
35                .map_err(|_| BundlrError::NoBytesLeft)?
36            {
37                length += chunk.len();
38                hasher.update(&chunk);
39            }
40
41            let tag = [BLOB_AS_BUFFER, length.to_string().as_bytes()].concat();
42
43            let tagged_hash = [
44                sha384hash(tag.into()),
45                Bytes::copy_from_slice(&hasher.finalize()),
46            ]
47            .concat();
48
49            Ok(sha384hash(tagged_hash.into()))
50        }
51        DeepHashChunk::Chunks(mut chunks) => {
52            // Be careful of truncation
53            let len = chunks.len() as f64;
54            let tag = [LIST_AS_BUFFER, len.to_string().as_bytes()].concat();
55
56            let acc = sha384hash(tag.into());
57
58            deep_hash_chunks(&mut chunks, acc).await
59        }
60    }
61}
62
63#[async_recursion(?Send)]
64pub async fn deep_hash_chunks(
65    chunks: &mut Vec<DeepHashChunk<'_>>,
66    acc: Bytes,
67) -> Result<Bytes, BundlrError> {
68    if chunks.is_empty() {
69        return Ok(acc);
70    };
71
72    let acc = Bytes::copy_from_slice(&acc);
73
74    let hash_pair = [acc, deep_hash(chunks.remove(0)).await?].concat();
75    let new_acc = sha384hash(hash_pair.into());
76    deep_hash_chunks(chunks, new_acc).await
77}
78
79fn sha384hash(b: Bytes) -> Bytes {
80    let mut hasher = Sha384::new();
81    hasher.update(&b);
82    Bytes::copy_from_slice(&hasher.finalize())
83}