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