miden_objects/block/
header.rs

1use alloc::vec::Vec;
2
3use super::{BlockNumber, Digest, Felt, Hasher, ZERO};
4use crate::utils::serde::{
5    ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
6};
7
8/// The header of a block. It contains metadata about the block, commitments to the current
9/// state of the chain and the hash of the proof that attests to the integrity of the chain.
10///
11/// A block header includes the following fields:
12///
13/// - `version` specifies the version of the protocol.
14/// - `prev_hash` is the hash of the previous block header.
15/// - `block_num` is a unique sequential number of the current block.
16/// - `chain_root` is a commitment to an MMR of the entire chain where each block is a leaf.
17/// - `account_root` is a commitment to account database.
18/// - `nullifier_root` is a commitment to the nullifier database.
19/// - `note_root` is a commitment to all notes created in the current block.
20/// - `tx_hash` is a commitment to a set of IDs of transactions which affected accounts in the
21///   block.
22/// - `kernel_root` a commitment to all transaction kernels supported by this block.
23/// - `proof_hash` is a hash of a STARK proof attesting to the correct state transition.
24/// - `timestamp` is the time when the block was created, in seconds since UNIX epoch. Current
25///   representation is sufficient to represent time up to year 2106.
26/// - `sub_hash` is a sequential hash of all fields except the note_root.
27/// - `hash` is a 2-to-1 hash of the sub_hash and the note_root.
28#[derive(Debug, Eq, PartialEq, Copy, Clone)]
29pub struct BlockHeader {
30    version: u32,
31    prev_hash: Digest,
32    block_num: BlockNumber,
33    chain_root: Digest,
34    account_root: Digest,
35    nullifier_root: Digest,
36    note_root: Digest,
37    tx_hash: Digest,
38    kernel_root: Digest,
39    proof_hash: Digest,
40    timestamp: u32,
41    sub_hash: Digest,
42    hash: Digest,
43}
44
45impl BlockHeader {
46    /// Creates a new block header.
47    #[allow(clippy::too_many_arguments)]
48    pub fn new(
49        version: u32,
50        prev_hash: Digest,
51        block_num: BlockNumber,
52        chain_root: Digest,
53        account_root: Digest,
54        nullifier_root: Digest,
55        note_root: Digest,
56        tx_hash: Digest,
57        kernel_root: Digest,
58        proof_hash: Digest,
59        timestamp: u32,
60    ) -> Self {
61        // compute block sub hash
62        let sub_hash = Self::compute_sub_hash(
63            version,
64            prev_hash,
65            chain_root,
66            account_root,
67            nullifier_root,
68            tx_hash,
69            kernel_root,
70            proof_hash,
71            timestamp,
72            block_num,
73        );
74
75        // The sub hash is merged with the note_root - hash(sub_hash, note_root) to produce the
76        // final hash. This is done to make the note_root easily accessible without having
77        // to unhash the entire header. Having the note_root easily accessible is useful
78        // when authenticating notes.
79        let hash = Hasher::merge(&[sub_hash, note_root]);
80
81        Self {
82            version,
83            prev_hash,
84            block_num,
85            chain_root,
86            account_root,
87            nullifier_root,
88            note_root,
89            tx_hash,
90            kernel_root,
91            proof_hash,
92            timestamp,
93            sub_hash,
94            hash,
95        }
96    }
97
98    // ACCESSORS
99    // --------------------------------------------------------------------------------------------
100
101    /// Returns the protocol version.
102    pub fn version(&self) -> u32 {
103        self.version
104    }
105
106    /// Returns the hash of the block header.
107    pub fn hash(&self) -> Digest {
108        self.hash
109    }
110
111    /// Returns the sub hash of the block header. The sub hash is a sequential hash of all block
112    /// header fields except the note root. This is used in the block hash computation which is a
113    /// 2-to-1 hash of the sub hash and the note root [hash(sub_hash, note_root)]. This procedure
114    /// is used to make the note root easily accessible without having to unhash the entire header.
115    pub fn sub_hash(&self) -> Digest {
116        self.sub_hash
117    }
118
119    /// Returns the hash of the previous block header.
120    pub fn prev_hash(&self) -> Digest {
121        self.prev_hash
122    }
123
124    /// Returns the block number.
125    pub fn block_num(&self) -> BlockNumber {
126        self.block_num
127    }
128
129    /// Returns the epoch to which this block belongs.
130    ///
131    /// This is the block number shifted right by [`BlockNumber::EPOCH_LENGTH_EXPONENT`].
132    pub fn block_epoch(&self) -> u16 {
133        self.block_num.block_epoch()
134    }
135
136    /// Returns the chain root.
137    pub fn chain_root(&self) -> Digest {
138        self.chain_root
139    }
140
141    /// Returns the account database root.
142    pub fn account_root(&self) -> Digest {
143        self.account_root
144    }
145
146    /// Returns the nullifier database root.
147    pub fn nullifier_root(&self) -> Digest {
148        self.nullifier_root
149    }
150
151    /// Returns the note root.
152    pub fn note_root(&self) -> Digest {
153        self.note_root
154    }
155
156    /// Returns the commitment to all transactions in this block.
157    ///
158    /// The commitment is computed as sequential hash of (`transaction_id`, `account_id`) tuples.
159    /// This makes it possible for the verifier to link transaction IDs to the accounts which
160    /// they were executed against.
161    pub fn tx_hash(&self) -> Digest {
162        self.tx_hash
163    }
164
165    /// Returns the kernel root.
166    ///
167    /// Kernel root is computed as a sequential hash of all kernel hashes.
168    pub fn kernel_root(&self) -> Digest {
169        self.kernel_root
170    }
171
172    /// Returns the proof hash.
173    pub fn proof_hash(&self) -> Digest {
174        self.proof_hash
175    }
176
177    /// Returns the timestamp at which the block was created, in seconds since UNIX epoch.
178    pub fn timestamp(&self) -> u32 {
179        self.timestamp
180    }
181
182    /// Returns the block number of the epoch block to which this block belongs.
183    pub fn epoch_block_num(&self) -> BlockNumber {
184        BlockNumber::from_epoch(self.block_epoch())
185    }
186
187    // HELPERS
188    // --------------------------------------------------------------------------------------------
189
190    /// Computes the sub hash of the block header.
191    ///
192    /// The sub hash is computed as a sequential hash of the following fields:
193    /// `prev_hash`, `chain_root`, `account_root`, `nullifier_root`, `note_root`, `tx_hash`,
194    /// `kernel_root`, `proof_hash`, `version`, `timestamp`, `block_num` (all fields except the
195    /// `note_root`).
196    #[allow(clippy::too_many_arguments)]
197    fn compute_sub_hash(
198        version: u32,
199        prev_hash: Digest,
200        chain_root: Digest,
201        account_root: Digest,
202        nullifier_root: Digest,
203        tx_hash: Digest,
204        kernel_root: Digest,
205        proof_hash: Digest,
206        timestamp: u32,
207        block_num: BlockNumber,
208    ) -> Digest {
209        let mut elements: Vec<Felt> = Vec::with_capacity(32);
210        elements.extend_from_slice(prev_hash.as_elements());
211        elements.extend_from_slice(chain_root.as_elements());
212        elements.extend_from_slice(account_root.as_elements());
213        elements.extend_from_slice(nullifier_root.as_elements());
214        elements.extend_from_slice(tx_hash.as_elements());
215        elements.extend_from_slice(kernel_root.as_elements());
216        elements.extend_from_slice(proof_hash.as_elements());
217        elements.extend([block_num.into(), version.into(), timestamp.into(), ZERO]);
218        Hasher::hash_elements(&elements)
219    }
220}
221
222// SERIALIZATION
223// ================================================================================================
224
225impl Serializable for BlockHeader {
226    fn write_into<W: ByteWriter>(&self, target: &mut W) {
227        self.version.write_into(target);
228        self.prev_hash.write_into(target);
229        self.block_num.write_into(target);
230        self.chain_root.write_into(target);
231        self.account_root.write_into(target);
232        self.nullifier_root.write_into(target);
233        self.note_root.write_into(target);
234        self.tx_hash.write_into(target);
235        self.kernel_root.write_into(target);
236        self.proof_hash.write_into(target);
237        self.timestamp.write_into(target);
238    }
239}
240
241impl Deserializable for BlockHeader {
242    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
243        let version = source.read()?;
244        let prev_hash = source.read()?;
245        let block_num = source.read()?;
246        let chain_root = source.read()?;
247        let account_root = source.read()?;
248        let nullifier_root = source.read()?;
249        let note_root = source.read()?;
250        let tx_hash = source.read()?;
251        let kernel_root = source.read()?;
252        let proof_hash = source.read()?;
253        let timestamp = source.read()?;
254
255        Ok(Self::new(
256            version,
257            prev_hash,
258            block_num,
259            chain_root,
260            account_root,
261            nullifier_root,
262            note_root,
263            tx_hash,
264            kernel_root,
265            proof_hash,
266            timestamp,
267        ))
268    }
269}
270
271#[cfg(test)]
272mod tests {
273    use vm_core::Word;
274    use winter_rand_utils::rand_array;
275
276    use super::*;
277
278    #[test]
279    fn test_serde() {
280        let chain_root: Word = rand_array();
281        let note_root: Word = rand_array();
282        let kernel_root: Word = rand_array();
283        let header = BlockHeader::mock(
284            0,
285            Some(chain_root.into()),
286            Some(note_root.into()),
287            &[],
288            kernel_root.into(),
289        );
290        let serialized = header.to_bytes();
291        let deserialized = BlockHeader::read_from_bytes(&serialized).unwrap();
292
293        assert_eq!(deserialized, header);
294    }
295}