Skip to main content

miden_protocol/transaction/
tx_header.rs

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