Skip to main content

miden_protocol/batch/
proven_batch.rs

1use alloc::collections::BTreeMap;
2use alloc::string::ToString;
3use alloc::vec::Vec;
4
5use crate::account::AccountId;
6use crate::batch::{BatchAccountUpdate, BatchId};
7use crate::block::BlockNumber;
8use crate::errors::ProvenBatchError;
9use crate::note::Nullifier;
10use crate::transaction::{InputNoteCommitment, InputNotes, OrderedTransactionHeaders, OutputNote};
11use crate::utils::serde::{
12    ByteReader,
13    ByteWriter,
14    Deserializable,
15    DeserializationError,
16    Serializable,
17};
18use crate::vm::ExecutionProof;
19use crate::{MIN_PROOF_SECURITY_LEVEL, Word};
20
21/// A transaction batch with an execution proof.
22/// Currently, this only carries a skeleton proof which does not attest to anything meaningful.
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct ProvenBatch {
25    id: BatchId,
26    reference_block_commitment: Word,
27    reference_block_num: BlockNumber,
28    account_updates: BTreeMap<AccountId, BatchAccountUpdate>,
29    input_notes: InputNotes<InputNoteCommitment>,
30    output_notes: Vec<OutputNote>,
31    batch_expiration_block_num: BlockNumber,
32    transactions: OrderedTransactionHeaders,
33    proof: ExecutionProof,
34}
35
36impl ProvenBatch {
37    // CONSTRUCTORS
38    // --------------------------------------------------------------------------------------------
39
40    /// Creates a new [`ProvenBatch`] from the provided parts without checking any constraints
41    /// except the ones listed in the errors section below.
42    ///
43    /// This should essentially never be called by users.
44    ///
45    /// # Errors
46    ///
47    /// Returns an error if the batch expiration block number is not greater than the reference
48    /// block number.
49    #[allow(clippy::too_many_arguments)]
50    pub fn new_unchecked(
51        id: BatchId,
52        reference_block_commitment: Word,
53        reference_block_num: BlockNumber,
54        account_updates: BTreeMap<AccountId, BatchAccountUpdate>,
55        input_notes: InputNotes<InputNoteCommitment>,
56        output_notes: Vec<OutputNote>,
57        batch_expiration_block_num: BlockNumber,
58        transactions: OrderedTransactionHeaders,
59        proof: ExecutionProof,
60    ) -> Result<Self, ProvenBatchError> {
61        // Check that the batch expiration block number is greater than the reference block number.
62        if batch_expiration_block_num <= reference_block_num {
63            return Err(ProvenBatchError::InvalidBatchExpirationBlockNum {
64                batch_expiration_block_num,
65                reference_block_num,
66            });
67        }
68
69        Ok(Self {
70            id,
71            reference_block_commitment,
72            reference_block_num,
73            account_updates,
74            input_notes,
75            output_notes,
76            batch_expiration_block_num,
77            transactions,
78            proof,
79        })
80    }
81
82    // PUBLIC ACCESSORS
83    // --------------------------------------------------------------------------------------------
84
85    /// The ID of this batch. See [`BatchId`] for details on how it is computed.
86    pub fn id(&self) -> BatchId {
87        self.id
88    }
89
90    /// Returns the commitment to the reference block of the batch.
91    pub fn reference_block_commitment(&self) -> Word {
92        self.reference_block_commitment
93    }
94
95    /// Returns the number of the reference block of the batch.
96    pub fn reference_block_num(&self) -> BlockNumber {
97        self.reference_block_num
98    }
99
100    /// Returns the block number at which the batch will expire.
101    pub fn batch_expiration_block_num(&self) -> BlockNumber {
102        self.batch_expiration_block_num
103    }
104
105    /// Returns an iterator over the IDs of all accounts updated in this batch.
106    pub fn updated_accounts(&self) -> impl Iterator<Item = AccountId> + use<'_> {
107        self.account_updates.keys().copied()
108    }
109
110    /// Returns the proof security level of the batch.
111    pub fn proof_security_level(&self) -> u32 {
112        MIN_PROOF_SECURITY_LEVEL
113    }
114
115    /// Returns the map of account IDs mapped to their [`BatchAccountUpdate`]s.
116    ///
117    /// If an account was updated by multiple transactions, the [`BatchAccountUpdate`] is the result
118    /// of merging the individual updates.
119    ///
120    /// For example, suppose an account's state before this batch is `A` and the batch contains two
121    /// transactions that updated it. Applying the first transaction results in intermediate state
122    /// `B`, and applying the second one results in state `C`. Then the returned update represents
123    /// the state transition from `A` to `C`.
124    pub fn account_updates(&self) -> &BTreeMap<AccountId, BatchAccountUpdate> {
125        &self.account_updates
126    }
127
128    /// Returns the [`InputNotes`] of this batch.
129    pub fn input_notes(&self) -> &InputNotes<InputNoteCommitment> {
130        &self.input_notes
131    }
132
133    /// Returns an iterator over the nullifiers created in this batch.
134    pub fn created_nullifiers(&self) -> impl Iterator<Item = Nullifier> + use<'_> {
135        self.input_notes.iter().map(InputNoteCommitment::nullifier)
136    }
137
138    /// Returns the output notes of the batch.
139    ///
140    /// This is the aggregation of all output notes by the transactions in the batch, except the
141    /// ones that were consumed within the batch itself.
142    pub fn output_notes(&self) -> &[OutputNote] {
143        &self.output_notes
144    }
145
146    /// Returns the [`OrderedTransactionHeaders`] included in this batch.
147    pub fn transactions(&self) -> &OrderedTransactionHeaders {
148        &self.transactions
149    }
150
151    /// Returns the execution proof attached to this batch.
152    pub fn proof(&self) -> &ExecutionProof {
153        &self.proof
154    }
155
156    // MUTATORS
157    // --------------------------------------------------------------------------------------------
158
159    /// Consumes self and returns the contained [`OrderedTransactionHeaders`] of this batch.
160    pub fn into_transactions(self) -> OrderedTransactionHeaders {
161        self.transactions
162    }
163}
164
165// SERIALIZATION
166// ================================================================================================
167
168impl Serializable for ProvenBatch {
169    fn write_into<W: ByteWriter>(&self, target: &mut W) {
170        self.reference_block_commitment.write_into(target);
171        self.reference_block_num.write_into(target);
172        self.account_updates.write_into(target);
173        self.input_notes.write_into(target);
174        self.output_notes.write_into(target);
175        self.batch_expiration_block_num.write_into(target);
176        self.transactions.write_into(target);
177        self.proof.write_into(target);
178    }
179}
180
181impl Deserializable for ProvenBatch {
182    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
183        let reference_block_commitment = Word::read_from(source)?;
184        let reference_block_num = BlockNumber::read_from(source)?;
185        let account_updates = BTreeMap::read_from(source)?;
186        let input_notes = InputNotes::<InputNoteCommitment>::read_from(source)?;
187        let output_notes = Vec::<OutputNote>::read_from(source)?;
188        let batch_expiration_block_num = BlockNumber::read_from(source)?;
189        let transactions = OrderedTransactionHeaders::read_from(source)?;
190        let proof = ExecutionProof::read_from(source)?;
191
192        let id =
193            BatchId::from_ids(transactions.as_slice().iter().map(|tx| (tx.id(), tx.account_id())));
194
195        Self::new_unchecked(
196            id,
197            reference_block_commitment,
198            reference_block_num,
199            account_updates,
200            input_notes,
201            output_notes,
202            batch_expiration_block_num,
203            transactions,
204            proof,
205        )
206        .map_err(|e| DeserializationError::UnknownError(e.to_string()))
207    }
208}