miden_objects/transaction/
tx_header.rs

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