1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use crate::account::{AccountId, AccountType};
5use crate::block::BlockNumber;
6use crate::utils::serde::{
7 ByteReader,
8 ByteWriter,
9 Deserializable,
10 DeserializationError,
11 Serializable,
12};
13use crate::{FeeError, Felt, Hasher, Word, ZERO};
14
15#[derive(Debug, Eq, PartialEq, Clone)]
39pub struct BlockHeader {
40 version: u32,
41 prev_block_commitment: Word,
42 block_num: BlockNumber,
43 chain_commitment: Word,
44 account_root: Word,
45 nullifier_root: Word,
46 note_root: Word,
47 tx_commitment: Word,
48 tx_kernel_commitment: Word,
49 proof_commitment: Word,
50 fee_parameters: FeeParameters,
51 timestamp: u32,
52 sub_commitment: Word,
53 commitment: Word,
54}
55
56impl BlockHeader {
57 #[allow(clippy::too_many_arguments)]
59 pub fn new(
60 version: u32,
61 prev_block_commitment: Word,
62 block_num: BlockNumber,
63 chain_commitment: Word,
64 account_root: Word,
65 nullifier_root: Word,
66 note_root: Word,
67 tx_commitment: Word,
68 tx_kernel_commitment: Word,
69 proof_commitment: Word,
70 fee_parameters: FeeParameters,
71 timestamp: u32,
72 ) -> Self {
73 let sub_commitment = Self::compute_sub_commitment(
75 version,
76 prev_block_commitment,
77 chain_commitment,
78 account_root,
79 nullifier_root,
80 tx_commitment,
81 tx_kernel_commitment,
82 proof_commitment,
83 &fee_parameters,
84 timestamp,
85 block_num,
86 );
87
88 let commitment = Hasher::merge(&[sub_commitment, note_root]);
93
94 Self {
95 version,
96 prev_block_commitment,
97 block_num,
98 chain_commitment,
99 account_root,
100 nullifier_root,
101 note_root,
102 tx_commitment,
103 tx_kernel_commitment,
104 proof_commitment,
105 fee_parameters,
106 timestamp,
107 sub_commitment,
108 commitment,
109 }
110 }
111
112 pub fn version(&self) -> u32 {
117 self.version
118 }
119
120 pub fn commitment(&self) -> Word {
122 self.commitment
123 }
124
125 pub fn sub_commitment(&self) -> Word {
132 self.sub_commitment
133 }
134
135 pub fn prev_block_commitment(&self) -> Word {
137 self.prev_block_commitment
138 }
139
140 pub fn block_num(&self) -> BlockNumber {
142 self.block_num
143 }
144
145 pub fn block_epoch(&self) -> u16 {
149 self.block_num.block_epoch()
150 }
151
152 pub fn chain_commitment(&self) -> Word {
154 self.chain_commitment
155 }
156
157 pub fn account_root(&self) -> Word {
159 self.account_root
160 }
161
162 pub fn nullifier_root(&self) -> Word {
164 self.nullifier_root
165 }
166
167 pub fn note_root(&self) -> Word {
169 self.note_root
170 }
171
172 pub fn tx_commitment(&self) -> Word {
178 self.tx_commitment
179 }
180
181 pub fn tx_kernel_commitment(&self) -> Word {
186 self.tx_kernel_commitment
187 }
188
189 pub fn proof_commitment(&self) -> Word {
191 self.proof_commitment
192 }
193
194 pub fn fee_parameters(&self) -> &FeeParameters {
196 &self.fee_parameters
197 }
198
199 pub fn timestamp(&self) -> u32 {
201 self.timestamp
202 }
203
204 pub fn epoch_block_num(&self) -> BlockNumber {
206 BlockNumber::from_epoch(self.block_epoch())
207 }
208
209 #[allow(clippy::too_many_arguments)]
219 fn compute_sub_commitment(
220 version: u32,
221 prev_block_commitment: Word,
222 chain_commitment: Word,
223 account_root: Word,
224 nullifier_root: Word,
225 tx_commitment: Word,
226 tx_kernel_commitment: Word,
227 proof_commitment: Word,
228 fee_parameters: &FeeParameters,
229 timestamp: u32,
230 block_num: BlockNumber,
231 ) -> Word {
232 let mut elements: Vec<Felt> = Vec::with_capacity(40);
233 elements.extend_from_slice(prev_block_commitment.as_elements());
234 elements.extend_from_slice(chain_commitment.as_elements());
235 elements.extend_from_slice(account_root.as_elements());
236 elements.extend_from_slice(nullifier_root.as_elements());
237 elements.extend_from_slice(tx_commitment.as_elements());
238 elements.extend_from_slice(tx_kernel_commitment.as_elements());
239 elements.extend_from_slice(proof_commitment.as_elements());
240 elements.extend([block_num.into(), version.into(), timestamp.into(), ZERO]);
241 elements.extend([
242 fee_parameters.native_asset_id().suffix(),
243 fee_parameters.native_asset_id().prefix().as_felt(),
244 fee_parameters.verification_base_fee().into(),
245 ZERO,
246 ]);
247 elements.extend([ZERO, ZERO, ZERO, ZERO]);
248 Hasher::hash_elements(&elements)
249 }
250}
251
252impl Serializable for BlockHeader {
256 fn write_into<W: ByteWriter>(&self, target: &mut W) {
257 self.version.write_into(target);
258 self.prev_block_commitment.write_into(target);
259 self.block_num.write_into(target);
260 self.chain_commitment.write_into(target);
261 self.account_root.write_into(target);
262 self.nullifier_root.write_into(target);
263 self.note_root.write_into(target);
264 self.tx_commitment.write_into(target);
265 self.tx_kernel_commitment.write_into(target);
266 self.proof_commitment.write_into(target);
267 self.fee_parameters.write_into(target);
268 self.timestamp.write_into(target);
269 }
270}
271
272impl Deserializable for BlockHeader {
273 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
274 let version = source.read()?;
275 let prev_block_commitment = source.read()?;
276 let block_num = source.read()?;
277 let chain_commitment = source.read()?;
278 let account_root = source.read()?;
279 let nullifier_root = source.read()?;
280 let note_root = source.read()?;
281 let tx_commitment = source.read()?;
282 let tx_kernel_commitment = source.read()?;
283 let proof_commitment = source.read()?;
284 let fee_parameters = source.read()?;
285 let timestamp = source.read()?;
286
287 Ok(Self::new(
288 version,
289 prev_block_commitment,
290 block_num,
291 chain_commitment,
292 account_root,
293 nullifier_root,
294 note_root,
295 tx_commitment,
296 tx_kernel_commitment,
297 proof_commitment,
298 fee_parameters,
299 timestamp,
300 ))
301 }
302}
303
304#[derive(Debug, Clone, PartialEq, Eq)]
311pub struct FeeParameters {
312 native_asset_id: AccountId,
315 verification_base_fee: u32,
317}
318
319impl FeeParameters {
320 pub fn new(native_asset_id: AccountId, verification_base_fee: u32) -> Result<Self, FeeError> {
330 if !matches!(native_asset_id.account_type(), AccountType::FungibleFaucet) {
331 return Err(FeeError::NativeAssetIdNotFungible {
332 account_type: native_asset_id.account_type(),
333 });
334 }
335
336 Ok(Self { native_asset_id, verification_base_fee })
337 }
338
339 pub fn native_asset_id(&self) -> AccountId {
345 self.native_asset_id
346 }
347
348 pub fn verification_base_fee(&self) -> u32 {
350 self.verification_base_fee
351 }
352}
353
354impl Serializable for FeeParameters {
358 fn write_into<W: ByteWriter>(&self, target: &mut W) {
359 self.native_asset_id.write_into(target);
360 self.verification_base_fee.write_into(target);
361 }
362}
363
364impl Deserializable for FeeParameters {
365 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
366 let native_asset_id = source.read()?;
367 let verification_base_fee = source.read()?;
368
369 Self::new(native_asset_id, verification_base_fee)
370 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
371 }
372}
373#[cfg(test)]
377mod tests {
378 use assert_matches::assert_matches;
379 use miden_core::Word;
380 use winter_rand_utils::rand_value;
381
382 use super::*;
383 use crate::testing::account_id::ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET;
384
385 #[test]
386 fn test_serde() {
387 let chain_commitment = rand_value::<Word>();
388 let note_root = rand_value::<Word>();
389 let tx_kernel_commitment = rand_value::<Word>();
390 let header = BlockHeader::mock(
391 0,
392 Some(chain_commitment),
393 Some(note_root),
394 &[],
395 tx_kernel_commitment,
396 );
397 let serialized = header.to_bytes();
398 let deserialized = BlockHeader::read_from_bytes(&serialized).unwrap();
399
400 assert_eq!(deserialized, header);
401 }
402
403 #[test]
406 fn fee_parameters_fail_when_native_asset_is_not_fungible() {
407 assert_matches!(
408 FeeParameters::new(ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET.try_into().unwrap(), 0)
409 .unwrap_err(),
410 FeeError::NativeAssetIdNotFungible { .. }
411 );
412 }
413}