miden_protocol/block/header.rs
1use alloc::vec::Vec;
2
3use crate::account::AccountId;
4use crate::block::BlockNumber;
5use crate::crypto::dsa::ecdsa_k256_keccak::PublicKey;
6use crate::utils::serde::{
7 ByteReader,
8 ByteWriter,
9 Deserializable,
10 DeserializationError,
11 Serializable,
12};
13use crate::{Felt, Hasher, Word, ZERO};
14
15// BLOCK HEADER
16// ================================================================================================
17
18/// The header of a block. It contains metadata about the block, commitments to the current state of
19/// the chain and the hash of the proof that attests to the integrity of the chain.
20///
21/// A block header includes the following fields:
22///
23/// - `version` specifies the version of the protocol.
24/// - `prev_block_commitment` is the hash of the previous block header.
25/// - `block_num` is a unique sequential number of the current block.
26/// - `chain_commitment` is a commitment to an MMR of the entire chain where each block is a leaf.
27/// - `account_root` is a commitment to account database.
28/// - `nullifier_root` is a commitment to the nullifier database.
29/// - `note_root` is a commitment to all notes created in the current block.
30/// - `tx_commitment` is a commitment to the set of transaction IDs which affected accounts in the
31/// block.
32/// - `tx_kernel_commitment` a commitment to all transaction kernels supported by this block.
33/// - `validator_key` is the public key of the validator that is expected to sign the block.
34/// - `fee_parameters` are the parameters defining the base fees and the fee faucet ID, see
35/// [`FeeParameters`] for more details.
36/// - `timestamp` is the time when the block was created, in seconds since UNIX epoch. Current
37/// representation is sufficient to represent time up to year 2106.
38/// - `sub_commitment` is a sequential hash of all fields except the note_root.
39/// - `commitment` is a 2-to-1 hash of the sub_commitment and the note_root.
40#[derive(Debug, Eq, PartialEq, Clone)]
41pub struct BlockHeader {
42 version: u32,
43 prev_block_commitment: Word,
44 block_num: BlockNumber,
45 chain_commitment: Word,
46 account_root: Word,
47 nullifier_root: Word,
48 note_root: Word,
49 tx_commitment: Word,
50 tx_kernel_commitment: Word,
51 validator_key: PublicKey,
52 fee_parameters: FeeParameters,
53 timestamp: u32,
54 sub_commitment: Word,
55 commitment: Word,
56}
57
58impl BlockHeader {
59 /// Creates a new block header.
60 #[allow(clippy::too_many_arguments)]
61 pub fn new(
62 version: u32,
63 prev_block_commitment: Word,
64 block_num: BlockNumber,
65 chain_commitment: Word,
66 account_root: Word,
67 nullifier_root: Word,
68 note_root: Word,
69 tx_commitment: Word,
70 tx_kernel_commitment: Word,
71 validator_key: PublicKey,
72 fee_parameters: FeeParameters,
73 timestamp: u32,
74 ) -> Self {
75 // Compute block sub commitment.
76 let sub_commitment = Self::compute_sub_commitment(
77 version,
78 prev_block_commitment,
79 chain_commitment,
80 account_root,
81 nullifier_root,
82 tx_commitment,
83 tx_kernel_commitment,
84 &validator_key,
85 &fee_parameters,
86 timestamp,
87 block_num,
88 );
89
90 // The sub commitment is merged with the note_root - hash(sub_commitment, note_root) to
91 // produce the final hash. This is done to make the note_root easily accessible
92 // without having to unhash the entire header. Having the note_root easily
93 // accessible is useful when authenticating notes.
94 let commitment = Hasher::merge(&[sub_commitment, note_root]);
95
96 Self {
97 version,
98 prev_block_commitment,
99 block_num,
100 chain_commitment,
101 account_root,
102 nullifier_root,
103 note_root,
104 tx_commitment,
105 tx_kernel_commitment,
106 validator_key,
107 fee_parameters,
108 timestamp,
109 sub_commitment,
110 commitment,
111 }
112 }
113
114 // ACCESSORS
115 // --------------------------------------------------------------------------------------------
116
117 /// Returns the protocol version.
118 pub fn version(&self) -> u32 {
119 self.version
120 }
121
122 /// Returns the commitment of the block header.
123 pub fn commitment(&self) -> Word {
124 self.commitment
125 }
126
127 /// Returns the sub commitment of the block header.
128 ///
129 /// The sub commitment is a sequential hash of all block header fields except the note root.
130 /// This is used in the block commitment computation which is a 2-to-1 hash of the sub
131 /// commitment and the note root [hash(sub_commitment, note_root)]. This procedure is used to
132 /// make the note root easily accessible without having to unhash the entire header.
133 pub fn sub_commitment(&self) -> Word {
134 self.sub_commitment
135 }
136
137 /// Returns the commitment to the previous block header.
138 pub fn prev_block_commitment(&self) -> Word {
139 self.prev_block_commitment
140 }
141
142 /// Returns the block number.
143 pub fn block_num(&self) -> BlockNumber {
144 self.block_num
145 }
146
147 /// Returns the epoch to which this block belongs.
148 ///
149 /// This is the block number shifted right by [`BlockNumber::EPOCH_LENGTH_EXPONENT`].
150 pub fn block_epoch(&self) -> u16 {
151 self.block_num.block_epoch()
152 }
153
154 /// Returns the chain commitment.
155 pub fn chain_commitment(&self) -> Word {
156 self.chain_commitment
157 }
158
159 /// Returns the account database root.
160 pub fn account_root(&self) -> Word {
161 self.account_root
162 }
163
164 /// Returns the nullifier database root.
165 pub fn nullifier_root(&self) -> Word {
166 self.nullifier_root
167 }
168
169 /// Returns the note root.
170 pub fn note_root(&self) -> Word {
171 self.note_root
172 }
173
174 /// Returns the public key of the block's validator.
175 pub fn validator_key(&self) -> &PublicKey {
176 &self.validator_key
177 }
178
179 /// Returns the commitment to all transactions in this block.
180 ///
181 /// The commitment is computed as sequential hash of (`transaction_id`, `account_id`) tuples.
182 /// This makes it possible for the verifier to link transaction IDs to the accounts which
183 /// they were executed against.
184 pub fn tx_commitment(&self) -> Word {
185 self.tx_commitment
186 }
187
188 /// Returns the transaction kernel commitment.
189 ///
190 /// The transaction kernel commitment is computed as a sequential hash of all transaction kernel
191 /// hashes.
192 pub fn tx_kernel_commitment(&self) -> Word {
193 self.tx_kernel_commitment
194 }
195
196 /// Returns a reference to the [`FeeParameters`] in this header.
197 pub fn fee_parameters(&self) -> &FeeParameters {
198 &self.fee_parameters
199 }
200
201 /// Returns the timestamp at which the block was created, in seconds since UNIX epoch.
202 pub fn timestamp(&self) -> u32 {
203 self.timestamp
204 }
205
206 /// Returns the block number of the epoch block to which this block belongs.
207 pub fn epoch_block_num(&self) -> BlockNumber {
208 BlockNumber::from_epoch(self.block_epoch())
209 }
210
211 // HELPERS
212 // --------------------------------------------------------------------------------------------
213
214 /// Computes the sub commitment of the block header.
215 ///
216 /// The sub commitment is computed as a sequential hash of the following fields:
217 /// `prev_block_commitment`, `chain_commitment`, `account_root`, `nullifier_root`, `note_root`,
218 /// `tx_commitment`, `tx_kernel_commitment`, `validator_key_commitment`, `version`, `timestamp`,
219 /// `block_num`, `fee_faucet_id`, `verification_base_fee` (all fields except the `note_root`).
220 #[allow(clippy::too_many_arguments)]
221 fn compute_sub_commitment(
222 version: u32,
223 prev_block_commitment: Word,
224 chain_commitment: Word,
225 account_root: Word,
226 nullifier_root: Word,
227 tx_commitment: Word,
228 tx_kernel_commitment: Word,
229 validator_key: &PublicKey,
230 fee_parameters: &FeeParameters,
231 timestamp: u32,
232 block_num: BlockNumber,
233 ) -> Word {
234 let mut elements: Vec<Felt> = Vec::with_capacity(40);
235 elements.extend_from_slice(prev_block_commitment.as_elements());
236 elements.extend_from_slice(chain_commitment.as_elements());
237 elements.extend_from_slice(account_root.as_elements());
238 elements.extend_from_slice(nullifier_root.as_elements());
239 elements.extend_from_slice(tx_commitment.as_elements());
240 elements.extend_from_slice(tx_kernel_commitment.as_elements());
241 elements.extend(validator_key.to_commitment());
242 elements.extend([block_num.into(), Felt::from(version), Felt::from(timestamp), ZERO]);
243 elements.extend([
244 ZERO,
245 Felt::from(fee_parameters.verification_base_fee()),
246 fee_parameters.fee_faucet_id().suffix(),
247 fee_parameters.fee_faucet_id().prefix().as_felt(),
248 ]);
249 elements.extend([ZERO, ZERO, ZERO, ZERO]);
250 Hasher::hash_elements(&elements)
251 }
252}
253
254// SERIALIZATION
255// ================================================================================================
256
257impl Serializable for BlockHeader {
258 fn write_into<W: ByteWriter>(&self, target: &mut W) {
259 let Self {
260 version,
261 prev_block_commitment,
262 block_num,
263 chain_commitment,
264 account_root,
265 nullifier_root,
266 note_root,
267 tx_commitment,
268 tx_kernel_commitment,
269 validator_key,
270 fee_parameters,
271 timestamp,
272 // Don't serialize sub commitment and commitment as they can be derived.
273 sub_commitment: _,
274 commitment: _,
275 } = self;
276
277 version.write_into(target);
278 prev_block_commitment.write_into(target);
279 block_num.write_into(target);
280 chain_commitment.write_into(target);
281 account_root.write_into(target);
282 nullifier_root.write_into(target);
283 note_root.write_into(target);
284 tx_commitment.write_into(target);
285 tx_kernel_commitment.write_into(target);
286 validator_key.write_into(target);
287 fee_parameters.write_into(target);
288 timestamp.write_into(target);
289 }
290}
291
292impl Deserializable for BlockHeader {
293 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
294 let version = source.read()?;
295 let prev_block_commitment = source.read()?;
296 let block_num = source.read()?;
297 let chain_commitment = source.read()?;
298 let account_root = source.read()?;
299 let nullifier_root = source.read()?;
300 let note_root = source.read()?;
301 let tx_commitment = source.read()?;
302 let tx_kernel_commitment = source.read()?;
303 let validator_key = source.read()?;
304 let fee_parameters = source.read()?;
305 let timestamp = source.read()?;
306
307 Ok(Self::new(
308 version,
309 prev_block_commitment,
310 block_num,
311 chain_commitment,
312 account_root,
313 nullifier_root,
314 note_root,
315 tx_commitment,
316 tx_kernel_commitment,
317 validator_key,
318 fee_parameters,
319 timestamp,
320 ))
321 }
322}
323
324// FEE PARAMETERS
325// ================================================================================================
326
327/// The fee-related parameters of a block.
328///
329/// This defines how to compute the fees of a transaction and which asset fees can be paid in.
330///
331/// The fee asset is assumed to be a fungible asset
332/// ([`AssetComposition::Fungible`](crate::asset::AssetComposition::Fungible)).
333#[derive(Debug, Clone, PartialEq, Eq)]
334pub struct FeeParameters {
335 /// The [`AccountId`] of the faucet whose assets are accepted for fee payments in the
336 /// transaction kernel, or in other words, the fee faucet of the blockchain.
337 fee_faucet_id: AccountId,
338
339 /// The base fee (in base units) capturing the cost for the verification of a transaction.
340 verification_base_fee: u32,
341}
342
343impl FeeParameters {
344 // CONSTRUCTORS
345 // --------------------------------------------------------------------------------------------
346
347 /// Creates [`FeeParameters`] from the provided inputs.
348 pub fn new(fee_faucet_id: AccountId, verification_base_fee: u32) -> Self {
349 Self { fee_faucet_id, verification_base_fee }
350 }
351
352 // PUBLIC ACCESSORS
353 // --------------------------------------------------------------------------------------------
354
355 /// Returns the [`AccountId`] of the faucet whose assets are accepted for fee payments in the
356 /// transaction kernel, or in other words, the fee faucet of the blockchain.
357 pub fn fee_faucet_id(&self) -> AccountId {
358 self.fee_faucet_id
359 }
360
361 /// Returns the base fee capturing the cost for the verification of a transaction.
362 pub fn verification_base_fee(&self) -> u32 {
363 self.verification_base_fee
364 }
365}
366
367// SERIALIZATION
368// ================================================================================================
369
370impl Serializable for FeeParameters {
371 fn write_into<W: ByteWriter>(&self, target: &mut W) {
372 self.fee_faucet_id.write_into(target);
373 self.verification_base_fee.write_into(target);
374 }
375}
376
377impl Deserializable for FeeParameters {
378 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
379 let fee_faucet_id = source.read()?;
380 let verification_base_fee = source.read()?;
381
382 Ok(Self::new(fee_faucet_id, verification_base_fee))
383 }
384}
385
386// TESTS
387// ================================================================================================
388
389#[cfg(test)]
390mod tests {
391 use miden_core::Word;
392 use miden_crypto::rand::test_utils::rand_value;
393
394 use super::*;
395
396 #[test]
397 fn test_serde() {
398 let chain_commitment = rand_value::<Word>();
399 let note_root = rand_value::<Word>();
400 let tx_kernel_commitment = rand_value::<Word>();
401 let header = BlockHeader::mock(
402 0,
403 Some(chain_commitment),
404 Some(note_root),
405 &[],
406 tx_kernel_commitment,
407 );
408 let serialized = header.to_bytes();
409 let deserialized = BlockHeader::read_from_bytes(&serialized).unwrap();
410
411 assert_eq!(deserialized, header);
412 }
413}