Skip to main content

thruput_native_exec/
transact_result.rs

1use crate::frame_result::FrameResult;
2use thruput_event::{EmittedEvent, Event, TypedEvent};
3use thruput_exn::Exn;
4use thruput_xdr::event::TxEmittedEvent;
5use thruput_xdr::exec_result::ExecResult;
6use thruput_xdr::types::ContractAddress;
7
8/// Result of a full transaction: root frame result + commit outcome.
9/// Events are accessed via the root frame's per-frame events.
10#[derive(Debug)]
11pub struct TransactResult {
12    frame: FrameResult,
13    exec_result: ExecResult,
14}
15
16impl TransactResult {
17    /// Combine a root-frame result with the transaction's commit outcome.
18    pub fn new(frame: FrameResult, exec_result: ExecResult) -> Self {
19        Self { frame, exec_result }
20    }
21
22    /// The panic that faulted the root frame's body, if it faulted with one.
23    /// See [`FrameResult::fault_panic`] for what `None` covers — notably, a
24    /// fault caused by a called contract rather than by this frame's own body.
25    pub fn fault_panic(&self) -> Option<&crate::invoke_error::PanicRecord> {
26        self.frame.fault_panic()
27    }
28
29    /// Just the message from [`fault_panic`](Self::fault_panic).
30    pub fn fault_message(&self) -> Option<&str> {
31        self.frame.fault_message()
32    }
33
34    /// The root frame's [`FrameResult::rv32_stack_usage`]: the stack record
35    /// of the most recent rv32 call made under the root frame.
36    pub fn rv32_stack_usage(
37        &self,
38    ) -> Option<crate::rv32_stack_usage::Rv32StackUsage> {
39        self.frame.rv32_stack_usage()
40    }
41
42    /// The root frame's [`FrameResult::rv32_stack_usage_records`]: the
43    /// rv32 calls made under it, in the order they finished.
44    pub fn rv32_stack_usage_records(
45        &self,
46    ) -> &[crate::rv32_stack_usage::Rv32StackUsage] {
47        self.frame.rv32_stack_usage_records()
48    }
49
50    /// True if the root frame returned successfully AND the commit was OK.
51    pub fn is_success(&self) -> bool {
52        self.frame.is_success() && self.exec_result == ExecResult::ExecResultOK
53    }
54
55    /// Asserts the root frame returned successfully AND the transaction committed OK.
56    #[track_caller]
57    pub fn assert_success(&self) {
58        self.frame.assert_success();
59        assert_eq!(
60            self.exec_result,
61            ExecResult::ExecResultOK,
62            "expected successful commit, got {:?}",
63            self.exec_result
64        );
65    }
66
67    /// Assert the root frame aborted. Frame-only check; the commit outcome
68    /// is not consulted.
69    #[track_caller]
70    pub fn assert_abort(&self) {
71        self.frame.assert_abort();
72    }
73
74    /// Assert the root frame faulted. Frame-only check.
75    #[track_caller]
76    pub fn assert_fault(&self) {
77        self.frame.assert_fault();
78    }
79
80    /// Assert the root frame returned successfully and decode its return buffer.
81    ///
82    /// This checks only the frame outcome (contract execution), not the
83    /// database commit. Use `assert_success()` when you need to verify both
84    /// frame return *and* commit. This separation lets you inspect the return
85    /// value even when the commit is expected to fail or hasn't happened yet.
86    #[track_caller]
87    pub fn assert_return_with<R>(&self, check: impl FnOnce(&R))
88    where
89        R: xdrs::ReadXDROwned,
90    {
91        self.frame.assert_return_with(check);
92    }
93
94    /// Assert the root frame returned successfully and decode its return
95    /// buffer, returning the decoded value. Frame-only check, like
96    /// `assert_return_with`.
97    #[track_caller]
98    pub fn assert_return_as<R>(&self) -> R
99    where
100        R: xdrs::ReadXDROwned,
101    {
102        self.frame.assert_return_as()
103    }
104
105    /// Assert the root frame aborted with a thrown `E` and return it; see
106    /// [`FrameResult::assert_abort_as`]. Frame-only check (commit status not
107    /// checked).
108    #[track_caller]
109    pub fn assert_abort_as<E>(&self) -> E
110    where
111        E: Exn + xdrs::ReadXDROwned,
112    {
113        self.frame.assert_abort_as()
114    }
115
116    /// Asserts the root frame succeeded but the DB commit step failed.
117    #[track_caller]
118    pub fn assert_commit_failed(&self) {
119        self.frame.assert_success();
120        assert_ne!(
121            self.exec_result,
122            ExecResult::ExecResultOK,
123            "expected commit failure, but commit succeeded"
124        );
125    }
126
127    /// Assert the root frame succeeded but the commit failed with
128    /// `ExecResultTxNonDeterministicError` (a storage conflict).
129    #[track_caller]
130    pub fn assert_commit_failed_nondeterministically(&self) {
131        self.frame.assert_success();
132        assert_eq!(
133            self.exec_result,
134            ExecResult::ExecResultTxNonDeterministicError,
135            "expected non-deterministic commit failure"
136        );
137    }
138
139    /// The transaction's commit outcome.
140    pub fn exec_result(&self) -> &ExecResult {
141        &self.exec_result
142    }
143
144    /// The root frame's result.
145    pub fn frame(&self) -> &FrameResult {
146        &self.frame
147    }
148
149    // -- Events (delegated to root frame) --
150
151    /// The events the root frame emitted; see [`FrameResult::events`].
152    pub fn events(&self) -> &[TxEmittedEvent] {
153        self.frame.events()
154    }
155
156    /// Assert exactly `expected` events were emitted by the root frame.
157    #[track_caller]
158    pub fn assert_event_count(&self, expected: usize) {
159        self.frame.assert_event_count(expected);
160    }
161
162    /// Assert the root frame emitted no events.
163    #[track_caller]
164    pub fn assert_no_events(&self) {
165        self.frame.assert_no_events();
166    }
167
168    /// All raw events emitted at the given address by the root frame.
169    pub fn events_from(
170        &self,
171        address: &ContractAddress,
172    ) -> Vec<&TxEmittedEvent> {
173        self.frame.events_from(address)
174    }
175
176    /// See [`FrameResult::filter_events`] for the matching rules.
177    pub fn filter_events<E>(
178        &self,
179        address: &ContractAddress,
180    ) -> Vec<TypedEvent<'_, E>>
181    where
182        E: Event,
183    {
184        self.frame.filter_events(address)
185    }
186
187    /// Inspect a raw emitted event by index; see
188    /// [`FrameResult::assert_raw_event`].
189    #[track_caller]
190    pub fn assert_raw_event(
191        &self,
192        index: usize,
193        check: impl FnOnce(&EmittedEvent<'_>),
194    ) {
195        self.frame.assert_raw_event(index, check);
196    }
197
198    /// Decode an event as a typed event and pass it to `check`; see
199    /// [`FrameResult::assert_event`].
200    #[track_caller]
201    pub fn assert_event<E>(
202        &self,
203        index: usize,
204        check: impl FnOnce(&TypedEvent<'_, E>),
205    ) where
206        E: Event,
207    {
208        self.frame.assert_event::<E>(index, check);
209    }
210
211    /// See [`FrameResult::assert_emitted`] for the matching rules.
212    #[track_caller]
213    pub fn assert_emitted<E>(
214        &self,
215        address: &ContractAddress,
216        predicate: impl Fn(&TypedEvent<'_, E>) -> bool,
217    ) where
218        E: Event,
219    {
220        self.frame.assert_emitted(address, predicate);
221    }
222}