Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions src/cmd/extrinsics/transcode/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,12 @@ impl<'a> Decoder<'a> {
anyhow::anyhow!("Failed to find type with id '{}'", type_id)
})?;

if *ty.type_def() == TypeDef::Primitive(TypeDefPrimitive::U8) {
let mut bytes = vec![0u8; len];
input.read(&mut bytes)?;
Ok(Value::Bytes(bytes.into()))
} else {
let mut elems = Vec::new();
while elems.len() < len as usize {
let elem = self.decode_type(type_id, ty, input)?;
elems.push(elem)
}
Ok(Value::Seq(elems.into()))
let mut elems = Vec::new();
while elems.len() < len as usize {
let elem = self.decode_type(type_id, ty, input)?;
elems.push(elem)
}
Ok(Value::Seq(elems.into()))
}

fn decode_type(
Expand Down
32 changes: 27 additions & 5 deletions src/cmd/extrinsics/transcode/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,11 @@ impl<'a> Encoder<'a> {
self.encode(ty.id(), value, output)?;
}
}
Value::Bytes(bytes) => {
Value::Hex(hex) => {
if encode_len {
Compact(bytes.bytes().len() as u32).encode_to(output);
Compact(hex.bytes().len() as u32).encode_to(output);
}
for byte in bytes.bytes() {
for byte in hex.bytes() {
output.push_byte(*byte);
}
}
Expand Down Expand Up @@ -410,7 +410,7 @@ impl<'a> Encoder<'a> {

fn uint_from_value<T>(value: &Value, expected: &str) -> Result<T>
where
T: TryFrom<u128> + FromStr,
T: TryFrom<u128> + TryFromHex + FromStr,
<T as TryFrom<u128>>::Error: Error + Send + Sync + 'static,
<T as FromStr>::Err: Error + Send + Sync + 'static,
{
Expand All @@ -424,6 +424,10 @@ where
let uint = T::from_str(&sanitized)?;
Ok(uint)
}
Value::Hex(hex) => {
let uint = T::try_from_hex(hex.as_str())?;
Ok(uint)
}
_ => {
Err(anyhow::anyhow!(
"Expected a {} or a String value, got {}",
Expand All @@ -436,7 +440,7 @@ where

fn encode_uint<T, O>(value: &Value, expected: &str, output: &mut O) -> Result<()>
where
T: TryFrom<u128> + FromStr + Encode,
T: TryFrom<u128> + TryFromHex + FromStr + Encode,
<T as TryFrom<u128>>::Error: Error + Send + Sync + 'static,
<T as FromStr>::Err: Error + Send + Sync + 'static,
O: Output,
Expand Down Expand Up @@ -479,3 +483,21 @@ where
int.encode_to(output);
Ok(())
}

/// Attempt to instantiate a type from its little-endian bytes representation.
pub trait TryFromHex: Sized {
/// Create a new instance from the little-endian bytes representation.
fn try_from_hex(hex: &str) -> Result<Self>;
}

macro_rules! impl_try_from_hex {
( $($ty:ident),* ) => { $(
impl TryFromHex for $ty {
fn try_from_hex(hex: &str) -> Result<Self> {
$ty::from_str_radix(hex, 16).map_err(Into::into)
}
}
)* }
}

impl_try_from_hex!(u8, u16, u32, u64, u128);
9 changes: 6 additions & 3 deletions src/cmd/extrinsics/transcode/env_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,12 @@ impl CustomTypeTranscoder for AccountId {
)
})?
}
Value::Bytes(bytes) => {
AccountId32::try_from(bytes.bytes()).map_err(|_| {
anyhow::anyhow!("Error converting bytes `{:?}` to AccountId", bytes)
Value::Hex(hex) => {
AccountId32::try_from(hex.bytes()).map_err(|_| {
anyhow::anyhow!(
"Error converting hex bytes `{:?}` to AccountId",
hex.bytes()
)
})?
}
_ => {
Expand Down
62 changes: 62 additions & 0 deletions src/cmd/extrinsics/transcode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,22 @@ mod tests {
pub fn primitive_vec_args(&self, args: Vec<u32>) {
let _ = args;
}

#[ink(message)]
pub fn uint_args(
&self,
_u8: u8,
_u16: u16,
_u32: u32,
_u64: u64,
_u128: u128,
) {
}

#[ink(message)]
pub fn uint_array_args(&self, arr: [u8; 4]) {
let _ = arr;
}
}
}

Expand Down Expand Up @@ -489,6 +505,52 @@ mod tests {
Ok(())
}

#[test]
fn encode_uint_hex_literals() -> Result<()> {
let metadata = generate_metadata();
let transcoder = ContractMessageTranscoder::new(&metadata);

let encoded = transcoder.encode(
"uint_args",
&[
"0x00",
"0xDEAD",
"0xDEADBEEF",
"0xDEADBEEF12345678",
"0xDEADBEEF0123456789ABCDEF01234567",
],
)?;

// encoded args follow the 4 byte selector
let encoded_args = &encoded[4..];

let expected = (
0x00u8,
0xDEADu16,
0xDEADBEEFu32,
0xDEADBEEF12345678u64,
0xDEADBEEF0123456789ABCDEF01234567u128,
);
assert_eq!(expected.encode(), encoded_args);
Ok(())
}

#[test]
fn encode_uint_arr_hex_literals() -> Result<()> {
let metadata = generate_metadata();
let transcoder = ContractMessageTranscoder::new(&metadata);

let encoded =
transcoder.encode("uint_array_args", &["[0xDE, 0xAD, 0xBE, 0xEF]"])?;

// encoded args follow the 4 byte selector
let encoded_args = &encoded[4..];

let expected: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
assert_eq!(expected.encode(), encoded_args);
Ok(())
}

#[test]
fn decode_primitive_return() -> Result<()> {
let metadata = generate_metadata();
Expand Down
8 changes: 4 additions & 4 deletions src/cmd/extrinsics/transcode/scon/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// along with cargo-contract. If not, see <http://www.gnu.org/licenses/>.

use super::{
Bytes,
Hex,
Map,
Seq,
Tuple,
Expand Down Expand Up @@ -43,7 +43,7 @@ impl<'a> Debug for DisplayValue<'a> {
Value::Tuple(tuple) => <DisplayTuple as Debug>::fmt(&DisplayTuple(tuple), f),
Value::String(string) => <String as Display>::fmt(string, f),
Value::Seq(seq) => <DisplaySeq as Debug>::fmt(&DisplaySeq(seq), f),
Value::Bytes(bytes) => <Bytes as Debug>::fmt(bytes, f),
Value::Hex(hex) => <Hex as Debug>::fmt(hex, f),
Value::Literal(literal) => <String as Display>::fmt(literal, f),
Value::Unit => write!(f, "()"),
}
Expand Down Expand Up @@ -107,13 +107,13 @@ impl<'a> Debug for DisplaySeq<'a> {
}
}

impl Debug for Bytes {
impl Debug for Hex {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{:#x}", self)
}
}

impl LowerHex for Bytes {
impl LowerHex for Hex {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
if f.alternate() {
write!(f, "0x{}", hex::encode(&self.bytes))
Expand Down
24 changes: 15 additions & 9 deletions src/cmd/extrinsics/transcode/scon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod parse;

use indexmap::IndexMap;

use crate::util;
use std::{
cmp::{
Eq,
Expand All @@ -35,6 +36,7 @@ use std::{
Index,
IndexMut,
},
str::FromStr,
};

pub use self::parse::parse_value;
Expand All @@ -49,7 +51,7 @@ pub enum Value {
Tuple(Tuple),
String(String),
Seq(Seq),
Bytes(Bytes),
Hex(Hex),
Literal(String),
Unit,
}
Expand Down Expand Up @@ -199,20 +201,24 @@ impl Seq {
}

#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Bytes {
pub struct Hex {
s: String,
bytes: Vec<u8>,
}

impl From<Vec<u8>> for Bytes {
fn from(bytes: Vec<u8>) -> Self {
Self { bytes }
impl FromStr for Hex {
type Err = hex::FromHexError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim_start_matches("0x").to_string();
let bytes = util::decode_hex(&s)?;
Ok(Self { s, bytes })
}
}

impl Bytes {
pub fn from_hex_string(s: &str) -> Result<Self, hex::FromHexError> {
let bytes = crate::util::decode_hex(s)?;
Ok(Self { bytes })
impl Hex {
pub fn as_str(&self) -> &str {
&self.s
}

pub fn bytes(&self) -> &[u8] {
Expand Down
20 changes: 12 additions & 8 deletions src/cmd/extrinsics/transcode/scon/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// along with cargo-contract. If not, see <http://www.gnu.org/licenses/>.

use super::{
Bytes,
Hex,
Map,
Tuple,
Value,
Expand Down Expand Up @@ -53,6 +53,7 @@ use nom_supreme::{
error::ErrorTree,
ParserExt,
};
use std::str::FromStr as _;

/// Attempt to parse a SCON value
pub fn parse_value(input: &str) -> anyhow::Result<Value> {
Expand All @@ -64,7 +65,7 @@ pub fn parse_value(input: &str) -> anyhow::Result<Value> {
fn scon_value(input: &str) -> IResult<&str, Value, ErrorTree<&str>> {
ws(alt((
scon_unit,
scon_bytes,
scon_hex,
scon_seq,
scon_tuple,
scon_map,
Expand Down Expand Up @@ -221,12 +222,12 @@ fn scon_map(input: &str) -> IResult<&str, Value, ErrorTree<&str>> {
.parse(input)
}

fn scon_bytes(input: &str) -> IResult<&str, Value, ErrorTree<&str>> {
fn scon_hex(input: &str) -> IResult<&str, Value, ErrorTree<&str>> {
tag("0x")
.precedes(hex_digit1)
.map_res::<_, _, hex::FromHexError>(|byte_str| {
let bytes = Bytes::from_hex_string(byte_str)?;
Ok(Value::Bytes(bytes))
let hex = Hex::from_str(byte_str)?;
Ok(Value::Hex(hex))
})
.parse(input)
}
Expand Down Expand Up @@ -602,10 +603,13 @@ mod tests {

#[test]
fn test_bytes() {
assert_scon_value(r#"0x0000"#, Value::Bytes(vec![0u8; 2].into()));
assert_scon_value("0x0000", Value::Hex(Hex::from_str("0x0000").unwrap()));
assert_scon_value(
r#"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"#,
Value::Bytes(vec![255u8; 23].into()),
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
Value::Hex(
Hex::from_str("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
.unwrap(),
),
);
}
}
Loading