Skip to main content

miden_protocol/block/
block_body.rs

1use alloc::vec::Vec;
2
3use miden_core::Word;
4
5use crate::block::{
6    BlockAccountUpdate,
7    BlockNoteIndex,
8    BlockNoteTree,
9    OutputNoteBatch,
10    ProposedBlock,
11};
12use crate::note::Nullifier;
13use crate::transaction::{OrderedTransactionHeaders, OutputNote};
14use crate::utils::serde::{
15    ByteReader,
16    ByteWriter,
17    Deserializable,
18    DeserializationError,
19    Serializable,
20};
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.output_notes().map(|(note_index, note)| (note_index, note.into()));
116
117        // SAFETY: We only construct block bodies that:
118        // - do not contain duplicates
119        // - contain at most the max allowed number of batches and each batch is guaranteed to
120        //   contain at most the max allowed number of output notes.
121        BlockNoteTree::with_entries(entries)
122                .expect("the output notes of the block should not contain duplicates and contain at most the allowed maximum")
123    }
124
125    // DESTRUCTURING
126    // --------------------------------------------------------------------------------------------
127
128    /// Consumes the block body and returns its parts.
129    pub fn into_parts(
130        self,
131    ) -> (
132        Vec<BlockAccountUpdate>,
133        Vec<OutputNoteBatch>,
134        Vec<Nullifier>,
135        OrderedTransactionHeaders,
136    ) {
137        (
138            self.updated_accounts,
139            self.output_note_batches,
140            self.created_nullifiers,
141            self.transactions,
142        )
143    }
144}
145
146impl From<ProposedBlock> for BlockBody {
147    fn from(block: ProposedBlock) -> Self {
148        // Split the proposed block into its constituent parts.
149        let (batches, account_updated_witnesses, output_note_batches, created_nullifiers, ..) =
150            block.into_parts();
151
152        // Transform the account update witnesses into block account updates.
153        let updated_accounts = account_updated_witnesses
154            .into_iter()
155            .map(|(account_id, update_witness)| {
156                let (
157                    _initial_state_commitment,
158                    final_state_commitment,
159                    // Note that compute_account_root took out this value so it should not be used.
160                    _initial_state_proof,
161                    details,
162                ) = update_witness.into_parts();
163                BlockAccountUpdate::new(account_id, final_state_commitment, details)
164            })
165            .collect();
166        let created_nullifiers = created_nullifiers.keys().copied().collect::<Vec<_>>();
167        // Aggregate the verified transactions of all batches.
168        let transactions = batches.into_transactions();
169        Self {
170            updated_accounts,
171            output_note_batches,
172            created_nullifiers,
173            transactions,
174        }
175    }
176}
177
178// SERIALIZATION
179// ================================================================================================
180
181impl Serializable for BlockBody {
182    fn write_into<W: ByteWriter>(&self, target: &mut W) {
183        self.updated_accounts.write_into(target);
184        self.output_note_batches.write_into(target);
185        self.created_nullifiers.write_into(target);
186        self.transactions.write_into(target);
187    }
188}
189
190impl Deserializable for BlockBody {
191    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
192        let block = Self {
193            updated_accounts: Vec::read_from(source)?,
194            output_note_batches: Vec::read_from(source)?,
195            created_nullifiers: Vec::read_from(source)?,
196            transactions: OrderedTransactionHeaders::read_from(source)?,
197        };
198        Ok(block)
199    }
200}