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 + 1)
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 hash: BlockHash,
62}
63
64impl Block {
65 pub fn genesis() -> Self {
66 Self {
67 height: Height::GENESIS,
68 parent_hash: BlockHash::GENESIS,
69 view: ViewNumber::GENESIS,
70 proposer: ValidatorId::default(),
71 payload: Vec::new(),
72 hash: BlockHash::GENESIS,
73 }
74 }
75
76 pub fn compute_hash(&self) -> BlockHash {
81 let mut hasher = blake3::Hasher::new();
82 hasher.update(&self.height.as_u64().to_le_bytes());
83 hasher.update(&self.parent_hash.0);
84 hasher.update(&self.view.as_u64().to_le_bytes());
85 hasher.update(&self.proposer.0.to_le_bytes());
86 hasher.update(&self.payload);
87 let hash = hasher.finalize();
88 BlockHash(*hash.as_bytes())
89 }
90}
91
92mod hex {
93 pub fn encode(bytes: &[u8]) -> String {
94 bytes.iter().map(|b| format!("{:02x}", b)).collect()
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_block_hash_genesis() {
104 assert!(BlockHash::GENESIS.is_genesis());
105 assert!(!BlockHash([1u8; 32]).is_genesis());
106 }
107
108 #[test]
109 fn test_block_hash_display() {
110 let h = BlockHash([
111 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,
112 0, 0, 0, 0, 0, 0, 0,
113 ]);
114 assert_eq!(format!("{h}"), "abcdef12");
115 }
116
117 #[test]
118 fn test_height_next() {
119 assert_eq!(Height(0).next(), Height(1));
120 assert_eq!(Height(99).next(), Height(100));
121 }
122
123 #[test]
124 fn test_height_ordering() {
125 assert!(Height(1) < Height(2));
126 assert!(Height(5) > Height(3));
127 assert!(Height(0) <= Height::GENESIS);
128 }
129
130 #[test]
131 fn test_genesis_block() {
132 let g = Block::genesis();
133 assert_eq!(g.height, Height::GENESIS);
134 assert!(g.parent_hash.is_genesis());
135 assert!(g.hash.is_genesis());
136 assert!(g.payload.is_empty());
137 }
138}