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