snops_checkpoint/
header.rs

1use std::{
2    io::{self, Read, Write},
3    path::PathBuf,
4};
5
6use chrono::{DateTime, TimeDelta, Utc};
7
8use crate::errors::CheckpointHeaderError::{self as Error, *};
9
10const CHECKPOINT_VERSION: u8 = 2;
11
12#[derive(Debug, Clone)]
13pub struct CheckpointHeader {
14    /// Block height
15    pub block_height: u32,
16    /// Block timestamp
17    pub timestamp: i64,
18    /// Block's hash
19    pub block_hash: [u8; 32],
20    /// Genesis block's hash - used to ensure the checkpoint is applicable to
21    /// this network
22    pub genesis_hash: [u8; 32],
23    /// Size of the checkpoint
24    pub content_len: u64,
25}
26
27impl CheckpointHeader {
28    pub fn read_file(path: &PathBuf) -> Result<Self, Error> {
29        let reader = std::fs::File::options()
30            .read(true)
31            .open(path)
32            .map_err(FileError)?;
33        let header = Self::read_bytes(&reader).map_err(ReadError)?;
34        Ok(header)
35    }
36
37    #[cfg(feature = "write")]
38    pub fn read_ledger<N: crate::aleo::Network>(path: PathBuf) -> Result<Self, Error> {
39        use Error::*;
40
41        use crate::aleo::*;
42
43        let commitee =
44            CommitteeDB::<N>::open(StorageMode::Custom(path.clone())).map_err(OpenLedger)?;
45        let blocks = BlockDB::<N>::open(StorageMode::Custom(path)).map_err(OpenLedger)?;
46
47        let height = commitee.current_height().map_err(ReadLedger)?;
48        let Some(block_hash): Option<BlockHash<N>> =
49            blocks.get_block_hash(height).map_err(ReadLedger)?
50        else {
51            return Err(BlockNotFound(height));
52        };
53        let Some(genesis_hash): Option<BlockHash<N>> =
54            blocks.get_block_hash(0).map_err(ReadLedger)?
55        else {
56            return Err(HashlessGenesis);
57        };
58        let Some(block_header) = blocks.get_block_header(&block_hash).map_err(ReadLedger)? else {
59            return Err(BlockMissingHeader(height, block_hash.to_string()));
60        };
61
62        Ok(Self {
63            block_height: height,
64            timestamp: block_header.timestamp(),
65            block_hash: block_bytes::<N>(&block_hash),
66            genesis_hash: block_bytes::<N>(&genesis_hash),
67            content_len: 0,
68        })
69    }
70
71    pub fn time(&self) -> DateTime<Utc> {
72        DateTime::UNIX_EPOCH + TimeDelta::new(self.timestamp, 0).unwrap()
73    }
74
75    pub fn write_bytes<W: Write>(&self, mut w: W) -> io::Result<()> {
76        w.write_all(&[CHECKPOINT_VERSION])?;
77        w.write_all(&self.block_height.to_le_bytes())?;
78        w.write_all(&self.timestamp.to_le_bytes())?;
79        w.write_all(&self.block_hash)?;
80        w.write_all(&self.genesis_hash)?;
81        w.write_all(&self.content_len.to_le_bytes())?;
82        Ok(())
83    }
84
85    pub fn read_bytes<R: Read>(mut r: R) -> io::Result<Self> {
86        let mut buf = [0u8; 1 + 4 + 8 + 32 + 32 + 8];
87        r.read_exact(&mut buf)?;
88        let mut buf = buf.into_iter();
89
90        let version = buf.next().unwrap();
91        if version != CHECKPOINT_VERSION {
92            return Err(io::Error::new(
93                io::ErrorKind::Interrupted,
94                format!("invalid checkpoint version: {version}, expected {CHECKPOINT_VERSION}"),
95            ));
96        }
97
98        fn take<const SIZE: usize>(buf: &mut impl Iterator<Item = u8>, n: usize) -> [u8; SIZE] {
99            let mut arr = [0u8; SIZE];
100            buf.take(n).enumerate().for_each(|(i, b)| arr[i] = b);
101            arr
102        }
103
104        let block_height = u32::from_le_bytes(take(&mut buf, 4));
105        let timestamp = i64::from_le_bytes(take(&mut buf, 8));
106        let block_hash = take(&mut buf, 32);
107        let genesis_hash = take(&mut buf, 32);
108        let content_len = u64::from_le_bytes(take(&mut buf, 8));
109
110        Ok(Self {
111            block_height,
112            timestamp,
113            block_hash,
114            genesis_hash,
115            content_len,
116        })
117    }
118}