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
use crate::runner::error::{DecodeError, RunnerError};
use cosmrs::proto::cosmos::base::abci::v1beta1::{GasInfo, TxMsgData};
use cosmrs::proto::tendermint::abci::ResponseDeliverTx;
use cosmrs::rpc::endpoint::broadcast::tx_commit::Response as TxCommitResponse;
use cosmwasm_std::{Attribute, Event};
use prost::Message;
use std::ffi::CString;
use std::str::Utf8Error;

pub type RunnerResult<T> = Result<T, RunnerError>;
pub type RunnerExecuteResult<R> = Result<ExecuteResponse<R>, RunnerError>;

#[derive(Debug, Clone, PartialEq)]
pub struct ExecuteResponse<R>
where
    R: prost::Message + Default,
{
    pub data: R,
    pub raw_data: Vec<u8>,
    pub events: Vec<Event>,
    pub gas_info: GasInfo,
}

impl<R> TryFrom<ResponseDeliverTx> for ExecuteResponse<R>
where
    R: prost::Message + Default,
{
    type Error = RunnerError;

    fn try_from(res: ResponseDeliverTx) -> Result<Self, Self::Error> {
        let tx_msg_data =
            TxMsgData::decode(res.data.as_slice()).map_err(DecodeError::ProtoDecodeError)?;

        let msg_data = &tx_msg_data
            .data
            // since this tx contains exactly 1 msg
            // when getting none of them, that means error
            .get(0)
            .ok_or(RunnerError::ExecuteError { msg: res.log })?;

        let data = R::decode(msg_data.data.as_slice()).map_err(DecodeError::ProtoDecodeError)?;

        let events = res
            .events
            .into_iter()
            .map(|e| -> Result<Event, DecodeError> {
                Ok(Event::new(e.r#type.to_string()).add_attributes(
                    e.attributes
                        .into_iter()
                        .map(|a| -> Result<Attribute, Utf8Error> {
                            Ok(Attribute {
                                key: std::str::from_utf8(a.key.as_slice())?.to_string(),
                                value: std::str::from_utf8(a.value.as_slice())?.to_string(),
                            })
                        })
                        .collect::<Result<Vec<Attribute>, Utf8Error>>()?,
                ))
            })
            .collect::<Result<Vec<Event>, DecodeError>>()?;

        Ok(ExecuteResponse {
            data,
            raw_data: res.data,
            events,
            gas_info: GasInfo {
                gas_wanted: res.gas_wanted as u64,
                gas_used: res.gas_used as u64,
            },
        })
    }
}

impl<R> TryFrom<TxCommitResponse> for ExecuteResponse<R>
where
    R: prost::Message + Default,
{
    type Error = RunnerError;

    fn try_from(tx_commit_response: TxCommitResponse) -> Result<Self, Self::Error> {
        let res = tx_commit_response.deliver_tx;
        let tx_msg_data = TxMsgData::decode(res.data.clone().unwrap().value().as_slice())
            .map_err(DecodeError::ProtoDecodeError)?;

        let msg_data = &tx_msg_data
            .data
            // since this tx contains exactly 1 msg
            // when getting none of them, that means error
            .get(0)
            .ok_or(RunnerError::ExecuteError {
                msg: res.log.to_string(),
            })?;

        let data = R::decode(msg_data.data.as_slice()).map_err(DecodeError::ProtoDecodeError)?;

        let events = res
            .events
            .into_iter()
            .map(|e| -> Result<Event, DecodeError> {
                Ok(Event::new(e.type_str).add_attributes(
                    e.attributes
                        .into_iter()
                        .map(|a| -> Result<Attribute, Utf8Error> {
                            Ok(Attribute {
                                key: a.key.to_string(),
                                value: a.value.to_string(),
                            })
                        })
                        .collect::<Result<Vec<Attribute>, Utf8Error>>()?,
                ))
            })
            .collect::<Result<Vec<Event>, DecodeError>>()?;

        Ok(Self {
            data,
            raw_data: res.data.unwrap().value().clone(),
            events,
            gas_info: GasInfo {
                gas_wanted: res.gas_wanted.value(),
                gas_used: res.gas_used.value(),
            },
        })
    }
}

/// `RawResult` facilitates type conversions between Go and Rust,
///
/// Since Go struct could not be exposed via cgo due to limitations on
/// its unstable behavior of its memory layout.
/// So, apart from passing primitive types, we need to:
///
///   Go { T -> bytes(T) -> base64 -> *c_char }
///                      ↓
///   Rust { *c_char -> base64 -> bytes(T') -> T' }
///
/// Where T and T' are corresponding data structures, regardless of their encoding
/// in their respective language plus error information.
///
/// Resulted bytes are tagged by prepending 4 bytes to byte array
/// before base64 encoded. The prepended byte represents
///   0 -> Ok
///   1 -> QueryError
///   2 -> ExecuteError
///
/// The rest are undefined and remaining spaces are reserved for future use.
#[derive(Debug)]
pub struct RawResult(Result<Vec<u8>, RunnerError>);

impl RawResult {
    /// Convert ptr to AppResult. Check the first byte tag before decoding the rest of the bytes into expected type
    ///
    /// # Safety
    ///
    /// `ptr` must contain a valid pointer to a null-terminated C string with base64 encoded bytes.
    /// The decoded content must be a valid utf-8 string.
    pub unsafe fn from_ptr(ptr: *mut std::os::raw::c_char) -> Option<Self> {
        if ptr.is_null() {
            return None;
        }

        let c_string = unsafe { CString::from_raw(ptr) };
        let base64_bytes = c_string.to_bytes();
        let bytes = base64::decode(base64_bytes).unwrap();
        let code = bytes[0];
        let content = &bytes[1..];

        if code == 0 {
            Some(Self(Ok(content.to_vec())))
        } else {
            let content_string = CString::new(content)
                .unwrap()
                .to_str()
                .expect("Go code must encode valid UTF-8 string")
                .to_string();

            let error = match code {
                1 => RunnerError::QueryError {
                    msg: content_string,
                },
                2 => RunnerError::ExecuteError {
                    msg: content_string,
                },
                _ => panic!("undefined code: {}", code),
            };
            Some(Self(Err(error)))
        }
    }

    /// Convert ptr to AppResult. Use this function only when it is sure that the
    /// pointer is not a null pointer.
    ///
    /// # Safety
    /// There is a potential null pointer here, need to be extra careful before
    /// calling this function
    pub unsafe fn from_non_null_ptr(ptr: *mut std::os::raw::c_char) -> Self {
        Self::from_ptr(ptr).expect("Must ensure that the pointer is not null")
    }

    pub fn into_result(self) -> Result<Vec<u8>, RunnerError> {
        self.0
    }
}