Unverified Commit 0215f1b3 authored by Andrew Jones's avatar Andrew Jones Committed by GitHub
Browse files

Allow hex literals for unsigned integers (#547)

* WIP in hex literals

* Convert bytes to seqs

* Fmt

* Warnings

* Add transcode hex literal test

* Fix Hex construction trim leading 0x

* Fix custom account id transcode test

* Fix uint hex literal transcoder test
parent c174a678
Pipeline #192195 failed with stages
in 1 minute and 49 seconds
......@@ -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(
......
......@@ -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);
}
}
......@@ -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,
{
......@@ -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 {}",
......@@ -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,
......@@ -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);
......@@ -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()
)
})?
}
_ => {
......
......@@ -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;
}
}
}
......@@ -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();
......
......@@ -15,7 +15,7 @@
// along with cargo-contract. If not, see <http://www.gnu.org/licenses/>.
use super::{
Bytes,
Hex,
Map,
Seq,
Tuple,
......@@ -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, "()"),
}
......@@ -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))
......
......@@ -21,6 +21,7 @@ mod parse;
use indexmap::IndexMap;
use crate::util;
use std::{
cmp::{
Eq,
......@@ -35,6 +36,7 @@ use std::{
Index,
IndexMut,
},
str::FromStr,
};
pub use self::parse::parse_value;
......@@ -49,7 +51,7 @@ pub enum Value {
Tuple(Tuple),
String(String),
Seq(Seq),
Bytes(Bytes),
Hex(Hex),
Literal(String),
Unit,
}
......@@ -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] {
......
......@@ -15,7 +15,7 @@
// along with cargo-contract. If not, see <http://www.gnu.org/licenses/>.
use super::{
Bytes,
Hex,
Map,
Tuple,
Value,
......@@ -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> {
......@@ -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,
......@@ -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)
}
......@@ -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(),
),
);
}
}
......@@ -133,6 +133,7 @@ mod tests {
use super::{
super::scon::{
self,
Hex,
Map,
Seq,
Tuple,
......@@ -140,16 +141,14 @@ mod tests {
},
*,
};
use crate::cmd::extrinsics::{
transcode,
transcode::scon::Bytes,
};
use crate::cmd::extrinsics::transcode;
use scale::Encode;
use scale_info::{
MetaType,
Registry,
TypeInfo,
};
use std::str::FromStr;
fn registry_with_type<T>() -> Result<(PortableRegistry, u32)>
where
......@@ -264,15 +263,31 @@ mod tests {
fn transcode_byte_array() -> Result<()> {
transcode_roundtrip::<[u8; 2]>(
r#"0x0000"#,
Value::Bytes(vec![0x00, 0x00].into()),
Value::Seq(vec![Value::UInt(0x00), Value::UInt(0x00)].into()),
)?;
transcode_roundtrip::<[u8; 4]>(
r#"0xDEADBEEF"#,
Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF].into()),
Value::Seq(
vec![
Value::UInt(0xDE),
Value::UInt(0xAD),
Value::UInt(0xBE),
Value::UInt(0xEF),
]
.into(),
),
)?;
transcode_roundtrip::<[u8; 4]>(
r#"0xdeadbeef"#,
Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF].into()),
Value::Seq(
vec![
Value::UInt(0xDE),
Value::UInt(0xAD),
Value::UInt(0xBE),
Value::UInt(0xEF),
]
.into(),
),
)
}
......@@ -321,7 +336,15 @@ mod tests {
vec![
Value::UInt(1),
Value::String("ink!".to_string()),
Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF].into()),
Value::Seq(
vec![
Value::UInt(0xDE),
Value::UInt(0xAD),
Value::UInt(0xBE),
Value::UInt(0xEF),
]
.into(),
),
],
)),
)
......@@ -350,7 +373,15 @@ mod tests {
),
(
Value::String("c".to_string()),
Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF].into()),
Value::Seq(
vec![
Value::UInt(0xDE),
Value::UInt(0xAD),
Value::UInt(0xBE),
Value::UInt(0xEF),
]
.into(),
),
),
(
Value::String("d".to_string()),
......@@ -364,7 +395,15 @@ mod tests {
),
(
Value::String("c".to_string()),
Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF].into()),
Value::Seq(
vec![
Value::UInt(0xDE),
Value::UInt(0xAD),
Value::UInt(0xBE),
Value::UInt(0xEF),
]
.into(),
),
),
(
Value::String("d".to_string()),
......@@ -439,7 +478,15 @@ mod tests {
),
(
Value::String("c".to_string()),
Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF].into()),
Value::Seq(
vec![
Value::UInt(0xDE),
Value::UInt(0xAD),
Value::UInt(0xBE),
Value::UInt(0xEF),
]
.into(),
),
),
]
.into_iter()
......@@ -461,7 +508,15 @@ mod tests {
vec![
Value::UInt(1),
Value::String("ink!".to_string()),
Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF].into()),
Value::Seq(
vec![
Value::UInt(0xDE),
Value::UInt(0xAD),
Value::UInt(0xBE),
Value::UInt(0xEF),
]
.into(),
),
],
)),
)
......@@ -477,7 +532,15 @@ mod tests {
r#"0xDEADBEEF"#,
Value::Tuple(Tuple::new(
Some("S"),
vec![Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF].into())],
vec![Value::Seq(
vec![
Value::UInt(0xDE),
Value::UInt(0xAD),
Value::UInt(0xBE),
Value::UInt(0xEF),
]
.into(),
)],
)),
)
}
......@@ -488,7 +551,15 @@ mod tests {
r#"0xDEADBEEF"#,
Value::Tuple(Tuple::new(
None,
vec![Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF].into())],
vec![Value::Seq(
vec![
Value::UInt(0xDE),
Value::UInt(0xAD),
Value::UInt(0xBE),
Value::UInt(0xEF),
]
.into(),
)],
)),
)
}
......@@ -611,6 +682,12 @@ mod tests {
#[test]
fn transcode_account_id_custom_ss58_encoding_seq() -> Result<()> {
let hex_to_bytes = |hex: &str| -> Result<Value> {
let hex = Hex::from_str(hex)?;
let values = hex.bytes().iter().map(|b| Value::UInt((*b).into()));
Ok(Value::Seq(Seq::new(values.collect())))
};
transcode_roundtrip::<Vec<sp_runtime::AccountId32>>(
r#"[
5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
......@@ -621,13 +698,13 @@ mod tests {
Value::Tuple(
Tuple::new(
Some("AccountId32"),
vec![Value::Bytes(Bytes::from_hex_string("0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d").unwrap())]
vec![hex_to_bytes("0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d")?]
)
),
Value::Tuple(
Tuple::new(
Some("AccountId32"),
vec![Value::Bytes(Bytes::from_hex_string("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48").unwrap())]
vec![hex_to_bytes("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48")?]
)
)
]
......@@ -678,4 +755,26 @@ mod tests {
)),
)
}
#[test]
fn transcode_hex_literal_uints() -> Result<()> {
#[derive(scale::Encode, TypeInfo)]
struct S(u8, u16, u32, u64, u128);
transcode_roundtrip::<S>(
r#"S (0xDE, 0xDEAD, 0xDEADBEEF, 0xDEADBEEF12345678, 0xDEADBEEF0123456789ABCDEF01234567)"#,
Value::Tuple(Tuple::new(
Some("S"),
vec![
Value::UInt(0xDE),
Value::UInt(0xDEAD),
Value::UInt(0xDEADBEEF),
Value::UInt(0xDEADBEEF12345678),
Value::UInt(0xDEADBEEF0123456789ABCDEF01234567),
]
.into_iter()
.collect(),
)),
)
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment