Skip to main content

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}