1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use crate::account::{AccountId, AccountType};
5use crate::block::BlockNumber;
6use crate::crypto::dsa::ecdsa_k256_keccak::PublicKey;
7use crate::errors::FeeError;
8use crate::utils::serde::{
9 ByteReader,
10 ByteWriter,
11 Deserializable,
12 DeserializationError,
13 Serializable,
14};
15use crate::{Felt, Hasher, Word, ZERO};
16
17#[derive(Debug, Eq, PartialEq, Clone)]
43pub struct BlockHeader {
44 version: u32,
45 prev_block_commitment: Word,
46 block_num: BlockNumber,
47 chain_commitment: Word,
48 account_root: Word,
49 nullifier_root: Word,
50 note_root: Word,
51 tx_commitment: Word,
52 tx_kernel_commitment: Word,
53 validator_key: PublicKey,
54 fee_parameters: FeeParameters,
55 timestamp: u32,
56 sub_commitment: Word,
57 commitment: Word,
58}
59
60impl BlockHeader {
61 #[allow(clippy::too_many_arguments)]
63 pub fn new(
64 version: u32,
65 prev_block_commitment: Word,
66 block_num: BlockNumber,
67 chain_commitment: Word,
68 account_root: Word,
69 nullifier_root: Word,
70 note_root: Word,
71 tx_commitment: Word,
72 tx_kernel_commitment: Word,
73 validator_key: PublicKey,
74 fee_parameters: FeeParameters,
75 timestamp: u32,
76 ) -> Self {
77 let sub_commitment = Self::compute_sub_commitment(
79 version,
80 prev_block_commitment,
81 chain_commitment,
82 account_root,
83 nullifier_root,
84 tx_commitment,
85 tx_kernel_commitment,
86 &validator_key,
87 &fee_parameters,
88 timestamp,
89 block_num,
90 );
91
92 let commitment = Hasher::merge(&[sub_commitment, note_root]);
97
98 Self {
99 version,
100 prev_block_commitment,
101 block_num,
102 chain_commitment,
103 account_root,
104 nullifier_root,
105 note_root,
106 tx_commitment,
107 tx_kernel_commitment,
108 validator_key,
109 fee_parameters,
110 timestamp,
111 sub_commitment,
112 commitment,
113 }
114 }
115
116 pub fn version(&self) -> u32 {
121 self.version
122 }
123
124 pub fn commitment(&self) -> Word {
126 self.commitment
127 }
128
129 pub fn sub_commitment(&self) -> Word {
136 self.sub_commitment
137 }
138
139 pub fn prev_block_commitment(&self) -> Word {
141 self.prev_block_commitment
142 }
143
144 pub fn block_num(&self) -> BlockNumber {
146 self.block_num
147 }
148
149 pub fn block_epoch(&self) -> u16 {
153 self.block_num.block_epoch()
154 }
155
156 pub fn chain_commitment(&self) -> Word {
158 self.chain_commitment
159 }
160
161 pub fn account_root(&self) -> Word {
163 self.account_root
164 }
165
166 pub fn nullifier_root(&self) -> Word {
168 self.nullifier_root
169 }
170
171 pub fn note_root(&self) -> Word {
173 self.note_root
174 }
175
176 pub fn validator_key(&self) -> &PublicKey {
178 &self.validator_key
179 }
180
181 pub fn tx_commitment(&self) -> Word {
187 self.tx_commitment
188 }
189
190 pub fn tx_kernel_commitment(&self) -> Word {
195 self.tx_kernel_commitment
196 }
197
198 pub fn fee_parameters(&self) -> &FeeParameters {
200 &self.fee_parameters
201 }
202
203 pub fn timestamp(&self) -> u32 {
205 self.timestamp
206 }
207
208 pub fn epoch_block_num(&self) -> BlockNumber {
210 BlockNumber::from_epoch(self.block_epoch())
211 }
212
213 #[allow(clippy::too_many_arguments)]
223 fn compute_sub_commitment(
224 version: u32,
225 prev_block_commitment: Word,
226 chain_commitment: Word,
227 account_root: Word,
228 nullifier_root: Word,
229 tx_commitment: Word,
230 tx_kernel_commitment: Word,
231 validator_key: &PublicKey,
232 fee_parameters: &FeeParameters,
233 timestamp: u32,
234 block_num: BlockNumber,
235 ) -> Word {
236 let mut elements: Vec<Felt> = Vec::with_capacity(40);
237 elements.extend_from_slice(prev_block_commitment.as_elements());
238 elements.extend_from_slice(chain_commitment.as_elements());
239 elements.extend_from_slice(account_root.as_elements());
240 elements.extend_from_slice(nullifier_root.as_elements());
241 elements.extend_from_slice(tx_commitment.as_elements());
242 elements.extend_from_slice(tx_kernel_commitment.as_elements());
243 elements.extend(validator_key.to_commitment());
244 elements.extend([block_num.into(), version.into(), timestamp.into(), ZERO]);
245 elements.extend([
246 fee_parameters.native_asset_id().suffix(),
247 fee_parameters.native_asset_id().prefix().as_felt(),
248 fee_parameters.verification_base_fee().into(),
249 ZERO,
250 ]);
251 elements.extend([ZERO, ZERO, ZERO, ZERO]);
252 Hasher::hash_elements(&elements)
253 }
254}
255
256impl Serializable for BlockHeader {
260 fn write_into<W: ByteWriter>(&self, target: &mut W) {
261 let Self {
262 version,
263 prev_block_commitment,
264 block_num,
265 chain_commitment,
266 account_root,
267 nullifier_root,
268 note_root,
269 tx_commitment,
270 tx_kernel_commitment,
271 validator_key,
272 fee_parameters,
273 timestamp,
274 sub_commitment: _,
276 commitment: _,
277 } = self;
278
279 version.write_into(target);
280 prev_block_commitment.write_into(target);
281 block_num.write_into(target);
282 chain_commitment.write_into(target);
283 account_root.write_into(target);
284 nullifier_root.write_into(target);
285 note_root.write_into(target);
286 tx_commitment.write_into(target);
287 tx_kernel_commitment.write_into(target);
288 validator_key.write_into(target);
289 fee_parameters.write_into(target);
290 timestamp.write_into(target);
291 }
292}
293
294impl Deserializable for BlockHeader {
295 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
296 let version = source.read()?;
297 let prev_block_commitment = source.read()?;
298 let block_num = source.read()?;
299 let chain_commitment = source.read()?;
300 let account_root = source.read()?;
301 let nullifier_root = source.read()?;
302 let note_root = source.read()?;
303 let tx_commitment = source.read()?;
304 let tx_kernel_commitment = source.read()?;
305 let validator_key = source.read()?;
306 let fee_parameters = source.read()?;
307 let timestamp = source.read()?;
308
309 Ok(Self::new(
310 version,
311 prev_block_commitment,
312 block_num,
313 chain_commitment,
314 account_root,
315 nullifier_root,
316 note_root,
317 tx_commitment,
318 tx_kernel_commitment,
319 validator_key,
320 fee_parameters,
321 timestamp,
322 ))
323 }
324}
325
326#[derive(Debug, Clone, PartialEq, Eq)]
333pub struct FeeParameters {
334 native_asset_id: AccountId,
337 verification_base_fee: u32,
339}
340
341impl FeeParameters {
342 pub fn new(native_asset_id: AccountId, verification_base_fee: u32) -> Result<Self, FeeError> {
352 if !matches!(native_asset_id.account_type(), AccountType::FungibleFaucet) {
353 return Err(FeeError::NativeAssetIdNotFungible {
354 account_type: native_asset_id.account_type(),
355 });
356 }
357
358 Ok(Self { native_asset_id, verification_base_fee })
359 }
360
361 pub fn native_asset_id(&self) -> AccountId {
367 self.native_asset_id
368 }
369
370 pub fn verification_base_fee(&self) -> u32 {
372 self.verification_base_fee
373 }
374}
375
376impl Serializable for FeeParameters {
380 fn write_into<W: ByteWriter>(&self, target: &mut W) {
381 self.native_asset_id.write_into(target);
382 self.verification_base_fee.write_into(target);
383 }
384}
385
386impl Deserializable for FeeParameters {
387 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
388 let native_asset_id = source.read()?;
389 let verification_base_fee = source.read()?;
390
391 Self::new(native_asset_id, verification_base_fee)
392 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
393 }
394}
395
396#[cfg(test)]
400mod tests {
401 use assert_matches::assert_matches;
402 use miden_core::Word;
403 use winter_rand_utils::rand_value;
404
405 use super::*;
406 use crate::testing::account_id::ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET;
407
408 #[test]
409 fn test_serde() {
410 let chain_commitment = rand_value::<Word>();
411 let note_root = rand_value::<Word>();
412 let tx_kernel_commitment = rand_value::<Word>();
413 let header = BlockHeader::mock(
414 0,
415 Some(chain_commitment),
416 Some(note_root),
417 &[],
418 tx_kernel_commitment,
419 );
420 let serialized = header.to_bytes();
421 let deserialized = BlockHeader::read_from_bytes(&serialized).unwrap();
422
423 assert_eq!(deserialized, header);
424 }
425
426 #[test]
429 fn fee_parameters_fail_when_native_asset_is_not_fungible() {
430 assert_matches!(
431 FeeParameters::new(ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET.try_into().unwrap(), 0)
432 .unwrap_err(),
433 FeeError::NativeAssetIdNotFungible { .. }
434 );
435 }
436}