sumchain_primitives/
block.rs1use serde::{Deserialize, Serialize};
7use serde_big_array::BigArray;
8
9use crate::{BlockHeight, Hash, SignedTransaction, Timestamp};
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct BlockHeader {
14 pub parent_hash: Hash,
16 pub height: BlockHeight,
18 pub timestamp: Timestamp,
20 pub tx_root: Hash,
22 pub state_root: Hash,
24 pub proposer_pubkey: [u8; 32],
26 #[serde(with = "BigArray")]
28 pub proposer_sig: [u8; 64],
29}
30
31impl BlockHeader {
32 pub fn new(
34 parent_hash: Hash,
35 height: BlockHeight,
36 timestamp: Timestamp,
37 tx_root: Hash,
38 state_root: Hash,
39 proposer_pubkey: [u8; 32],
40 ) -> Self {
41 Self {
42 parent_hash,
43 height,
44 timestamp,
45 tx_root,
46 state_root,
47 proposer_pubkey,
48 proposer_sig: [0u8; 64], }
50 }
51
52 pub fn signing_hash(&self) -> Hash {
55 let signable = SignableHeader {
57 parent_hash: self.parent_hash,
58 height: self.height,
59 timestamp: self.timestamp,
60 tx_root: self.tx_root,
61 state_root: self.state_root,
62 proposer_pubkey: self.proposer_pubkey,
63 };
64 let bytes = bincode::serialize(&signable).expect("Header serialization should not fail");
65 Hash::hash(&bytes)
66 }
67
68 pub fn hash(&self) -> Hash {
71 let bytes = bincode::serialize(self).expect("Header serialization should not fail");
72 Hash::hash(&bytes)
73 }
74
75 pub fn is_genesis(&self) -> bool {
77 self.height == 0 && self.parent_hash.is_zero()
78 }
79
80 pub fn set_signature(&mut self, signature: [u8; 64]) {
82 self.proposer_sig = signature;
83 }
84}
85
86#[derive(Serialize)]
88struct SignableHeader {
89 parent_hash: Hash,
90 height: BlockHeight,
91 timestamp: Timestamp,
92 tx_root: Hash,
93 state_root: Hash,
94 proposer_pubkey: [u8; 32],
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
99pub struct Block {
100 pub header: BlockHeader,
102 pub transactions: Vec<SignedTransaction>,
104}
105
106impl Block {
107 pub fn new(header: BlockHeader, transactions: Vec<SignedTransaction>) -> Self {
109 Self {
110 header,
111 transactions,
112 }
113 }
114
115 pub fn genesis(state_root: Hash, proposer_pubkey: [u8; 32], timestamp: Timestamp) -> Self {
117 let header = BlockHeader::new(
118 Hash::ZERO,
119 0,
120 timestamp,
121 Hash::ZERO, state_root,
123 proposer_pubkey,
124 );
125
126 Self {
127 header,
128 transactions: Vec::new(),
129 }
130 }
131
132 pub fn hash(&self) -> Hash {
134 self.header.hash()
135 }
136
137 pub fn height(&self) -> BlockHeight {
139 self.header.height
140 }
141
142 pub fn compute_tx_root(&self) -> Hash {
144 if self.transactions.is_empty() {
145 return Hash::ZERO;
146 }
147
148 let tx_hashes: Vec<Hash> = self.transactions.iter().map(|tx| tx.hash()).collect();
149 Hash::merkle_root(&tx_hashes)
150 }
151
152 pub fn verify_tx_root(&self) -> bool {
154 self.header.tx_root == self.compute_tx_root()
155 }
156
157 pub fn to_bytes(&self) -> Vec<u8> {
159 bincode::serialize(self).expect("Block serialization should not fail")
160 }
161
162 pub fn from_bytes(bytes: &[u8]) -> Result<Self, bincode::Error> {
164 bincode::deserialize(bytes)
165 }
166
167 pub fn tx_count(&self) -> usize {
169 self.transactions.len()
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 fn sample_header() -> BlockHeader {
178 BlockHeader::new(
179 Hash::ZERO,
180 1,
181 1000,
182 Hash::ZERO,
183 Hash::hash(b"state"),
184 [0u8; 32],
185 )
186 }
187
188 #[test]
189 fn test_signing_hash_excludes_signature() {
190 let mut header1 = sample_header();
191 let mut header2 = sample_header();
192
193 header1.proposer_sig = [1u8; 64];
194 header2.proposer_sig = [2u8; 64];
195
196 assert_eq!(header1.signing_hash(), header2.signing_hash());
198 }
199
200 #[test]
201 fn test_block_hash_includes_signature() {
202 let mut header1 = sample_header();
203 let mut header2 = sample_header();
204
205 header1.proposer_sig = [1u8; 64];
206 header2.proposer_sig = [2u8; 64];
207
208 assert_ne!(header1.hash(), header2.hash());
210 }
211
212 #[test]
213 fn test_genesis_block() {
214 let genesis = Block::genesis(Hash::hash(b"genesis_state"), [0u8; 32], 0);
215 assert!(genesis.header.is_genesis());
216 assert_eq!(genesis.height(), 0);
217 assert!(genesis.transactions.is_empty());
218 }
219
220 #[test]
221 fn test_tx_root_empty() {
222 let block = Block::new(sample_header(), vec![]);
223 assert_eq!(block.compute_tx_root(), Hash::ZERO);
224 }
225
226 #[test]
227 fn test_serialization_roundtrip() {
228 let block = Block::genesis(Hash::hash(b"state"), [42u8; 32], 12345);
229 let bytes = block.to_bytes();
230 let block2 = Block::from_bytes(&bytes).unwrap();
231 assert_eq!(block, block2);
232 }
233}