miden_objects/block/
mod.rs1use alloc::{collections::BTreeSet, string::ToString, vec::Vec};
2
3use super::{
4 Digest, Felt, Hasher, MAX_ACCOUNTS_PER_BLOCK, MAX_BATCHES_PER_BLOCK, MAX_INPUT_NOTES_PER_BLOCK,
5 MAX_OUTPUT_NOTES_PER_BATCH, MAX_OUTPUT_NOTES_PER_BLOCK, ZERO,
6};
7
8mod header;
9pub use header::BlockHeader;
10mod block_number;
11pub use block_number::BlockNumber;
12mod note_tree;
13pub use note_tree::{BlockNoteIndex, BlockNoteTree};
14
15use crate::{
16 account::{delta::AccountUpdateDetails, AccountId},
17 errors::BlockError,
18 note::Nullifier,
19 transaction::{OutputNote, TransactionId},
20 utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
21};
22
23pub type NoteBatch = Vec<OutputNote>;
24
25#[derive(Debug, Clone)]
45pub struct Block {
46 header: BlockHeader,
48
49 updated_accounts: Vec<BlockAccountUpdate>,
51
52 output_note_batches: Vec<NoteBatch>,
54
55 nullifiers: Vec<Nullifier>,
57 }
60
61impl Block {
62 pub fn new(
69 header: BlockHeader,
70 updated_accounts: Vec<BlockAccountUpdate>,
71 output_note_batches: Vec<NoteBatch>,
72 nullifiers: Vec<Nullifier>,
73 ) -> Result<Self, BlockError> {
74 let block = Self {
75 header,
76 updated_accounts,
77 output_note_batches,
78 nullifiers,
79 };
80
81 block.validate()?;
82
83 Ok(block)
84 }
85
86 pub fn hash(&self) -> Digest {
88 self.header.hash()
89 }
90
91 pub fn header(&self) -> BlockHeader {
93 self.header
94 }
95
96 pub fn updated_accounts(&self) -> &[BlockAccountUpdate] {
98 &self.updated_accounts
99 }
100
101 pub fn output_note_batches(&self) -> &[NoteBatch] {
103 &self.output_note_batches
104 }
105
106 pub fn notes(&self) -> impl Iterator<Item = (BlockNoteIndex, &OutputNote)> {
111 self.output_note_batches.iter().enumerate().flat_map(|(batch_idx, notes)| {
112 notes.iter().enumerate().map(move |(note_idx_in_batch, note)| {
113 (
114 BlockNoteIndex::new(batch_idx, note_idx_in_batch).expect(
115 "Something went wrong: block is invalid, but passed or skipped validation",
116 ),
117 note,
118 )
119 })
120 })
121 }
122
123 pub fn build_note_tree(&self) -> BlockNoteTree {
125 let entries =
126 self.notes().map(|(note_index, note)| (note_index, note.id(), *note.metadata()));
127
128 BlockNoteTree::with_entries(entries)
129 .expect("Something went wrong: block is invalid, but passed or skipped validation")
130 }
131
132 pub fn nullifiers(&self) -> &[Nullifier] {
134 &self.nullifiers
135 }
136
137 pub fn transactions(&self) -> impl Iterator<Item = (TransactionId, AccountId)> + '_ {
140 self.updated_accounts.iter().flat_map(|update| {
141 update
142 .transactions
143 .iter()
144 .map(|transaction_id| (*transaction_id, update.account_id))
145 })
146 }
147
148 pub fn compute_tx_hash(&self) -> Digest {
150 compute_tx_hash(self.transactions())
151 }
152
153 fn validate(&self) -> Result<(), BlockError> {
157 let account_count = self.updated_accounts.len();
158 if account_count > MAX_ACCOUNTS_PER_BLOCK {
159 return Err(BlockError::TooManyAccountUpdates(account_count));
160 }
161
162 let batch_count = self.output_note_batches.len();
163 if batch_count > MAX_BATCHES_PER_BLOCK {
164 return Err(BlockError::TooManyTransactionBatches(batch_count));
165 }
166
167 let nullifier_count = self.nullifiers.len();
170 if nullifier_count > MAX_INPUT_NOTES_PER_BLOCK {
171 return Err(BlockError::TooManyNullifiersInBlock(nullifier_count));
172 }
173
174 let mut output_notes = BTreeSet::new();
175 let mut output_note_count = 0;
176 for batch in self.output_note_batches.iter() {
177 if batch.len() > MAX_OUTPUT_NOTES_PER_BATCH {
181 return Err(BlockError::TooManyNotesInBatch(batch.len()));
182 }
183 output_note_count += batch.len();
184 for note in batch.iter() {
185 if !output_notes.insert(note.id()) {
186 return Err(BlockError::DuplicateNoteFound(note.id()));
187 }
188 }
189 }
190
191 if output_note_count > MAX_OUTPUT_NOTES_PER_BLOCK {
192 return Err(BlockError::TooManyNotesInBlock(output_note_count));
193 }
194
195 Ok(())
196 }
197}
198
199impl Serializable for Block {
200 fn write_into<W: ByteWriter>(&self, target: &mut W) {
201 self.header.write_into(target);
202 self.updated_accounts.write_into(target);
203 self.output_note_batches.write_into(target);
204 self.nullifiers.write_into(target);
205 }
206}
207
208impl Deserializable for Block {
209 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
210 let block = Self {
211 header: BlockHeader::read_from(source)?,
212 updated_accounts: <Vec<BlockAccountUpdate>>::read_from(source)?,
213 output_note_batches: <Vec<NoteBatch>>::read_from(source)?,
214 nullifiers: <Vec<Nullifier>>::read_from(source)?,
215 };
216
217 block
218 .validate()
219 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
220
221 Ok(block)
222 }
223}
224
225pub fn compute_tx_hash(
230 updated_accounts: impl Iterator<Item = (TransactionId, AccountId)>,
231) -> Digest {
232 let mut elements = vec![];
233 for (transaction_id, account_id) in updated_accounts {
234 let account_id_felts: [Felt; 2] = account_id.into();
235 elements.extend_from_slice(&[account_id_felts[0], account_id_felts[1], ZERO, ZERO]);
236 elements.extend_from_slice(transaction_id.as_elements());
237 }
238
239 Hasher::hash_elements(&elements)
240}
241
242#[derive(Debug, Clone, PartialEq, Eq)]
248pub struct BlockAccountUpdate {
249 account_id: AccountId,
251
252 new_state_hash: Digest,
254
255 details: AccountUpdateDetails,
259
260 transactions: Vec<TransactionId>,
262}
263
264impl BlockAccountUpdate {
265 pub const fn new(
267 account_id: AccountId,
268 new_state_hash: Digest,
269 details: AccountUpdateDetails,
270 transactions: Vec<TransactionId>,
271 ) -> Self {
272 Self {
273 account_id,
274 new_state_hash,
275 details,
276 transactions,
277 }
278 }
279
280 pub fn account_id(&self) -> AccountId {
282 self.account_id
283 }
284
285 pub fn new_state_hash(&self) -> Digest {
287 self.new_state_hash
288 }
289
290 pub fn details(&self) -> &AccountUpdateDetails {
295 &self.details
296 }
297
298 pub fn transactions(&self) -> &[TransactionId] {
300 &self.transactions
301 }
302
303 pub fn is_private(&self) -> bool {
305 self.details.is_private()
306 }
307}
308
309impl Serializable for BlockAccountUpdate {
310 fn write_into<W: ByteWriter>(&self, target: &mut W) {
311 self.account_id.write_into(target);
312 self.new_state_hash.write_into(target);
313 self.details.write_into(target);
314 self.transactions.write_into(target);
315 }
316}
317
318impl Deserializable for BlockAccountUpdate {
319 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
320 Ok(Self {
321 account_id: AccountId::read_from(source)?,
322 new_state_hash: Digest::read_from(source)?,
323 details: AccountUpdateDetails::read_from(source)?,
324 transactions: Vec::<TransactionId>::read_from(source)?,
325 })
326 }
327}