1use std::sync::Arc;
2
3use miden_node_proto::domain::proof_request::BlockProofRequest;
4use miden_node_utils::ErrorReport;
5use miden_node_utils::tracing::{miden_instrument, miden_span_record};
6use miden_protocol::Word;
7use miden_protocol::account::delta::AccountUpdateDetails;
8use miden_protocol::batch::OrderedBatches;
9use miden_protocol::block::account_tree::AccountMutationSet;
10use miden_protocol::block::nullifier_tree::NullifierMutationSet;
11use miden_protocol::block::{BlockBody, BlockHeader, BlockInputs, BlockNumber, SignedBlock};
12use miden_protocol::note::{NoteDetails, Nullifier};
13use miden_protocol::transaction::OutputNote;
14use miden_protocol::utils::serde::Serializable;
15use tokio::sync::oneshot;
16use tracing::{Instrument, info_span};
17
18use crate::db::NoteRecord;
19use crate::errors::{ApplyBlockError, ApplyBlockWithProvingInputsError, InvalidBlockError};
20use crate::state::{BlockNotification, State};
21use crate::{COMPONENT, HistoricalError, LOG_TARGET};
22
23impl State {
24 #[miden_instrument(
28 target = COMPONENT,
29 skip_all,
30 err,
31 )]
32 pub async fn apply_block_with_proving_inputs(
33 &self,
34 ordered_batches: OrderedBatches,
35 block_inputs: BlockInputs,
36 signed_block: SignedBlock,
37 ) -> Result<(), ApplyBlockWithProvingInputsError> {
38 let block_header = signed_block.header().clone();
39 let block_num = block_header.block_num();
40
41 let proving_inputs = BlockProofRequest {
42 tx_batches: ordered_batches,
43 block_header,
44 block_inputs,
45 };
46
47 self.save_proving_inputs(block_num, &proving_inputs)
48 .await
49 .map_err(ApplyBlockWithProvingInputsError::SaveProvingInputs)?;
50
51 self.apply_block(signed_block)
52 .await
53 .map_err(ApplyBlockWithProvingInputsError::ApplyBlock)
54 }
55
56 #[miden_instrument(
81 target = COMPONENT,
82 skip_all,
83 err,
84 )]
85 pub async fn apply_block(&self, signed_block: SignedBlock) -> Result<(), ApplyBlockError> {
86 let _lock = self.writer.try_lock().map_err(|_| ApplyBlockError::ConcurrentWrite)?;
87
88 let header = signed_block.header();
89 let body = signed_block.body();
90
91 let block_num = header.block_num();
92 let block_commitment = header.commitment();
93 let num_transactions = body.transactions().as_slice().len();
94
95 miden_span_record!(
96 block.number = %block_num,
97 block.commitment = %block_commitment,
98 block.transactions.count = num_transactions,
99 );
100
101 self.validate_block_header(header, body).await?;
102
103 let signed_block_bytes = signed_block.to_bytes();
107 let cache_bytes = signed_block_bytes.clone();
109 let store = Arc::clone(&self.block_store);
110 let block_save_task = tokio::spawn(
111 async move { store.save_block(block_num, &signed_block_bytes).await }.in_current_span(),
112 );
113
114 let (
115 nullifier_tree_old_root,
116 nullifier_tree_update,
117 account_tree_old_root,
118 account_tree_update,
119 ) = self.compute_tree_mutations(header, body).await?;
120
121 let notes = Self::build_note_records(header, body)?;
122
123 let (allow_acquire, acquired_allowed) = oneshot::channel::<()>();
125 let (inform_acquire_done, acquire_done) = oneshot::channel::<()>();
127
128 let account_deltas =
131 Vec::from_iter(body.updated_accounts().iter().filter_map(
132 |update| match update.details() {
133 AccountUpdateDetails::Delta(delta) => Some(delta.clone()),
134 AccountUpdateDetails::Private => None,
135 },
136 ));
137
138 let db = Arc::clone(&self.db);
142 let db_update_task = tokio::spawn(
143 async move { db.apply_block(allow_acquire, acquire_done, signed_block, notes).await }
144 .in_current_span(),
145 );
146
147 acquired_allowed
149 .instrument(info_span!(target: COMPONENT, "await_db_readiness"))
150 .await
151 .map_err(ApplyBlockError::ClosedChannel)?;
152
153 block_save_task.await??;
155
156 self.with_inner_write_blocking(|inner| {
157 if inner.nullifier_tree.root() != nullifier_tree_old_root
162 || inner.account_tree.root_latest() != account_tree_old_root
163 {
164 return Err(ApplyBlockError::ConcurrentWrite);
165 }
166
167 inform_acquire_done
170 .send(())
171 .map_err(|_| ApplyBlockError::DbUpdateTaskFailed("Receiver was dropped".into()))?;
172
173 tokio::runtime::Handle::current()
177 .block_on(db_update_task)?
178 .map_err(|err| ApplyBlockError::DbUpdateTaskFailed(err.as_report()))?;
179
180 inner
182 .nullifier_tree
183 .apply_mutations(nullifier_tree_update)
184 .expect("Unreachable: old nullifier tree root must be checked before this step");
185 inner
186 .account_tree
187 .apply_mutations(account_tree_update)
188 .expect("Unreachable: old account tree root must be checked before this step");
189
190 inner.blockchain.push(block_commitment);
191
192 Ok(())
193 })?;
194
195 self.with_forest_write_blocking(|forest| {
196 forest.apply_block_updates(block_num, account_deltas)
197 })?;
198
199 self.block_cache
201 .push(block_num, BlockNotification::new(block_num, cache_bytes))
202 .expect("block cache receives sequential block numbers");
203 let _ = self.committed_tip_tx.send(block_num);
204
205 tracing::debug!(target: LOG_TARGET, "Block applied");
206
207 Ok(())
208 }
209
210 pub async fn save_proving_inputs(
212 &self,
213 block_num: BlockNumber,
214 proving_inputs: &BlockProofRequest,
215 ) -> std::io::Result<()> {
216 self.block_store
217 .save_proving_inputs(block_num, &proving_inputs.to_bytes())
218 .await
219 }
220
221 #[miden_instrument(
223 target = COMPONENT,
224 skip_all,
225 err,
226 )]
227 async fn validate_block_header(
228 &self,
229 header: &BlockHeader,
230 body: &BlockBody,
231 ) -> Result<(), ApplyBlockError> {
232 let tx_commitment = body.transactions().commitment();
234 if header.tx_commitment() != tx_commitment {
235 return Err(InvalidBlockError::InvalidBlockTxCommitment {
236 expected: tx_commitment,
237 actual: header.tx_commitment(),
238 }
239 .into());
240 }
241
242 let block_num = header.block_num();
243
244 let prev_block = self
246 .db
247 .select_block_header_by_block_num(None)
248 .await?
249 .ok_or(ApplyBlockError::DbBlockHeaderEmpty)?;
250 let expected_block_num = prev_block.block_num().child();
251 if block_num != expected_block_num {
252 return Err(InvalidBlockError::NewBlockInvalidBlockNum {
253 expected: expected_block_num,
254 submitted: block_num,
255 }
256 .into());
257 }
258 if header.prev_block_commitment() != prev_block.commitment() {
259 return Err(InvalidBlockError::NewBlockInvalidPrevCommitment.into());
260 }
261
262 Ok(())
263 }
264
265 #[miden_instrument(
267 target = COMPONENT,
268 skip_all,
269 err,
270 )]
271 async fn compute_tree_mutations(
272 &self,
273 header: &BlockHeader,
274 body: &BlockBody,
275 ) -> Result<(Word, NullifierMutationSet, Word, AccountMutationSet), ApplyBlockError> {
276 self.with_inner_read_blocking(|inner| {
277 let block_num = header.block_num();
278
279 let duplicate_nullifiers: Vec<_> = body
281 .created_nullifiers()
282 .iter()
283 .filter(|&nullifier| inner.nullifier_tree.get_block_num(nullifier).is_some())
284 .copied()
285 .collect();
286 if !duplicate_nullifiers.is_empty() {
287 return Err(InvalidBlockError::DuplicatedNullifiers(duplicate_nullifiers).into());
288 }
289
290 let peaks = inner.blockchain.peaks();
292 if peaks.hash_peaks() != header.chain_commitment() {
293 return Err(InvalidBlockError::NewBlockInvalidChainCommitment.into());
294 }
295
296 let nullifier_tree_update = inner
298 .nullifier_tree
299 .compute_mutations(
300 body.created_nullifiers().iter().map(|nullifier| (*nullifier, block_num)),
301 )
302 .map_err(InvalidBlockError::NewBlockNullifierAlreadySpent)?;
303
304 if nullifier_tree_update.as_mutation_set().root() != header.nullifier_root() {
305 return Err(InvalidBlockError::NewBlockInvalidNullifierRoot.into());
306 }
307
308 let account_tree_update = inner
310 .account_tree
311 .compute_mutations(
312 body.updated_accounts()
313 .iter()
314 .map(|update| (update.account_id(), update.final_state_commitment())),
315 )
316 .map_err(|e| match e {
317 HistoricalError::AccountTreeError(err) => {
318 InvalidBlockError::NewBlockDuplicateAccountIdPrefix(err)
319 },
320 HistoricalError::MerkleError(_) => {
321 panic!("Unexpected MerkleError during account tree mutation computation")
322 },
323 })?;
324
325 if account_tree_update.as_mutation_set().root() != header.account_root() {
326 return Err(InvalidBlockError::NewBlockInvalidAccountRoot.into());
327 }
328
329 Ok((
330 inner.nullifier_tree.root(),
331 nullifier_tree_update,
332 inner.account_tree.root_latest(),
333 account_tree_update,
334 ))
335 })
336 }
337
338 #[miden_instrument(
340 target = COMPONENT,
341 skip_all,
342 err,
343 )]
344 fn build_note_records(
345 header: &BlockHeader,
346 body: &BlockBody,
347 ) -> Result<Vec<(NoteRecord, Option<Nullifier>)>, ApplyBlockError> {
348 let block_num = header.block_num();
349
350 let note_tree = body.compute_block_note_tree();
351 if note_tree.root() != header.note_root() {
352 return Err(InvalidBlockError::NewBlockInvalidNoteRoot.into());
353 }
354
355 let notes = body
356 .output_notes()
357 .map(|(note_index, note)| {
358 let (details, attachments, nullifier) = match note {
359 OutputNote::Public(public) => (
360 Some(NoteDetails::from(public.as_note())),
361 public.as_note().attachments().clone(),
362 Some(public.as_note().nullifier()),
363 ),
364 OutputNote::Private(private) => (None, private.attachments().clone(), None),
365 };
366
367 let inclusion_path = note_tree.open(note_index);
368
369 let note_record = NoteRecord {
370 block_num,
371 note_index,
372 note_id: note.id().as_word(),
373 metadata: *note.metadata(),
374 details,
375 attachments,
376 inclusion_path,
377 };
378
379 Ok((note_record, nullifier))
380 })
381 .collect::<Result<Vec<_>, InvalidBlockError>>()?;
382
383 Ok(notes)
384 }
385}