data_anchor_blober/state/blob/
mod.rs

1use std::time::{Duration, SystemTime};
2
3use anchor_lang::{prelude::*, solana_program::hash};
4
5use super::bitmap::Bitmap;
6use crate::{
7    constants::{BLOB_SLOT_INCREMENTAL_DELAY_LIMIT, BLOB_SLOT_TOTAL_DELAY_LIMIT, CHUNK_SIZE},
8    error::ErrorCode,
9    hash_leaf, initial_hash,
10};
11
12#[cfg(test)]
13mod tests;
14
15#[account]
16#[derive(InitSpace)]
17pub struct Blob {
18    digest: [u8; hash::HASH_BYTES],
19    pub(crate) size: u32,
20    bitmap: Bitmap,
21    pub(crate) timestamp: u64,
22    pub(crate) created_at: u64,
23    pub(crate) last_updated_at: u64,
24    pub(crate) bump: u8,
25}
26
27impl std::fmt::Debug for Blob {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        f.debug_struct("Accumulator")
30            .field("digest", &hex::encode(self.digest))
31            .field(
32                "timestamp",
33                &(SystemTime::UNIX_EPOCH + Duration::from_secs(self.timestamp)),
34            )
35            .field("last_updated", &self.last_updated_at)
36            .field("num_chunks", &self.bitmap.num_chunks)
37            .finish()
38    }
39}
40
41impl Blob {
42    pub fn new(slot: u64, timestamp: u64, blob_size: u32, bump: u8) -> Self {
43        let num_chunks = blob_size.div_ceil(CHUNK_SIZE as u32) as u16;
44
45        Self {
46            digest: initial_hash(),
47            timestamp,
48            size: blob_size,
49            created_at: slot,
50            last_updated_at: slot,
51            bitmap: Bitmap::new(num_chunks),
52            bump,
53        }
54    }
55
56    pub fn blob_digest(&self) -> &[u8; hash::HASH_BYTES] {
57        &self.digest
58    }
59
60    pub fn is_complete(&self) -> bool {
61        self.bitmap.is_complete()
62    }
63
64    pub fn insert(&mut self, slot: u64, chunk_index: u16, chunk_data: &[u8]) {
65        if self.check_preconditions(slot, chunk_index).is_err() {
66            return;
67        }
68        self.digest = hash_leaf(self.digest, chunk_index, chunk_data);
69    }
70
71    fn check_preconditions(
72        &mut self,
73        slot: u64,
74        chunk_index: u16,
75    ) -> std::result::Result<(), ErrorCode> {
76        if chunk_index >= self.bitmap.num_chunks {
77            panic!("chunk {chunk_index} out of bounds");
78        }
79        self.check_time_limits(slot);
80
81        self.bitmap.test_and_set(chunk_index)
82    }
83
84    fn check_time_limits(&mut self, slot: u64) {
85        if slot.abs_diff(self.created_at) > BLOB_SLOT_TOTAL_DELAY_LIMIT {
86            panic!("blob created at {} is too far in the past", self.created_at);
87        }
88        if slot.abs_diff(self.last_updated_at) > BLOB_SLOT_INCREMENTAL_DELAY_LIMIT {
89            panic!(
90                "blob last updated at {} is too far in the past",
91                self.last_updated_at
92            );
93        }
94        self.last_updated_at = slot;
95    }
96}