Skip to main content

miden_protocol/block/
block_body.rs

1use alloc::vec::Vec;
2
3use miden_core::Word;
4use miden_core::utils::{
5    ByteReader,
6    ByteWriter,
7    Deserializable,
8    DeserializationError,
9    Serializable,
10};
11
12use crate::block::{
13    BlockAccountUpdate,
14    BlockNoteIndex,
15    BlockNoteTree,
16    OutputNoteBatch,
17    ProposedBlock,
18};
19use crate::note::Nullifier;
20use crate::transaction::{OrderedTransactionHeaders, OutputNote};
21
22// BLOCK BODY
23// ================================================================================================
24
25/// Body of a block in the chain which contains data pertaining to all relevant state changes.
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct BlockBody {
28    /// Account updates for the block.
29    updated_accounts: Vec<BlockAccountUpdate>,
30
31    /// Note batches created by the transactions in this block.
32    output_note_batches: Vec<OutputNoteBatch>,
33
34    /// Nullifiers created by the transactions in this block through the consumption of notes.
35    created_nullifiers: Vec<Nullifier>,
36
37    /// The aggregated and flattened transaction headers of all batches in the order in which they
38    /// appeared in the proposed block.
39    transactions: OrderedTransactionHeaders,
40}
41
42impl BlockBody {
43    // CONSTRUCTOR
44    // --------------------------------------------------------------------------------------------
45
46    /// Creates a new [`BlockBody`] without performing any validation.
47    ///
48    /// # Warning
49    ///
50    /// This does not validate any of the guarantees of this type. It should only be used internally
51    /// (in miden-lib) or in tests.
52    pub fn new_unchecked(
53        updated_accounts: Vec<BlockAccountUpdate>,
54        output_note_batches: Vec<OutputNoteBatch>,
55        created_nullifiers: Vec<Nullifier>,
56        transactions: OrderedTransactionHeaders,
57    ) -> Self {
58        Self {
59            updated_accounts,
60            output_note_batches,
61            created_nullifiers,
62            transactions,
63        }
64    }
65
66    // PUBLIC ACCESSORS
67    // --------------------------------------------------------------------------------------------
68
69    /// Returns the slice of [`BlockAccountUpdate`]s for all accounts updated in the block.
70    pub fn updated_accounts(&self) -> &[BlockAccountUpdate] {
71        &self.updated_accounts
72    }
73
74    /// Returns the slice of [`OutputNoteBatch`]es for all output notes created in the block.
75    pub fn output_note_batches(&self) -> &[OutputNoteBatch] {
76        &self.output_note_batches
77    }
78
79    /// Returns a reference to the slice of nullifiers for all notes consumed in the block.
80    pub fn created_nullifiers(&self) -> &[Nullifier] {
81        &self.created_nullifiers
82    }
83
84    /// Returns the [`OrderedTransactionHeaders`] of all transactions included in this block.
85    pub fn transactions(&self) -> &OrderedTransactionHeaders {
86        &self.transactions
87    }
88
89    /// Returns the commitment of all transactions included in this block.
90    pub fn transaction_commitment(&self) -> Word {
91        self.transactions.commitment()
92    }
93
94    /// Returns an iterator over all [`OutputNote`]s created in this block.
95    ///
96    /// Each note is accompanied by a corresponding index specifying where the note is located
97    /// in the block's [`BlockNoteTree`].
98    pub fn output_notes(&self) -> impl Iterator<Item = (BlockNoteIndex, &OutputNote)> {
99        self.output_note_batches.iter().enumerate().flat_map(|(batch_idx, notes)| {
100            notes.iter().map(move |(note_idx_in_batch, note)| {
101                (
102                    // SAFETY: The block body contains at most the max allowed number of
103                    // batches and each batch is guaranteed to contain
104                    // at most the max allowed number of output notes.
105                    BlockNoteIndex::new(batch_idx, *note_idx_in_batch)
106                        .expect("max batches in block and max notes in batches should be enforced"),
107                    note,
108                )
109            })
110        })
111    }
112
113    /// Computes the [`BlockNoteTree`] containing all [`OutputNote`]s created in this block.
114    pub fn compute_block_note_tree(&self) -> BlockNoteTree {
115        let entries = self
116            .output_notes()
117            .map(|(note_index, note)| (note_index, note.id(), note.metadata()));
118
119        // SAFETY: We only construct block bodies that:
120        // - do not contain duplicates
121        // - contain at most the max allowed number of batches and each batch is guaranteed to
122        //   contain at most the max allowed number of output notes.
123        BlockNoteTree::with_entries(entries)
124                .expect("the output notes of the block should not contain duplicates and contain at most the allowed maximum")
125    }
126
127    // DESTRUCTURING
128    // --------------------------------------------------------------------------------------------
129
130    /// Consumes the block body and returns its parts.
131    pub fn into_parts(
132        self,
133    ) -> (
134        Vec<BlockAccountUpdate>,
135        Vec<OutputNoteBatch>,
136        Vec<Nullifier>,
137        OrderedTransactionHeaders,
138    ) {
139        (
140            self.updated_accounts,
141            self.output_note_batches,
142            self.created_nullifiers,
143            self.transactions,
144        )
145    }
146}
147
148impl From<ProposedBlock> for BlockBody {
149    fn from(block: ProposedBlock) -> Self {
150        // Split the proposed block into its constituent parts.
151        let (batches, account_updated_witnesses, output_note_batches, created_nullifiers, ..) =
152            block.into_parts();
153
154        // Transform the account update witnesses into block account updates.
155        let updated_accounts = account_updated_witnesses
156            .into_iter()
157            .map(|(account_id, update_witness)| {
158                let (
159                    _initial_state_commitment,
160                    final_state_commitment,
161                    // Note that compute_account_root took out this value so it should not be used.
162                    _initial_state_proof,
163                    details,
164                ) = update_witness.into_parts();
165                BlockAccountUpdate::new(account_id, final_state_commitment, details)
166            })
167            .collect();
168        let created_nullifiers = created_nullifiers.keys().copied().collect::<Vec<_>>();
169        // Aggregate the verified transactions of all batches.
170        let transactions = batches.into_transactions();
171        Self {
172            updated_accounts,
173            output_note_batches,
174            created_nullifiers,
175            transactions,
176        }
177    }
178}
179
180// SERIALIZATION
181// ================================================================================================
182
183impl Serializable for BlockBody {
184    fn write_into<W: ByteWriter>(&self, target: &mut W) {
185        self.updated_accounts.write_into(target);
186        self.output_note_batches.write_into(target);
187        self.created_nullifiers.write_into(target);
188        self.transactions.write_into(target);
189    }
190}
191
192impl Deserializable for BlockBody {
193    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
194        let block = Self {
195            updated_accounts: Vec::read_from(source)?,
196            output_note_batches: Vec::read_from(source)?,
197            created_nullifiers: Vec::read_from(source)?,
198            transactions: OrderedTransactionHeaders::read_from(source)?,
199        };
200        Ok(block)
201    }
202}