data_anchor_blober/state/blob/
mod.rs1use 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}