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}
151
152impl DiscardCause {
153 pub fn from_string(cause: &str) -> Result<Self, DeserializationError> {
154 match cause {
155 "Expired" => Ok(DiscardCause::Expired),
156 "InputConsumed" => Ok(DiscardCause::InputConsumed),
157 "DiscardedInitialState" => Ok(DiscardCause::DiscardedInitialState),
158 "Stale" => Ok(DiscardCause::Stale),
159 _ => Err(DeserializationError::InvalidValue(format!("Invalid discard cause: {cause}"))),
160 }
161 }
162}
163
164impl fmt::Display for DiscardCause {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 match self {
167 DiscardCause::Expired => write!(f, "Expired"),
168 DiscardCause::InputConsumed => write!(f, "InputConsumed"),
169 DiscardCause::DiscardedInitialState => write!(f, "DiscardedInitialState"),
170 DiscardCause::Stale => write!(f, "Stale"),
171 }
172 }
173}
174
175impl Serializable for DiscardCause {
176 fn write_into<W: ByteWriter>(&self, target: &mut W) {
177 match self {
178 DiscardCause::Expired => target.write_u8(0),
179 DiscardCause::InputConsumed => target.write_u8(1),
180 DiscardCause::DiscardedInitialState => target.write_u8(2),
181 DiscardCause::Stale => target.write_u8(3),
182 }
183 }
184}
185
186impl Deserializable for DiscardCause {
187 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
188 match source.read_u8()? {
189 0 => Ok(DiscardCause::Expired),
190 1 => Ok(DiscardCause::InputConsumed),
191 2 => Ok(DiscardCause::DiscardedInitialState),
192 3 => Ok(DiscardCause::Stale),
193 _ => Err(DeserializationError::InvalidValue("Invalid discard cause".to_string())),
194 }
195 }
196}
197
198#[derive(Debug, Clone, PartialEq)]
200pub enum TransactionStatus {
201 Pending,
203 Committed {
205 block_number: BlockNumber,
207 commit_timestamp: u64,
209 },
210 Discarded(DiscardCause),
212}
213
214pub enum TransactionStatusVariant {
215 Pending = 0,
216 Committed = 1,
217 Discarded = 2,
218}
219
220impl TransactionStatus {
221 pub const fn variant(&self) -> TransactionStatusVariant {
222 match self {
223 TransactionStatus::Pending => TransactionStatusVariant::Pending,
224 TransactionStatus::Committed { .. } => TransactionStatusVariant::Committed,
225 TransactionStatus::Discarded(_) => TransactionStatusVariant::Discarded,
226 }
227 }
228}
229
230impl fmt::Display for TransactionStatus {
231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232 match self {
233 TransactionStatus::Pending => write!(f, "Pending"),
234 TransactionStatus::Committed { block_number, .. } => {
235 write!(f, "Committed (Block: {block_number})")
236 },
237 TransactionStatus::Discarded(cause) => write!(f, "Discarded ({cause})"),
238 }
239 }
240}
241
242impl Serializable for TransactionStatus {
243 fn write_into<W: ByteWriter>(&self, target: &mut W) {
244 match self {
245 TransactionStatus::Pending => target.write_u8(self.variant() as u8),
246 TransactionStatus::Committed { block_number, commit_timestamp } => {
247 target.write_u8(self.variant() as u8);
248 block_number.write_into(target);
249 commit_timestamp.write_into(target);
250 },
251 TransactionStatus::Discarded(cause) => {
252 target.write_u8(self.variant() as u8);
253 cause.write_into(target);
254 },
255 }
256 }
257}
258
259impl Deserializable for TransactionStatus {
260 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
261 match source.read_u8()? {
262 variant if variant == TransactionStatusVariant::Pending as u8 => {
263 Ok(TransactionStatus::Pending)
264 },
265 variant if variant == TransactionStatusVariant::Committed as u8 => {
266 let block_number = BlockNumber::read_from(source)?;
267 let commit_timestamp = source.read_u64()?;
268 Ok(TransactionStatus::Committed { block_number, commit_timestamp })
269 },
270 variant if variant == TransactionStatusVariant::Discarded as u8 => {
271 let cause = DiscardCause::read_from(source)?;
272 Ok(TransactionStatus::Discarded(cause))
273 },
274 _ => Err(DeserializationError::InvalidValue("Invalid transaction status".to_string())),
275 }
276 }
277}