1use alloc::vec::Vec;
2
3use crate::{
4 Digest, Felt, Hasher, ZERO,
5 account::AccountId,
6 block::BlockNumber,
7 transaction::TransactionId,
8 utils::serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
9};
10
11#[derive(Debug, Eq, PartialEq, Clone)]
33pub struct BlockHeader {
34 version: u32,
35 prev_block_commitment: Digest,
36 block_num: BlockNumber,
37 chain_commitment: Digest,
38 account_root: Digest,
39 nullifier_root: Digest,
40 note_root: Digest,
41 tx_commitment: Digest,
42 tx_kernel_commitment: Digest,
43 proof_commitment: Digest,
44 timestamp: u32,
45 sub_commitment: Digest,
46 commitment: Digest,
47}
48
49impl BlockHeader {
50 #[allow(clippy::too_many_arguments)]
52 pub fn new(
53 version: u32,
54 prev_block_commitment: Digest,
55 block_num: BlockNumber,
56 chain_commitment: Digest,
57 account_root: Digest,
58 nullifier_root: Digest,
59 note_root: Digest,
60 tx_commitment: Digest,
61 tx_kernel_commitment: Digest,
62 proof_commitment: Digest,
63 timestamp: u32,
64 ) -> Self {
65 let sub_commitment = Self::compute_sub_commitment(
67 version,
68 prev_block_commitment,
69 chain_commitment,
70 account_root,
71 nullifier_root,
72 tx_commitment,
73 tx_kernel_commitment,
74 proof_commitment,
75 timestamp,
76 block_num,
77 );
78
79 let commitment = Hasher::merge(&[sub_commitment, note_root]);
84
85 Self {
86 version,
87 prev_block_commitment,
88 block_num,
89 chain_commitment,
90 account_root,
91 nullifier_root,
92 note_root,
93 tx_commitment,
94 tx_kernel_commitment,
95 proof_commitment,
96 timestamp,
97 sub_commitment,
98 commitment,
99 }
100 }
101
102 pub fn version(&self) -> u32 {
107 self.version
108 }
109
110 pub fn commitment(&self) -> Digest {
112 self.commitment
113 }
114
115 pub fn sub_commitment(&self) -> Digest {
122 self.sub_commitment
123 }
124
125 pub fn prev_block_commitment(&self) -> Digest {
127 self.prev_block_commitment
128 }
129
130 pub fn block_num(&self) -> BlockNumber {
132 self.block_num
133 }
134
135 pub fn block_epoch(&self) -> u16 {
139 self.block_num.block_epoch()
140 }
141
142 pub fn chain_commitment(&self) -> Digest {
144 self.chain_commitment
145 }
146
147 pub fn account_root(&self) -> Digest {
149 self.account_root
150 }
151
152 pub fn nullifier_root(&self) -> Digest {
154 self.nullifier_root
155 }
156
157 pub fn note_root(&self) -> Digest {
159 self.note_root
160 }
161
162 pub fn tx_commitment(&self) -> Digest {
168 self.tx_commitment
169 }
170
171 pub fn tx_kernel_commitment(&self) -> Digest {
176 self.tx_kernel_commitment
177 }
178
179 pub fn proof_commitment(&self) -> Digest {
181 self.proof_commitment
182 }
183
184 pub fn timestamp(&self) -> u32 {
186 self.timestamp
187 }
188
189 pub fn epoch_block_num(&self) -> BlockNumber {
191 BlockNumber::from_epoch(self.block_epoch())
192 }
193
194 #[allow(clippy::too_many_arguments)]
204 fn compute_sub_commitment(
205 version: u32,
206 prev_block_commitment: Digest,
207 chain_commitment: Digest,
208 account_root: Digest,
209 nullifier_root: Digest,
210 tx_commitment: Digest,
211 tx_kernel_commitment: Digest,
212 proof_commitment: Digest,
213 timestamp: u32,
214 block_num: BlockNumber,
215 ) -> Digest {
216 let mut elements: Vec<Felt> = Vec::with_capacity(32);
217 elements.extend_from_slice(prev_block_commitment.as_elements());
218 elements.extend_from_slice(chain_commitment.as_elements());
219 elements.extend_from_slice(account_root.as_elements());
220 elements.extend_from_slice(nullifier_root.as_elements());
221 elements.extend_from_slice(tx_commitment.as_elements());
222 elements.extend_from_slice(tx_kernel_commitment.as_elements());
223 elements.extend_from_slice(proof_commitment.as_elements());
224 elements.extend([block_num.into(), version.into(), timestamp.into(), ZERO]);
225 Hasher::hash_elements(&elements)
226 }
227
228 pub fn compute_tx_commitment(
230 updated_accounts: impl Iterator<Item = (TransactionId, AccountId)>,
231 ) -> Digest {
232 let mut elements = vec![];
233 for (transaction_id, account_id) in updated_accounts {
234 let [account_id_prefix, account_id_suffix] = <[Felt; 2]>::from(account_id);
235 elements.extend_from_slice(transaction_id.as_elements());
236 elements.extend_from_slice(&[account_id_prefix, account_id_suffix, ZERO, ZERO]);
237 }
238
239 Hasher::hash_elements(&elements)
240 }
241}
242
243impl Serializable for BlockHeader {
247 fn write_into<W: ByteWriter>(&self, target: &mut W) {
248 self.version.write_into(target);
249 self.prev_block_commitment.write_into(target);
250 self.block_num.write_into(target);
251 self.chain_commitment.write_into(target);
252 self.account_root.write_into(target);
253 self.nullifier_root.write_into(target);
254 self.note_root.write_into(target);
255 self.tx_commitment.write_into(target);
256 self.tx_kernel_commitment.write_into(target);
257 self.proof_commitment.write_into(target);
258 self.timestamp.write_into(target);
259 }
260}
261
262impl Deserializable for BlockHeader {
263 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
264 let version = source.read()?;
265 let prev_block_commitment = source.read()?;
266 let block_num = source.read()?;
267 let chain_commitment = source.read()?;
268 let account_root = source.read()?;
269 let nullifier_root = source.read()?;
270 let note_root = source.read()?;
271 let tx_commitment = source.read()?;
272 let tx_kernel_commitment = source.read()?;
273 let proof_commitment = source.read()?;
274 let timestamp = source.read()?;
275
276 Ok(Self::new(
277 version,
278 prev_block_commitment,
279 block_num,
280 chain_commitment,
281 account_root,
282 nullifier_root,
283 note_root,
284 tx_commitment,
285 tx_kernel_commitment,
286 proof_commitment,
287 timestamp,
288 ))
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use vm_core::Word;
295 use winter_rand_utils::rand_array;
296
297 use super::*;
298
299 #[test]
300 fn test_serde() {
301 let chain_commitment: Word = rand_array();
302 let note_root: Word = rand_array();
303 let tx_kernel_commitment: Word = rand_array();
304 let header = BlockHeader::mock(
305 0,
306 Some(chain_commitment.into()),
307 Some(note_root.into()),
308 &[],
309 tx_kernel_commitment.into(),
310 );
311 let serialized = header.to_bytes();
312 let deserialized = BlockHeader::read_from_bytes(&serialized).unwrap();
313
314 assert_eq!(deserialized, header);
315 }
316}