miden_client/transaction/
record.rs1use alloc::string::ToString;
2use alloc::vec::Vec;
3use core::fmt;
4
5use miden_protocol::Word;
6use miden_protocol::account::AccountId;
7use miden_protocol::block::BlockNumber;
8use miden_protocol::transaction::{RawOutputNotes, TransactionId, TransactionScript};
9use miden_tx::utils::serde::{
10 ByteReader,
11 ByteWriter,
12 Deserializable,
13 DeserializationError,
14 Serializable,
15};
16
17#[derive(Debug, Clone)]
22pub struct TransactionRecord {
23 pub id: TransactionId,
25 pub details: TransactionDetails,
27 pub script: Option<TransactionScript>,
30 pub status: TransactionStatus,
32}
33
34impl TransactionRecord {
35 pub fn new(
37 id: TransactionId,
38 details: TransactionDetails,
39 script: Option<TransactionScript>,
40 status: TransactionStatus,
41 ) -> TransactionRecord {
42 TransactionRecord { id, details, script, status }
43 }
44
45 pub fn commit_transaction(
48 &mut self,
49 commit_height: BlockNumber,
50 commit_timestamp: u64,
51 ) -> bool {
52 match self.status {
53 TransactionStatus::Pending => {
54 self.status = TransactionStatus::Committed {
55 block_number: commit_height,
56 commit_timestamp,
57 };
58 true
59 },
60 TransactionStatus::Discarded(_) | TransactionStatus::Committed { .. } => false,
64 }
65 }
66
67 pub fn discard_transaction(&mut self, cause: DiscardCause) -> bool {
70 match self.status {
71 TransactionStatus::Pending => {
72 self.status = TransactionStatus::Discarded(cause);
73 true
74 },
75 TransactionStatus::Discarded(_) | TransactionStatus::Committed { .. } => false,
76 }
77 }
78}
79
80#[derive(Debug, Clone)]
82pub struct TransactionDetails {
83 pub account_id: AccountId,
85 pub init_account_state: Word,
87 pub final_account_state: Word,
89 pub input_note_nullifiers: Vec<Word>,
91 pub output_notes: RawOutputNotes,
93 pub block_num: BlockNumber,
95 pub submission_height: BlockNumber,
97 pub expiration_block_num: BlockNumber,
99 pub creation_timestamp: u64,
101}
102
103impl Serializable for TransactionDetails {
104 fn write_into<W: ByteWriter>(&self, target: &mut W) {
105 self.account_id.write_into(target);
106 self.init_account_state.write_into(target);
107 self.final_account_state.write_into(target);
108 self.input_note_nullifiers.write_into(target);
109 self.output_notes.write_into(target);
110 self.block_num.write_into(target);
111 self.submission_height.write_into(target);
112 self.expiration_block_num.write_into(target);
113 self.creation_timestamp.write_into(target);
114 }
115}
116
117impl Deserializable for TransactionDetails {
118 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
119 let account_id = AccountId::read_from(source)?;
120 let init_account_state = Word::read_from(source)?;
121 let final_account_state = Word::read_from(source)?;
122 let input_note_nullifiers = Vec::<Word>::read_from(source)?;
123 let output_notes = RawOutputNotes::read_from(source)?;
124 let block_num = BlockNumber::read_from(source)?;
125 let submission_height = BlockNumber::read_from(source)?;
126 let expiration_block_num = BlockNumber::read_from(source)?;
127 let creation_timestamp = source.read_u64()?;
128
129 Ok(Self {
130 account_id,
131 init_account_state,
132 final_account_state,
133 input_note_nullifiers,
134 output_notes,
135 block_num,
136 submission_height,
137 expiration_block_num,
138 creation_timestamp,
139 })
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq)]
145pub enum DiscardCause {
146 Expired,
147 InputConsumed,
148 DiscardedInitialState,
149 Stale,
150 Superseded,
152}
153
154impl DiscardCause {
155 pub fn from_string(cause: &str) -> Result<Self, DeserializationError> {
156 match cause {
157 "Expired" => Ok(DiscardCause::Expired),
158 "InputConsumed" => Ok(DiscardCause::InputConsumed),
159 "DiscardedInitialState" => Ok(DiscardCause::DiscardedInitialState),
160 "Stale" => Ok(DiscardCause::Stale),
161 "Superseded" => Ok(DiscardCause::Superseded),
162 _ => Err(DeserializationError::InvalidValue(format!("Invalid discard cause: {cause}"))),
163 }
164 }
165}
166
167impl fmt::Display for DiscardCause {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self {
170 DiscardCause::Expired => write!(f, "Expired"),
171 DiscardCause::InputConsumed => write!(f, "InputConsumed"),
172 DiscardCause::DiscardedInitialState => write!(f, "DiscardedInitialState"),
173 DiscardCause::Stale => write!(f, "Stale"),
174 DiscardCause::Superseded => write!(f, "Superseded"),
175 }
176 }
177}
178
179impl Serializable for DiscardCause {
180 fn write_into<W: ByteWriter>(&self, target: &mut W) {
181 match self {
182 DiscardCause::Expired => target.write_u8(0),
183 DiscardCause::InputConsumed => target.write_u8(1),
184 DiscardCause::DiscardedInitialState => target.write_u8(2),
185 DiscardCause::Stale => target.write_u8(3),
186 DiscardCause::Superseded => target.write_u8(4),
187 }
188 }
189}
190
191impl Deserializable for DiscardCause {
192 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
193 match source.read_u8()? {
194 0 => Ok(DiscardCause::Expired),
195 1 => Ok(DiscardCause::InputConsumed),
196 2 => Ok(DiscardCause::DiscardedInitialState),
197 3 => Ok(DiscardCause::Stale),
198 4 => Ok(DiscardCause::Superseded),
199 _ => Err(DeserializationError::InvalidValue("Invalid discard cause".to_string())),
200 }
201 }
202}
203
204#[derive(Debug, Clone, PartialEq)]
206pub enum TransactionStatus {
207 Pending,
209 Committed {
211 block_number: BlockNumber,
213 commit_timestamp: u64,
215 },
216 Discarded(DiscardCause),
218}
219
220pub enum TransactionStatusVariant {
221 Pending = 0,
222 Committed = 1,
223 Discarded = 2,
224}
225
226impl TransactionStatus {
227 pub const fn variant(&self) -> TransactionStatusVariant {
228 match self {
229 TransactionStatus::Pending => TransactionStatusVariant::Pending,
230 TransactionStatus::Committed { .. } => TransactionStatusVariant::Committed,
231 TransactionStatus::Discarded(_) => TransactionStatusVariant::Discarded,
232 }
233 }
234}
235
236impl fmt::Display for TransactionStatus {
237 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238 match self {
239 TransactionStatus::Pending => write!(f, "Pending"),
240 TransactionStatus::Committed { block_number, .. } => {
241 write!(f, "Committed (Block: {block_number})")
242 },
243 TransactionStatus::Discarded(cause) => write!(f, "Discarded ({cause})"),
244 }
245 }
246}
247
248impl Serializable for TransactionStatus {
249 fn write_into<W: ByteWriter>(&self, target: &mut W) {
250 match self {
251 TransactionStatus::Pending => target.write_u8(self.variant() as u8),
252 TransactionStatus::Committed { block_number, commit_timestamp } => {
253 target.write_u8(self.variant() as u8);
254 block_number.write_into(target);
255 commit_timestamp.write_into(target);
256 },
257 TransactionStatus::Discarded(cause) => {
258 target.write_u8(self.variant() as u8);
259 cause.write_into(target);
260 },
261 }
262 }
263}
264
265impl Deserializable for TransactionStatus {
266 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
267 match source.read_u8()? {
268 variant if variant == TransactionStatusVariant::Pending as u8 => {
269 Ok(TransactionStatus::Pending)
270 },
271 variant if variant == TransactionStatusVariant::Committed as u8 => {
272 let block_number = BlockNumber::read_from(source)?;
273 let commit_timestamp = source.read_u64()?;
274 Ok(TransactionStatus::Committed { block_number, commit_timestamp })
275 },
276 variant if variant == TransactionStatusVariant::Discarded as u8 => {
277 let cause = DiscardCause::read_from(source)?;
278 Ok(TransactionStatus::Discarded(cause))
279 },
280 _ => Err(DeserializationError::InvalidValue("Invalid transaction status".to_string())),
281 }
282 }
283}