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}