multiversx_chain_vm/host/context/
tx_result.rs1use std::fmt;
2
3use multiversx_chain_core::types::ReturnCode;
4
5use crate::{
6 host::context::{TxFunctionName, TxInput},
7 vm_err_msg,
8};
9
10use super::{AsyncCallTxData, GasUsed, TxErrorTrace, TxLog, TxPanic, TxResultCalls};
11
12#[derive(Clone, Debug)]
13#[must_use]
14pub struct TxResult {
15 pub result_status: ReturnCode,
16 pub result_message: String,
17 pub result_values: Vec<Vec<u8>>,
18
19 pub esdt_transfer_log: Option<TxLog>,
31
32 pub result_logs: Vec<TxLog>,
34
35 pub gas_used: GasUsed,
36
37 pub error_trace: Vec<TxErrorTrace>,
45
46 pub pending_calls: TxResultCalls,
50
51 pub all_calls: Vec<AsyncCallTxData>,
55}
56
57impl Default for TxResult {
58 fn default() -> Self {
59 TxResult {
60 result_status: ReturnCode::Success,
61 result_message: String::new(),
62 result_values: Vec::new(),
63 esdt_transfer_log: None,
64 result_logs: Vec::new(),
65 gas_used: GasUsed::Unknown,
66 error_trace: Vec::new(),
67 pending_calls: TxResultCalls::empty(),
68 all_calls: Vec::new(),
69 }
70 }
71}
72
73impl TxResult {
74 pub fn empty() -> TxResult {
75 TxResult::default()
76 }
77
78 pub fn print(&self) {
79 println!("{self}");
80 }
81
82 pub fn from_panic_obj(panic_obj: &TxPanic) -> Self {
83 TxResult::from_error(panic_obj.status, &panic_obj.message)
84 }
85
86 pub fn from_panic_string(s: &str) -> Self {
87 TxResult::from_error(ReturnCode::UserError, s)
88 }
89
90 pub fn from_unknown_panic() -> Self {
91 Self::from_panic_string("")
92 }
93
94 pub fn from_error<S>(return_code: ReturnCode, result_message: S) -> Self
95 where
96 S: Into<String>,
97 {
98 TxResult {
99 result_status: return_code,
100 result_message: result_message.into(),
101 ..Default::default()
102 }
103 }
104
105 pub fn from_vm_error<S>(result_message: S) -> Self
106 where
107 S: Into<String>,
108 {
109 TxResult::from_error(ReturnCode::ExecutionFailed, result_message)
110 }
111
112 pub fn from_function_not_found() -> Self {
113 TxResult::from_error(ReturnCode::FunctionNotFound, vm_err_msg::FUNCTION_NOT_FOUND)
114 }
115
116 pub fn all_logs(&self) -> Vec<&TxLog> {
118 let mut all_logs = Vec::new();
119 if let Some(esdt_log) = &self.esdt_transfer_log {
120 all_logs.push(esdt_log);
121 }
122 for log in &self.result_logs {
123 all_logs.push(log);
124 }
125 all_logs
126 }
127
128 pub fn append_all_logs(&mut self, source: &mut TxResult) {
130 if let Some(esdt_log) = source.esdt_transfer_log.take() {
131 self.result_logs.push(esdt_log);
132 }
133 self.result_logs.append(&mut source.result_logs);
134 }
135
136 pub fn merge_after_sync_call(&mut self, sync_call_result: &TxResult) {
137 self.result_values
138 .extend_from_slice(&sync_call_result.result_values);
139 if let Some(transfer_log) = &sync_call_result.esdt_transfer_log {
140 self.result_logs.push(transfer_log.clone());
141 }
142 self.result_logs
143 .extend_from_slice(&sync_call_result.result_logs);
144 self.error_trace
145 .extend_from_slice(&sync_call_result.error_trace);
146 if let Some(sync_result_async) = &sync_call_result.pending_calls.async_call {
147 assert!(
148 self.pending_calls.async_call.is_none(),
149 "Multiple async calls not supported"
150 );
151 self.pending_calls.async_call = Some(sync_result_async.clone());
152 }
153 }
154
155 pub fn merge_error(&mut self, error_tx_result: TxResult) {
161 let mut old_value = std::mem::replace(self, error_tx_result);
162
163 if let Some(transfer_log) = old_value.esdt_transfer_log {
164 self.result_logs.push(transfer_log);
165 }
166 self.error_trace.append(&mut old_value.error_trace);
167 }
168
169 pub fn assert_ok(&self) {
170 assert!(
171 self.result_status.is_success(),
172 "Tx success expected, but failed. Status: {}, message: \"{}\"",
173 self.result_status,
174 self.result_message.as_str()
175 );
176 }
177
178 pub fn assert_error(&self, expected_status: u64, expected_message: &str) {
179 assert!(
180 self.result_message.as_str() == expected_message,
181 "Tx error message mismatch. Want status {}, message \"{}\". Have status {}, message \"{}\"",
182 expected_status,
183 expected_message,
184 self.result_status,
185 self.result_message.as_str()
186 );
187 assert!(
188 self.result_status.as_u64() == expected_status,
189 "Tx error status mismatch. Want status {}, message \"{}\". Have status {}, message \"{}\"",
190 expected_status,
191 expected_message,
192 self.result_status,
193 self.result_message.as_str()
194 );
195 }
196
197 pub fn assert_user_error(&self, expected_message: &str) {
198 self.assert_error(ReturnCode::UserError.as_u64(), expected_message);
199 }
200}
201
202impl fmt::Display for TxResult {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 let results_hex: Vec<String> = self
205 .result_values
206 .iter()
207 .map(|r| format!("0x{}", hex::encode(r)))
208 .collect();
209 write!(
210 f,
211 "TxResult {{\n\tresult_status: {},\n\tresult_values:{results_hex:?}\n}}",
212 self.result_status
213 )
214 }
215}
216
217impl TxResult {
218 pub fn result_values_to_string(&self) -> String {
219 result_values_to_string(&self.result_values)
220 }
221}
222
223pub fn result_values_to_string(values: &[Vec<u8>]) -> String {
224 itertools::join(
225 values.iter().map(|val| format!("0x{}", hex::encode(val))),
226 ", ",
227 )
228}
229
230impl TxResult {
231 pub fn append_internal_vm_errors_event_log(&mut self, input: &TxInput) {
232 if self.error_trace.is_empty() {
233 return;
234 }
235
236 self.result_logs.push(TxLog {
237 address: input.from.clone(),
238 endpoint: TxFunctionName::from_static("internalVMErrors"),
239 topics: vec![input.to.to_vec(), input.func_name.to_bytes()],
240 data: self
241 .error_trace
242 .iter()
243 .map(|err| err.error_trace_message.as_bytes().to_vec())
244 .collect(),
245 });
246 }
247}