Skip to main content

miden_protocol/transaction/
tx_header.rs

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