test_tube/runner/
result.rs1use crate::runner::error::{DecodeError, RunnerError};
2use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
3use base64::Engine;
4use cosmrs::proto::cosmos::base::abci::v1beta1::{GasInfo, TxMsgData};
5use cosmrs::proto::tendermint::abci::ResponseFinalizeBlock;
6use cosmrs::proto::tendermint::v0_37::abci::ResponseDeliverTx;
7use cosmrs::rpc::endpoint::broadcast::tx_commit::Response as TxCommitResponse;
8use cosmrs::tendermint::abci::types::ExecTxResult;
9use cosmwasm_std::{Attribute, Event};
10use prost::Message;
11use std::ffi::CString;
12use std::str::Utf8Error;
13
14pub type RunnerResult<T> = Result<T, RunnerError>;
15pub type RunnerExecuteResult<R> = Result<ExecuteResponse<R>, RunnerError>;
16
17#[derive(Debug, Clone, PartialEq)]
18pub struct ExecuteResponse<R>
19where
20 R: prost::Message + Default,
21{
22 pub data: R,
23 pub raw_data: Vec<u8>,
24 pub events: Vec<Event>,
25 pub gas_info: GasInfo,
26}
27
28impl<R> TryFrom<ExecTxResult> for ExecuteResponse<R>
29where
30 R: prost::Message + Default,
31{
32 type Error = RunnerError;
33
34 fn try_from(res: ExecTxResult) -> Result<Self, Self::Error> {
35 let tx_msg_data =
36 TxMsgData::decode(res.data.as_ref()).map_err(DecodeError::ProtoDecodeError)?;
37
38 let msg_data = tx_msg_data
39 .msg_responses
40 .first()
43 .ok_or(RunnerError::ExecuteError { msg: res.log })?;
44
45 let data = R::decode(msg_data.value.as_slice()).map_err(DecodeError::ProtoDecodeError)?;
46
47 let events = res
48 .events
49 .into_iter()
50 .map(|e| -> Result<Event, DecodeError> {
51 Ok(Event::new(e.kind).add_attributes(
52 e.attributes
53 .into_iter()
54 .map(|a| -> Result<Attribute, Utf8Error> {
55 Ok(Attribute {
56 key: std::str::from_utf8(a.key_bytes())?.to_string(),
57 value: std::str::from_utf8(a.value_bytes())?.to_string(),
58 })
59 })
60 .collect::<Result<Vec<Attribute>, Utf8Error>>()?,
61 ))
62 })
63 .collect::<Result<Vec<Event>, DecodeError>>()?;
64
65 Ok(ExecuteResponse {
66 data,
67 raw_data: res.data.to_vec(),
68 events,
69 gas_info: GasInfo {
70 gas_wanted: res.gas_wanted as u64,
71 gas_used: res.gas_used as u64,
72 },
73 })
74 }
75}
76
77impl<R> TryFrom<TxCommitResponse> for ExecuteResponse<R>
78where
79 R: prost::Message + Default,
80{
81 type Error = RunnerError;
82
83 fn try_from(tx_commit_response: TxCommitResponse) -> Result<Self, Self::Error> {
84 let res = tx_commit_response.tx_result;
85 let tx_msg_data =
86 TxMsgData::decode(res.data.as_ref()).map_err(DecodeError::ProtoDecodeError)?;
87
88 let msg_data = tx_msg_data
89 .msg_responses
90 .first()
93 .ok_or(RunnerError::ExecuteError { msg: res.log })?;
94
95 let data = R::decode(msg_data.value.as_slice()).map_err(DecodeError::ProtoDecodeError)?;
96
97 let events = res
98 .events
99 .into_iter()
100 .map(|e| -> Result<Event, DecodeError> {
101 Ok(Event::new(e.kind).add_attributes(
102 e.attributes
103 .into_iter()
104 .map(|a| -> Result<Attribute, Utf8Error> {
105 Ok(Attribute {
106 key: std::str::from_utf8(a.key_bytes())?.to_string(),
107 value: std::str::from_utf8(a.value_bytes())?.to_string(),
108 })
109 })
110 .collect::<Result<Vec<Attribute>, Utf8Error>>()?,
111 ))
112 })
113 .collect::<Result<Vec<Event>, DecodeError>>()?;
114
115 Ok(Self {
116 data,
117 raw_data: res.data.to_vec(),
118 events,
119 gas_info: GasInfo {
120 gas_wanted: res.gas_wanted as u64,
121 gas_used: res.gas_used as u64,
122 },
123 })
124 }
125}
126
127impl<R> TryFrom<ResponseDeliverTx> for ExecuteResponse<R>
128where
129 R: prost::Message + Default,
130{
131 type Error = RunnerError;
132
133 fn try_from(res: ResponseDeliverTx) -> Result<Self, Self::Error> {
134 let tx_msg_data =
135 TxMsgData::decode(res.data.as_ref()).map_err(DecodeError::ProtoDecodeError)?;
136
137 let msg_data = tx_msg_data
138 .msg_responses
139 .first()
142 .ok_or(RunnerError::ExecuteError { msg: res.log })?;
143
144 let data = R::decode(msg_data.value.as_slice()).map_err(DecodeError::ProtoDecodeError)?;
145
146 let events = res
147 .events
148 .into_iter()
149 .map(|e| -> Result<Event, DecodeError> {
150 Ok(Event::new(e.r#type).add_attributes(
151 e.attributes
152 .into_iter()
153 .map(|a| -> Result<Attribute, Utf8Error> {
154 Ok(Attribute {
155 key: a.key.to_string(),
156 value: a.value.to_string(),
157 })
158 })
159 .collect::<Result<Vec<Attribute>, Utf8Error>>()?,
160 ))
161 })
162 .collect::<Result<Vec<Event>, DecodeError>>()?;
163
164 Ok(Self {
165 data,
166 raw_data: res.data.to_vec(),
167 events,
168 gas_info: GasInfo {
169 gas_wanted: res.gas_wanted as u64,
170 gas_used: res.gas_used as u64,
171 },
172 })
173 }
174}
175
176impl<R> TryFrom<ResponseFinalizeBlock> for ExecuteResponse<R>
177where
178 R: prost::Message + Default,
179{
180 type Error = RunnerError;
181
182 fn try_from(res: ResponseFinalizeBlock) -> Result<Self, Self::Error> {
183 let tx_results = res.tx_results;
184 let first_tx_result = tx_results
185 .first()
186 .expect("tx_results should have at least one element");
187
188 let tx_msg_data = TxMsgData::decode(first_tx_result.data.clone().as_ref())
189 .map_err(DecodeError::ProtoDecodeError)?;
190
191 let msg_data = tx_msg_data
192 .msg_responses
193 .first()
196 .ok_or(RunnerError::ExecuteError {
197 msg: first_tx_result.log.clone(),
198 })?;
199
200 let data = R::decode(msg_data.value.as_slice()).map_err(DecodeError::ProtoDecodeError)?;
201
202 let events = first_tx_result
203 .events
204 .clone()
205 .into_iter()
206 .map(|e| -> Result<Event, DecodeError> {
207 Ok(Event::new(e.r#type).add_attributes(
208 e.attributes
209 .into_iter()
210 .map(|a| -> Result<Attribute, Utf8Error> {
211 Ok(Attribute {
212 key: a.key.to_string(),
213 value: a.value.to_string(),
214 })
215 })
216 .collect::<Result<Vec<Attribute>, Utf8Error>>()?,
217 ))
218 })
219 .collect::<Result<Vec<Event>, DecodeError>>()?;
220
221 let gas_wanted = first_tx_result.gas_used as u64;
222 let gas_used = first_tx_result.gas_used as u64;
223
224 Ok(Self {
225 data,
226 raw_data: msg_data.value.to_vec(),
227 events,
228 gas_info: GasInfo {
229 gas_wanted,
230 gas_used,
231 },
232 })
233 }
234}
235
236#[derive(Debug)]
257pub struct RawResult(Result<Vec<u8>, RunnerError>);
258
259impl RawResult {
260 pub unsafe fn from_ptr(ptr: *mut std::os::raw::c_char) -> Option<Self> {
267 if ptr.is_null() {
268 return None;
269 }
270
271 let c_string = unsafe { CString::from_raw(ptr) };
272 let base64_bytes = c_string.to_bytes();
273 let bytes = BASE64_STANDARD.decode(base64_bytes).unwrap();
274 let code = bytes[0];
275 let content = &bytes[1..];
276
277 if code == 0 {
278 Some(Self(Ok(content.to_vec())))
279 } else {
280 let content_string = CString::new(content).unwrap().to_string_lossy().to_string();
281
282 let error = match code {
283 1 => RunnerError::QueryError {
284 msg: content_string,
285 },
286 2 => RunnerError::ExecuteError {
287 msg: content_string,
288 },
289 _ => panic!("undefined code: {}", code),
290 };
291 Some(Self(Err(error)))
292 }
293 }
294
295 pub unsafe fn from_non_null_ptr(ptr: *mut std::os::raw::c_char) -> Self {
302 Self::from_ptr(ptr).expect("Must ensure that the pointer is not null")
303 }
304
305 pub fn into_result(self) -> Result<Vec<u8>, RunnerError> {
306 self.0
307 }
308}