snops_checkpoint/
header.rs1use 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 pub block_height: u32,
16 pub timestamp: i64,
18 pub block_hash: [u8; 32],
20 pub genesis_hash: [u8; 32],
23 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}