use thiserror::Error;
use cosmwasm_std::Binary;
const WIRE_TYPE_LENGTH_DELIMITED: u8 = 2;
const VARINT_MAX_BYTES: usize = 9;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MsgInstantiateContractResponse {
pub contract_address: String,
pub data: Option<Binary>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MsgExecuteContractResponse {
pub data: Option<Binary>,
}
fn parse_protobuf_varint(data: &mut Vec<u8>, field_number: u8) -> Result<usize, ParseReplyError> {
let data_len = data.len();
let mut len: u64 = 0;
let mut i = 0;
while i < VARINT_MAX_BYTES {
if data_len == i {
return Err(ParseReplyError::ParseFailure(format!(
"failed to decode Protobuf message: field #{}: varint data too short",
field_number
)));
}
len += ((data[i] & 0x7f) as u64) << (i * 7);
if data[i] & 0x80 == 0 {
break;
}
i += 1;
}
if i == VARINT_MAX_BYTES {
return Err(ParseReplyError::ParseFailure(format!(
"failed to decode Protobuf message: field #{}: varint data too long",
field_number
)));
}
*data = data[i + 1..].to_owned();
Ok(len as usize) }
fn parse_protobuf_length_prefixed(
data: &mut Vec<u8>,
field_number: u8,
) -> Result<Vec<u8>, ParseReplyError> {
if data.is_empty() {
return Ok(vec![]);
};
let mut rest_1 = data.split_off(1);
let wire_type = data[0] & 0b11;
let field = data[0] >> 3;
if field != field_number {
return Err(ParseReplyError::ParseFailure(format!(
"failed to decode Protobuf message: invalid field #{} for field #{}",
field, field_number
)));
}
if wire_type != WIRE_TYPE_LENGTH_DELIMITED {
return Err(ParseReplyError::ParseFailure(format!(
"failed to decode Protobuf message: field #{}: invalid wire type {}",
field_number, wire_type
)));
}
let len = parse_protobuf_varint(&mut rest_1, field_number)?;
if rest_1.len() < len {
return Err(ParseReplyError::ParseFailure(format!(
"failed to decode Protobuf message: field #{}: message too short",
field_number
)));
}
*data = rest_1.split_off(len);
Ok(rest_1)
}
fn parse_protobuf_string(data: &mut Vec<u8>, field_number: u8) -> Result<String, ParseReplyError> {
let str_field = parse_protobuf_length_prefixed(data, field_number)?;
Ok(String::from_utf8(str_field)?)
}
fn parse_protobuf_bytes(
data: &mut Vec<u8>,
field_number: u8,
) -> Result<Option<Binary>, ParseReplyError> {
let bytes_field = parse_protobuf_length_prefixed(data, field_number)?;
if bytes_field.is_empty() {
Ok(None)
} else {
Ok(Some(Binary::new(bytes_field)))
}
}
pub fn parse_instantiate_response_data(
data: &[u8],
) -> Result<MsgInstantiateContractResponse, ParseReplyError> {
let mut data = data.to_vec();
let contract_addr = parse_protobuf_string(&mut data, 1)?;
let data = parse_protobuf_bytes(&mut data, 2)?;
Ok(MsgInstantiateContractResponse {
contract_address: contract_addr,
data,
})
}
pub fn parse_execute_response_data(
data: &[u8],
) -> Result<MsgExecuteContractResponse, ParseReplyError> {
let mut data = data.to_vec();
let inner_data = parse_protobuf_bytes(&mut data, 1)?;
Ok(MsgExecuteContractResponse { data: inner_data })
}
#[derive(Error, Debug, PartialEq, Eq)]
pub enum ParseReplyError {
#[error("Failure response from sub-message: {0}")]
SubMsgFailure(String),
#[error("Invalid reply from sub-message: {0}")]
ParseFailure(String),
#[error("Error occurred while converting from UTF-8")]
BrokenUtf8(#[from] std::string::FromUtf8Error),
}
#[cfg(test)]
mod test {
use super::*;
use crate::parse_reply::ParseReplyError::{BrokenUtf8, ParseFailure};
use prost::Message;
use std::str::from_utf8;
fn encode_bytes(data: &[u8]) -> Vec<u8> {
#[derive(Clone, PartialEq, Message)]
struct ProtobufBytes {
#[prost(bytes, tag = "1")]
pub data: Vec<u8>,
}
let data = ProtobufBytes {
data: data.to_vec(),
};
let mut encoded_data = Vec::<u8>::with_capacity(data.encoded_len());
data.encode(&mut encoded_data).unwrap();
encoded_data
}
fn encode_string(data: &str) -> Vec<u8> {
#[derive(Clone, PartialEq, Message)]
struct ProtobufString {
#[prost(string, tag = "1")]
pub data: String,
}
let data = ProtobufString {
data: data.to_string(),
};
let mut encoded_data = Vec::<u8>::with_capacity(data.encoded_len());
data.encode(&mut encoded_data).unwrap();
encoded_data
}
#[derive(Clone, PartialEq, Message)]
struct MsgInstantiateContractResponse {
#[prost(string, tag = "1")]
pub contract_address: ::prost::alloc::string::String,
#[prost(bytes, tag = "2")]
pub data: ::prost::alloc::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, Message)]
struct MsgExecuteContractResponse {
#[prost(bytes, tag = "1")]
pub data: ::prost::alloc::vec::Vec<u8>,
}
#[test]
fn parse_protobuf_varint_tests() {
let field_number = 1;
let mut data = b"\x0a".to_vec();
let len = parse_protobuf_varint(&mut data, field_number).unwrap();
assert_eq!(len, 10);
let mut data = b"\x0a\x0b".to_vec();
let len = parse_protobuf_varint(&mut data, field_number).unwrap();
assert_eq!(len, 10);
assert_eq!(data, b"\x0b".to_vec());
let mut data = b"\xac\x02".to_vec();
let len = parse_protobuf_varint(&mut data, field_number).unwrap();
assert_eq!(len, 300);
let mut data = b"\xac\x02\x0c".to_vec();
let len = parse_protobuf_varint(&mut data, field_number).unwrap();
assert_eq!(len, 300);
assert_eq!(data, b"\x0c".to_vec());
let mut data = vec![];
let err = parse_protobuf_varint(&mut data, field_number).unwrap_err();
assert!(matches!(err, ParseFailure(..)));
let mut data = b"\x80".to_vec();
let err = parse_protobuf_varint(&mut data, field_number).unwrap_err();
assert!(matches!(err, ParseFailure(..)));
let mut data = b"\x80\x81\x82\x83\x84\x83\x82\x81\x80".to_vec();
let err = parse_protobuf_varint(&mut data, field_number).unwrap_err();
assert!(matches!(err, ParseFailure(..)));
}
#[test]
fn parse_protobuf_length_prefixed_tests() {
let field_number = 1;
let mut data = b"\x0a\x03abc".to_vec();
let res = parse_protobuf_length_prefixed(&mut data, field_number).unwrap();
assert_eq!(res, b"abc".to_vec());
assert_eq!(data, vec![0u8; 0]);
let mut data = b"\x0a\x03abcd".to_vec();
let res = parse_protobuf_length_prefixed(&mut data, field_number).unwrap();
assert_eq!(res, b"abc".to_vec());
assert_eq!(data, b"d".to_vec());
let mut data = [b"\x0a\xac\x02", vec![65u8; 300].as_slice()]
.concat()
.to_vec();
let res = parse_protobuf_length_prefixed(&mut data, field_number).unwrap();
assert_eq!(res, vec![65u8; 300]);
assert_eq!(data, vec![0u8; 0]);
let mut data = [b"\x0a\xac\x02", vec![65u8; 300].as_slice(), b"rest"]
.concat()
.to_vec();
let res = parse_protobuf_length_prefixed(&mut data, field_number).unwrap();
assert_eq!(res, vec![65u8; 300]);
assert_eq!(data, b"rest");
let mut data = b"\x0a\x01".to_vec();
let field_number = 1;
let err = parse_protobuf_length_prefixed(&mut data, field_number).unwrap_err();
assert!(matches!(err, ParseFailure(..)));
let mut data = b"\x0b\x01a".to_vec();
let err = parse_protobuf_length_prefixed(&mut data, field_number).unwrap_err();
assert!(matches!(err, ParseFailure(..)));
let field_number = 2;
let mut data = b"\x0a\x01a".to_vec();
let err = parse_protobuf_length_prefixed(&mut data, field_number).unwrap_err();
assert!(matches!(err, ParseFailure(..)));
}
#[test]
fn parse_protobuf_bytes_works() {
let field_number = 1;
let data = vec![];
let mut encoded_data = encode_bytes(&data);
let res = parse_protobuf_bytes(&mut encoded_data, field_number).unwrap();
assert_eq!(res, None);
let data = b"test".to_vec();
let mut encoded_data = encode_bytes(&data);
let res = parse_protobuf_bytes(&mut encoded_data, field_number).unwrap();
assert_eq!(res, Some(Binary::new(data)));
let data = vec![0x40; 300];
let mut encoded_data = encode_bytes(&data);
let res = parse_protobuf_bytes(&mut encoded_data, field_number).unwrap();
assert_eq!(res, Some(Binary::new(data)));
let field_number = 5;
let data = b"test field 5".to_vec();
let mut encoded_data = encode_bytes(&data);
encoded_data[0] = (field_number << 3) + WIRE_TYPE_LENGTH_DELIMITED;
let res = parse_protobuf_bytes(&mut encoded_data, field_number).unwrap();
assert_eq!(res, Some(Binary::new(data)));
let field_number = 1;
let test_len: usize = 4;
let data = b"test_remainder".to_vec();
let mut encoded_data = encode_bytes(&data);
encoded_data[1] = test_len as u8;
let res = parse_protobuf_bytes(&mut encoded_data, field_number).unwrap();
assert_eq!(res, Some(Binary::new(data[..test_len].to_owned())));
assert_eq!(encoded_data, data[test_len..].to_owned());
}
#[test]
fn parse_protobuf_string_tests() {
let field_number = 1;
let data = "";
let mut encoded_data = encode_string(data);
let res = parse_protobuf_string(&mut encoded_data, field_number).unwrap();
assert_eq!(res, data);
let data = "test";
let mut encoded_data = encode_string(data);
let res = parse_protobuf_string(&mut encoded_data, field_number).unwrap();
assert_eq!(res, data);
let data = vec![0x40; 300];
let str_data = from_utf8(data.as_slice()).unwrap();
let mut encoded_data = encode_string(str_data);
let res = parse_protobuf_string(&mut encoded_data, field_number).unwrap();
assert_eq!(res, str_data);
let field_number = 5;
let data = "test field 5";
let mut encoded_data = encode_string(data);
encoded_data[0] = (field_number << 3) + WIRE_TYPE_LENGTH_DELIMITED;
let res = parse_protobuf_string(&mut encoded_data, field_number).unwrap();
assert_eq!(res, data);
let field_number = 1;
let test_len: usize = 4;
let data = "test_remainder";
let mut encoded_data = encode_string(data);
encoded_data[1] = test_len as u8;
let res = parse_protobuf_string(&mut encoded_data, field_number).unwrap();
assert_eq!(res, data[..test_len]);
assert_eq!(encoded_data, data[test_len..].as_bytes());
let field_number = 1;
let data = "test_X";
let mut encoded_data = encode_string(data);
let encoded_len = encoded_data.len();
encoded_data[encoded_len - 1] = 0xd3;
let err = parse_protobuf_string(&mut encoded_data, field_number).unwrap_err();
assert!(matches!(err, BrokenUtf8(..)));
}
#[test]
fn parse_instantiate_response_data_works() {
let contract_addr: &str = "Contract #1";
for (data, expected) in [
(
vec![],
super::MsgInstantiateContractResponse {
contract_address: contract_addr.to_string(),
data: None,
},
),
(
vec![1u8, 2, 255, 7, 5],
super::MsgInstantiateContractResponse {
contract_address: contract_addr.to_string(),
data: Some(Binary::new(vec![1u8, 2, 255, 7, 5])),
},
),
(
vec![1u8; 127],
super::MsgInstantiateContractResponse {
contract_address: contract_addr.to_string(),
data: Some(Binary::new(vec![1u8; 127])),
},
),
(
vec![2u8; 128],
super::MsgInstantiateContractResponse {
contract_address: contract_addr.to_string(),
data: Some(Binary::new(vec![2u8; 128])),
},
),
(
vec![3u8; 257],
super::MsgInstantiateContractResponse {
contract_address: contract_addr.to_string(),
data: Some(Binary::new(vec![3u8; 257])),
},
),
] {
let instantiate_reply = MsgInstantiateContractResponse {
contract_address: contract_addr.to_string(),
data,
};
let mut encoded_instantiate_reply =
Vec::<u8>::with_capacity(instantiate_reply.encoded_len());
instantiate_reply
.encode(&mut encoded_instantiate_reply)
.unwrap();
let res = parse_instantiate_response_data(&encoded_instantiate_reply).unwrap();
assert_eq!(res, expected);
}
}
#[test]
fn parse_execute_response_data_works() {
for (data, expected) in [
(vec![], super::MsgExecuteContractResponse { data: None }),
(
vec![1u8, 2, 3, 127, 15],
super::MsgExecuteContractResponse {
data: Some(Binary::new(vec![1u8, 2, 3, 127, 15])),
},
),
(
vec![0u8; 255],
super::MsgExecuteContractResponse {
data: Some(Binary::new(vec![0u8; 255])),
},
),
(
vec![1u8; 256],
super::MsgExecuteContractResponse {
data: Some(Binary::new(vec![1u8; 256])),
},
),
(
vec![2u8; 32769],
super::MsgExecuteContractResponse {
data: Some(Binary::new(vec![2u8; 32769])),
},
),
] {
let execute_reply = MsgExecuteContractResponse { data };
let mut encoded_execute_reply = Vec::<u8>::with_capacity(execute_reply.encoded_len());
execute_reply.encode(&mut encoded_execute_reply).unwrap();
let res = parse_execute_response_data(&encoded_execute_reply).unwrap();
assert_eq!(res, expected);
}
}
}