Skip to main content

miden_protocol/transaction/
tx_header.rs

1use alloc::vec::Vec;
2
3use miden_processor::DeserializationError;
4
5use crate::Word;
6use crate::asset::FungibleAsset;
7use crate::note::NoteHeader;
8use crate::transaction::{
9    AccountId,
10    ExecutedTransaction,
11    InputNoteCommitment,
12    InputNotes,
13    OutputNote,
14    OutputNotes,
15    ProvenTransaction,
16    TransactionId,
17};
18use crate::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
19
20/// A transaction header derived from a
21/// [`ProvenTransaction`](crate::transaction::ProvenTransaction).
22///
23/// The header is essentially a direct copy of the transaction's commitments, in particular the
24/// initial and final account state commitment as well as all nullifiers of consumed notes and all
25/// note IDs of created notes. While account updates may be aggregated and notes may be erased as
26/// part of batch and block building, the header retains the original transaction's data.
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct TransactionHeader {
29    id: TransactionId,
30    account_id: AccountId,
31    initial_state_commitment: Word,
32    final_state_commitment: Word,
33    input_notes: InputNotes<InputNoteCommitment>,
34    output_notes: Vec<NoteHeader>,
35    fee: FungibleAsset,
36}
37
38impl TransactionHeader {
39    // CONSTRUCTORS
40    // --------------------------------------------------------------------------------------------
41
42    /// Constructs a new [`TransactionHeader`] from the provided parameters.
43    ///
44    /// The [`TransactionId`] is computed from the provided parameters.
45    ///
46    /// The input notes and output notes must be in the same order as they appeared in the
47    /// transaction that this header represents, otherwise an incorrect ID will be computed.
48    ///
49    /// Note that this cannot validate that the [`AccountId`] or the fee asset is valid with respect
50    /// to the other data. This must be validated outside of this type.
51    pub fn new(
52        account_id: AccountId,
53        initial_state_commitment: Word,
54        final_state_commitment: Word,
55        input_notes: InputNotes<InputNoteCommitment>,
56        output_notes: Vec<NoteHeader>,
57        fee: FungibleAsset,
58    ) -> Self {
59        let input_notes_commitment = input_notes.commitment();
60        let output_notes_commitment = OutputNotes::compute_commitment(output_notes.iter());
61
62        let id = TransactionId::new(
63            initial_state_commitment,
64            final_state_commitment,
65            input_notes_commitment,
66            output_notes_commitment,
67        );
68
69        Self {
70            id,
71            account_id,
72            initial_state_commitment,
73            final_state_commitment,
74            input_notes,
75            output_notes,
76            fee,
77        }
78    }
79
80    /// Constructs a new [`TransactionHeader`] from the provided parameters.
81    ///
82    /// # Warning
83    ///
84    /// This does not validate the internal consistency of the data. Prefer [`Self::new`] whenever
85    /// possible.
86    pub fn new_unchecked(
87        id: TransactionId,
88        account_id: AccountId,
89        initial_state_commitment: Word,
90        final_state_commitment: Word,
91        input_notes: InputNotes<InputNoteCommitment>,
92        output_notes: Vec<NoteHeader>,
93        fee: FungibleAsset,
94    ) -> Self {
95        Self {
96            id,
97            account_id,
98            initial_state_commitment,
99            final_state_commitment,
100            input_notes,
101            output_notes,
102            fee,
103        }
104    }
105
106    // PUBLIC ACCESSORS
107    // --------------------------------------------------------------------------------------------
108
109    /// Returns the unique identifier of this transaction.
110    pub fn id(&self) -> TransactionId {
111        self.id
112    }
113
114    /// Returns the ID of the account against which this transaction was executed.
115    pub fn account_id(&self) -> AccountId {
116        self.account_id
117    }
118
119    /// Returns a commitment to the state of the account before this update is applied.
120    ///
121    /// This is equal to [`Word::empty()`] for new accounts.
122    pub fn initial_state_commitment(&self) -> Word {
123        self.initial_state_commitment
124    }
125
126    /// Returns a commitment to the state of the account after this update is applied.
127    pub fn final_state_commitment(&self) -> Word {
128        self.final_state_commitment
129    }
130
131    /// Returns a reference to the consumed notes of the transaction.
132    ///
133    /// The returned input note commitments have the same order as the transaction to which the
134    /// header belongs.
135    ///
136    /// Note that the note may have been erased at the batch or block level, so it may not be
137    /// present there.
138    pub fn input_notes(&self) -> &InputNotes<InputNoteCommitment> {
139        &self.input_notes
140    }
141
142    /// Returns a reference to the ID and metadata of the output notes created by the transaction.
143    ///
144    /// The returned output note data has the same order as the transaction to which the header
145    /// belongs.
146    ///
147    /// Note that the note may have been erased at the batch or block level, so it may not be
148    /// present there.
149    pub fn output_notes(&self) -> &[NoteHeader] {
150        &self.output_notes
151    }
152
153    /// Returns the fee paid by this transaction.
154    pub fn fee(&self) -> FungibleAsset {
155        self.fee
156    }
157}
158
159impl From<&ProvenTransaction> for TransactionHeader {
160    /// Constructs a [`TransactionHeader`] from a [`ProvenTransaction`].
161    fn from(tx: &ProvenTransaction) -> Self {
162        // SAFETY: The data in a proven transaction is guaranteed to be internally consistent and so
163        // we can skip the consistency checks by the `new` constructor.
164        TransactionHeader::new_unchecked(
165            tx.id(),
166            tx.account_id(),
167            tx.account_update().initial_state_commitment(),
168            tx.account_update().final_state_commitment(),
169            tx.input_notes().clone(),
170            tx.output_notes().iter().map(OutputNote::header).cloned().collect(),
171            tx.fee(),
172        )
173    }
174}
175
176impl From<&ExecutedTransaction> for TransactionHeader {
177    /// Constructs a [`TransactionHeader`] from a [`ExecutedTransaction`].
178    fn from(tx: &ExecutedTransaction) -> Self {
179        TransactionHeader::new_unchecked(
180            tx.id(),
181            tx.account_id(),
182            tx.initial_account().initial_commitment(),
183            tx.final_account().commitment(),
184            tx.input_notes().to_commitments(),
185            tx.output_notes().iter().map(OutputNote::header).cloned().collect(),
186            tx.fee(),
187        )
188    }
189}
190
191// SERIALIZATION
192// ================================================================================================
193
194impl Serializable for TransactionHeader {
195    fn write_into<W: ByteWriter>(&self, target: &mut W) {
196        let Self {
197            id: _,
198            account_id,
199            initial_state_commitment,
200            final_state_commitment,
201            input_notes,
202            output_notes,
203            fee,
204        } = self;
205
206        account_id.write_into(target);
207        initial_state_commitment.write_into(target);
208        final_state_commitment.write_into(target);
209        input_notes.write_into(target);
210        output_notes.write_into(target);
211        fee.write_into(target);
212    }
213}
214
215impl Deserializable for TransactionHeader {
216    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
217        let account_id = <AccountId>::read_from(source)?;
218        let initial_state_commitment = <Word>::read_from(source)?;
219        let final_state_commitment = <Word>::read_from(source)?;
220        let input_notes = <InputNotes<InputNoteCommitment>>::read_from(source)?;
221        let output_notes = <Vec<NoteHeader>>::read_from(source)?;
222        let fee = FungibleAsset::read_from(source)?;
223
224        let tx_header = Self::new(
225            account_id,
226            initial_state_commitment,
227            final_state_commitment,
228            input_notes,
229            output_notes,
230            fee,
231        );
232
233        Ok(tx_header)
234    }
235}