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}