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
.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
.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(),
},
})
}
}
#[derive(Debug)]
pub struct RawResult(Result<Vec<u8>, RunnerError>);
impl RawResult {
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)))
}
}
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
}
}