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