1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4use crate::validator::ValidatorId;
5use crate::view::ViewNumber;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
8pub struct BlockHash(pub [u8; 32]);
9
10impl BlockHash {
11 pub const GENESIS: Self = Self([0u8; 32]);
12
13 pub fn is_genesis(&self) -> bool {
14 self.0 == [0u8; 32]
15 }
16}
17
18impl fmt::Display for BlockHash {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 write!(f, "{}", hex::encode(&self.0[..4]))
21 }
22}
23
24#[derive(
25 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default,
26)]
27pub struct Height(pub u64);
28
29impl Height {
30 pub const GENESIS: Self = Self(0);
31
32 pub fn next(self) -> Self {
33 Self(self.0.checked_add(1).expect("height overflow"))
34 }
35
36 pub fn as_u64(self) -> u64 {
37 self.0
38 }
39}
40
41impl fmt::Display for Height {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 write!(f, "h{}", self.0)
44 }
45}
46
47impl From<u64> for Height {
48 fn from(v: u64) -> Self {
49 Self(v)
50 }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct Block {
56 pub height: Height,
57 pub parent_hash: BlockHash,
58 pub view: ViewNumber,
59 pub proposer: ValidatorId,
60 pub payload: Vec<u8>,
61 pub app_hash: BlockHash,
67 pub hash: BlockHash,
68}
69
70impl Block {
71 pub fn genesis() -> Self {
72 Self {
73 height: Height::GENESIS,
74 parent_hash: BlockHash::GENESIS,
75 view: ViewNumber::GENESIS,
76 proposer: ValidatorId::default(),
77 payload: Vec::new(),
78 app_hash: BlockHash::GENESIS,
79 hash: BlockHash::GENESIS,
80 }
81 }
82
83 pub fn compute_hash(&self) -> BlockHash {
88 let mut hasher = blake3::Hasher::new();
89 hasher.update(&self.height.as_u64().to_le_bytes());
90 hasher.update(&self.parent_hash.0);
91 hasher.update(&self.view.as_u64().to_le_bytes());
92 hasher.update(&self.proposer.0.to_le_bytes());
93 hasher.update(&self.app_hash.0);
94 hasher.update(&self.payload);
95 let hash = hasher.finalize();
96 BlockHash(*hash.as_bytes())
97 }
98}
99
100mod hex {
101 pub fn encode(bytes: &[u8]) -> String {
102 bytes.iter().map(|b| format!("{:02x}", b)).collect()
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn test_block_hash_genesis() {
112 assert!(BlockHash::GENESIS.is_genesis());
113 assert!(!BlockHash([1u8; 32]).is_genesis());
114 }
115
116 #[test]
117 fn test_block_hash_display() {
118 let h = BlockHash([
119 0xab, 0xcd, 0xef, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0,
121 ]);
122 assert_eq!(format!("{h}"), "abcdef12");
123 }
124
125 #[test]
126 fn test_height_next() {
127 assert_eq!(Height(0).next(), Height(1));
128 assert_eq!(Height(99).next(), Height(100));
129 }
130
131 #[test]
132 fn test_height_ordering() {
133 assert!(Height(1) < Height(2));
134 assert!(Height(5) > Height(3));
135 assert!(Height(0) <= Height::GENESIS);
136 }
137
138 #[test]
139 fn test_genesis_block() {
140 let g = Block::genesis();
141 assert_eq!(g.height, Height::GENESIS);
142 assert!(g.parent_hash.is_genesis());
143 assert!(g.hash.is_genesis());
144 assert!(g.payload.is_empty());
145 }
146}