bundlr_sdk/verify/
file.rs

1use super::types::{Header, Item};
2use crate::error::BundlrError;
3use crate::utils::read_offset;
4use crate::BundlrTx;
5use data_encoding::BASE64URL;
6use primitive_types::U256;
7use std::{cmp, fs::File};
8
9impl From<std::io::Error> for BundlrError {
10    fn from(e: std::io::Error) -> Self {
11        BundlrError::FsError(e.to_string())
12    }
13}
14
15pub async fn verify_file_bundle(filename: String) -> Result<Vec<Item>, BundlrError> {
16    let mut file = File::open(&filename)?;
17
18    let bundle_length = U256::from_little_endian(&read_offset(&mut file, 0, 32)?).as_u64();
19
20    // NOTE THIS IS UNSAFE BEYOND USIZE LIMIT
21    let header_bytes = read_offset(&mut file, 32, bundle_length as usize * 64)?;
22    // This will use ~100 bytes per header. So 1 GB is 1e+7 headers
23    let mut headers = Vec::with_capacity(cmp::min(bundle_length as usize, 1000));
24
25    for i in (0..(64
26        * usize::try_from(bundle_length)
27            .map_err(|err| BundlrError::TypeParseError(err.to_string()))?))
28        .step_by(64)
29    {
30        let h = Header(
31            U256::from_little_endian(&header_bytes[i..i + 32]).as_u64(),
32            BASE64URL.encode(&header_bytes[i + 32..i + 64]),
33        );
34        headers.push(h);
35    }
36
37    let mut offset = 32 + (64 * bundle_length);
38    let mut items = Vec::with_capacity(cmp::min(bundle_length as usize, 1000));
39
40    for Header(size, id) in headers {
41        // Read 4 KiB - max data-less Bundlr tx
42        // We do it all at once to improve performance - by lowering fs ops and doing ops in memory
43        let mut tx = BundlrTx::from_file_position(&mut file, size, offset, 4096)?;
44
45        match tx.verify().await {
46            Err(err) => return Err(err),
47            Ok(_) => {
48                let sig = tx.get_signarure();
49                let item = Item {
50                    tx_id: id,
51                    signature: sig,
52                };
53                items.push(item);
54                offset += size;
55            }
56        }
57    }
58
59    Ok(items)
60}
61
62#[cfg(test)]
63mod tests {
64    use crate::error::BundlrError;
65
66    use super::verify_file_bundle;
67
68    #[tokio::test]
69    async fn should_verify_test_bundle() -> Result<(), BundlrError> {
70        verify_file_bundle("./res/test_bundles/test_bundle".to_string())
71            .await
72            .map(|_| ())
73    }
74
75    #[tokio::test]
76    async fn should_verify_arweave() -> Result<(), BundlrError> {
77        verify_file_bundle("./res/test_bundles/arweave_sig".to_string())
78            .await
79            .map(|_| ())
80    }
81
82    #[tokio::test]
83    async fn should_verify_secp256k1() -> Result<(), BundlrError> {
84        verify_file_bundle("./res/test_bundles/ethereum_sig".to_string()).await?;
85        verify_file_bundle("./res/test_bundles/typedethereum_sig".to_string()).await?;
86        verify_file_bundle("./res/test_bundles/arbitrum_sig".to_string()).await?;
87        verify_file_bundle("./res/test_bundles/avalanche_sig".to_string()).await?;
88        verify_file_bundle("./res/test_bundles/bnb_sig".to_string()).await?;
89        verify_file_bundle("./res/test_bundles/boba-eth_sig".to_string()).await?;
90        verify_file_bundle("./res/test_bundles/chainlink_sig".to_string()).await?;
91        verify_file_bundle("./res/test_bundles/kyve_sig".to_string()).await?;
92        verify_file_bundle("./res/test_bundles/matic_sig".to_string()).await?;
93        Ok(())
94    }
95
96    /*
97    #[tokio::test]
98    #[cfg(feature = "cosmos")]
99    async fn should_verify_cosmos() {
100        //TODO: update cosmos signed transaction when its constant is defined
101        assert!(
102            verify_file_bundle("./res/test_bundles/cosmos_sig".to_string())
103            .await
104            .is_ok()
105        );
106    }
107    */
108
109    #[tokio::test]
110    async fn should_verify_ed25519() -> Result<(), BundlrError> {
111        verify_file_bundle("./res/test_bundles/solana_sig".to_string()).await?;
112        verify_file_bundle("./res/test_bundles/algorand_sig".to_string()).await?;
113        verify_file_bundle("./res/test_bundles/near_sig".to_string()).await?;
114        verify_file_bundle("./res/test_bundles/aptos_sig".to_string()).await?;
115        verify_file_bundle("./res/test_bundles/aptos_multisig".to_string()).await?;
116        Ok(())
117    }
118
119    #[tokio::test]
120    async fn should_verify_random_bundles() -> Result<(), BundlrError> {
121        for i in 1..100 {
122            verify_file_bundle(format!("./res/gen_bundles/bundle_{}", i).to_string()).await?;
123        }
124        Ok(())
125    }
126}