--- a/crates/evm-coder/procedural/src/solidity_interface.rs +++ b/crates/evm-coder/procedural/src/solidity_interface.rs @@ -328,14 +328,14 @@ } } -trait AbiType { +trait AbiTypeHelper { fn plain(&self) -> syn::Result<&Ident>; fn is_value(&self) -> bool; fn is_caller(&self) -> bool; fn is_special(&self) -> bool; } -impl AbiType for Type { +impl AbiTypeHelper for Type { fn plain(&self) -> syn::Result<&Ident> { let path = parse_path(self)?; let segment = parse_path_segment(path)?; --- a/crates/evm-coder/src/abi.rs +++ /dev/null @@ -1,968 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -//! Implementation of EVM RLP reader/writer - -#![allow(dead_code)] - -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; -use evm_core::ExitError; -use primitive_types::{H160, U256}; - -use crate::{ - execution::{Error, ResultWithPostInfo, WithPostDispatchInfo}, - types::*, - make_signature, - custom_signature::{SignatureUnit}, -}; -use crate::execution::Result; - -const ABI_ALIGNMENT: usize = 32; - -trait TypeHelper { - /// Is type dynamic sized. - fn is_dynamic() -> bool; - - /// Size for type aligned to [`ABI_ALIGNMENT`]. - fn size() -> usize; -} - -/// View into RLP data, which provides method to read typed items from it -#[derive(Clone)] -pub struct AbiReader<'i> { - buf: &'i [u8], - subresult_offset: usize, - offset: usize, -} -impl<'i> AbiReader<'i> { - /// Start reading RLP buffer, assuming there is no padding bytes - pub fn new(buf: &'i [u8]) -> Self { - Self { - buf, - subresult_offset: 0, - offset: 0, - } - } - /// Start reading RLP buffer, parsing first 4 bytes as selector - pub fn new_call(buf: &'i [u8]) -> Result<(bytes4, Self)> { - if buf.len() < 4 { - return Err(Error::Error(ExitError::OutOfOffset)); - } - let mut method_id = [0; 4]; - method_id.copy_from_slice(&buf[0..4]); - - Ok(( - method_id, - Self { - buf, - subresult_offset: 4, - offset: 4, - }, - )) - } - - fn read_pad( - buf: &[u8], - offset: usize, - pad_start: usize, - pad_size: usize, - block_start: usize, - block_size: usize, - ) -> Result<[u8; S]> { - if buf.len() - offset < ABI_ALIGNMENT { - return Err(Error::Error(ExitError::OutOfOffset)); - } - let mut block = [0; S]; - let is_pad_zeroed = buf[pad_start..pad_size].iter().all(|&v| v == 0); - if !is_pad_zeroed { - return Err(Error::Error(ExitError::InvalidRange)); - } - block.copy_from_slice(&buf[block_start..block_size]); - Ok(block) - } - - fn read_padleft(&mut self) -> Result<[u8; S]> { - let offset = self.offset; - self.offset += ABI_ALIGNMENT; - Self::read_pad( - self.buf, - offset, - offset, - offset + ABI_ALIGNMENT - S, - offset + ABI_ALIGNMENT - S, - offset + ABI_ALIGNMENT, - ) - } - - fn read_padright(&mut self) -> Result<[u8; S]> { - let offset = self.offset; - self.offset += ABI_ALIGNMENT; - Self::read_pad( - self.buf, - offset, - offset + S, - offset + ABI_ALIGNMENT, - offset, - offset + S, - ) - } - - /// Read [`H160`] at current position, then advance - pub fn address(&mut self) -> Result { - Ok(H160(self.read_padleft()?)) - } - - /// Read [`bool`] at current position, then advance - pub fn bool(&mut self) -> Result { - let data: [u8; 1] = self.read_padleft()?; - match data[0] { - 0 => Ok(false), - 1 => Ok(true), - _ => Err(Error::Error(ExitError::InvalidRange)), - } - } - - /// Read [`[u8; 4]`] at current position, then advance - pub fn bytes4(&mut self) -> Result<[u8; 4]> { - self.read_padright() - } - - /// Read [`Vec`] at current position, then advance - pub fn bytes(&mut self) -> Result> { - let mut subresult = self.subresult(None)?; - let length = subresult.uint32()? as usize; - if subresult.buf.len() < subresult.offset + length { - return Err(Error::Error(ExitError::OutOfOffset)); - } - Ok(subresult.buf[subresult.offset..subresult.offset + length].into()) - } - - /// Read [`string`] at current position, then advance - pub fn string(&mut self) -> Result { - string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange)) - } - - /// Read [`u8`] at current position, then advance - pub fn uint8(&mut self) -> Result { - Ok(self.read_padleft::<1>()?[0]) - } - - /// Read [`u32`] at current position, then advance - pub fn uint32(&mut self) -> Result { - Ok(u32::from_be_bytes(self.read_padleft()?)) - } - - /// Read [`u128`] at current position, then advance - pub fn uint128(&mut self) -> Result { - Ok(u128::from_be_bytes(self.read_padleft()?)) - } - - /// Read [`U256`] at current position, then advance - pub fn uint256(&mut self) -> Result { - let buf: [u8; 32] = self.read_padleft()?; - Ok(U256::from_big_endian(&buf)) - } - - /// Read [`u64`] at current position, then advance - pub fn uint64(&mut self) -> Result { - Ok(u64::from_be_bytes(self.read_padleft()?)) - } - - /// Read [`usize`] at current position, then advance - #[deprecated = "dangerous, as usize may have different width in wasm and native execution"] - pub fn read_usize(&mut self) -> Result { - Ok(usize::from_be_bytes(self.read_padleft()?)) - } - - /// Slice recursive buffer, advance one word for buffer offset - /// If `size` is [`None`] then [`Self::offset`] and [`Self::subresult_offset`] evals from [`Self::buf`]. - fn subresult(&mut self, size: Option) -> Result> { - let subresult_offset = self.subresult_offset; - let offset = if let Some(size) = size { - self.offset += size; - self.subresult_offset += size; - 0 - } else { - self.uint32()? as usize - }; - - if offset + self.subresult_offset > self.buf.len() { - return Err(Error::Error(ExitError::InvalidRange)); - } - - let new_offset = offset + subresult_offset; - Ok(AbiReader { - buf: self.buf, - subresult_offset: new_offset, - offset: new_offset, - }) - } - - /// Is this parser reached end of buffer? - pub fn is_finished(&self) -> bool { - self.buf.len() == self.offset - } -} - -/// Writer for RLP encoded data -#[derive(Default)] -pub struct AbiWriter { - static_part: Vec, - dynamic_part: Vec<(usize, AbiWriter)>, - had_call: bool, - is_dynamic: bool, -} -impl AbiWriter { - /// Initialize internal buffers for output data, assuming no padding required - pub fn new() -> Self { - Self::default() - } - - /// Initialize internal buffers with data size - pub fn new_dynamic(is_dynamic: bool) -> Self { - Self { - is_dynamic, - ..Default::default() - } - } - /// Initialize internal buffers, inserting method selector at beginning - pub fn new_call(method_id: u32) -> Self { - let mut val = Self::new(); - val.static_part.extend(&method_id.to_be_bytes()); - val.had_call = true; - val - } - - fn write_padleft(&mut self, block: &[u8]) { - assert!(block.len() <= ABI_ALIGNMENT); - self.static_part - .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]); - self.static_part.extend(block); - } - - fn write_padright(&mut self, block: &[u8]) { - assert!(block.len() <= ABI_ALIGNMENT); - self.static_part.extend(block); - self.static_part - .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]); - } - - /// Write [`H160`] to end of buffer - pub fn address(&mut self, address: &H160) { - self.write_padleft(&address.0) - } - - /// Write [`bool`] to end of buffer - pub fn bool(&mut self, value: &bool) { - self.write_padleft(&[if *value { 1 } else { 0 }]) - } - - /// Write [`u8`] to end of buffer - pub fn uint8(&mut self, value: &u8) { - self.write_padleft(&[*value]) - } - - /// Write [`u32`] to end of buffer - pub fn uint32(&mut self, value: &u32) { - self.write_padleft(&u32::to_be_bytes(*value)) - } - - /// Write [`u128`] to end of buffer - pub fn uint128(&mut self, value: &u128) { - self.write_padleft(&u128::to_be_bytes(*value)) - } - - /// Write [`U256`] to end of buffer - pub fn uint256(&mut self, value: &U256) { - let mut out = [0; 32]; - value.to_big_endian(&mut out); - self.write_padleft(&out) - } - - /// Write [`usize`] to end of buffer - #[deprecated = "dangerous, as usize may have different width in wasm and native execution"] - pub fn write_usize(&mut self, value: &usize) { - self.write_padleft(&usize::to_be_bytes(*value)) - } - - /// Append recursive data, writing pending offset at end of buffer - pub fn write_subresult(&mut self, result: Self) { - self.dynamic_part.push((self.static_part.len(), result)); - // Empty block, to be filled later - self.write_padleft(&[]); - } - - fn memory(&mut self, value: &[u8]) { - let mut sub = Self::new(); - sub.uint32(&(value.len() as u32)); - for chunk in value.chunks(ABI_ALIGNMENT) { - sub.write_padright(chunk); - } - self.write_subresult(sub); - } - - /// Append recursive [`str`] at end of buffer - pub fn string(&mut self, value: &str) { - self.memory(value.as_bytes()) - } - - /// Append recursive [`[u8]`] at end of buffer - pub fn bytes(&mut self, value: &[u8]) { - self.memory(value) - } - - /// Finish writer, concatenating all internal buffers - pub fn finish(mut self) -> Vec { - for (static_offset, part) in self.dynamic_part { - let part_offset = self.static_part.len() - - if self.had_call { 4 } else { 0 } - - if self.is_dynamic { ABI_ALIGNMENT } else { 0 }; - - let encoded_dynamic_offset = usize::to_be_bytes(part_offset); - let start = static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len(); - let stop = static_offset + ABI_ALIGNMENT; - self.static_part[start..stop].copy_from_slice(&encoded_dynamic_offset); - self.static_part.extend(part.finish()) - } - self.static_part - } -} - -/// [`AbiReader`] implements reading of many types. -pub trait AbiRead { - /// Read item from current position, advanding decoder - fn abi_read(reader: &mut AbiReader) -> Result - where - Self: Sized; -} - -macro_rules! impl_abi_readable { - ($ty:ty, $method:ident, $dynamic:literal) => { - impl sealed::CanBePlacedInVec for $ty {} - - impl TypeHelper for $ty { - fn is_dynamic() -> bool { - $dynamic - } - - fn size() -> usize { - ABI_ALIGNMENT - } - } - - impl AbiRead for $ty { - fn abi_read(reader: &mut AbiReader) -> Result<$ty> { - reader.$method() - } - } - }; -} - -impl_abi_readable!(bool, bool, false); -impl_abi_readable!(uint32, uint32, false); -impl_abi_readable!(uint64, uint64, false); -impl_abi_readable!(uint128, uint128, false); -impl_abi_readable!(uint256, uint256, false); -impl_abi_readable!(bytes4, bytes4, false); -impl_abi_readable!(address, address, false); -impl_abi_readable!(string, string, true); - -impl TypeHelper for uint8 { - fn is_dynamic() -> bool { - false - } - fn size() -> usize { - ABI_ALIGNMENT - } -} -impl AbiRead for uint8 { - fn abi_read(reader: &mut AbiReader) -> Result { - reader.uint8() - } -} - -impl TypeHelper for bytes { - fn is_dynamic() -> bool { - true - } - fn size() -> usize { - ABI_ALIGNMENT - } -} -impl AbiRead for bytes { - fn abi_read(reader: &mut AbiReader) -> Result { - Ok(bytes(reader.bytes()?)) - } -} - -mod sealed { - /// Not all types can be placed in vec, i.e `Vec` is restricted, `bytes` should be used instead - pub trait CanBePlacedInVec {} -} - -impl AbiRead for Vec { - fn abi_read(reader: &mut AbiReader) -> Result> { - let mut sub = reader.subresult(None)?; - let size = sub.uint32()? as usize; - sub.subresult_offset = sub.offset; - let mut out = Vec::with_capacity(size); - for _ in 0..size { - out.push(::abi_read(&mut sub)?); - } - Ok(out) - } -} - -impl Signature for Vec { - const SIGNATURE: SignatureUnit = make_signature!(new nameof(R::SIGNATURE) fixed("[]")); -} - -impl sealed::CanBePlacedInVec for EthCrossAccount {} - -impl TypeHelper for EthCrossAccount { - fn is_dynamic() -> bool { - address::is_dynamic() || uint256::is_dynamic() - } - - fn size() -> usize { -
::size() + ::size() - } -} - -impl AbiRead for EthCrossAccount { - fn abi_read(reader: &mut AbiReader) -> Result { - let size = if !EthCrossAccount::is_dynamic() { - Some(::size()) - } else { - None - }; - let mut subresult = reader.subresult(size)?; - let eth =
::abi_read(&mut subresult)?; - let sub = ::abi_read(&mut subresult)?; - - Ok(EthCrossAccount { eth, sub }) - } -} - -impl AbiWrite for EthCrossAccount { - fn abi_write(&self, writer: &mut AbiWriter) { - self.eth.abi_write(writer); - self.sub.abi_write(writer); - } -} - -macro_rules! impl_tuples { - ($($ident:ident)+) => { - impl<$($ident: TypeHelper,)+> TypeHelper for ($($ident,)+) - where - $( - $ident: TypeHelper, - )+ - { - fn is_dynamic() -> bool { - false - $( - || <$ident>::is_dynamic() - )* - } - - fn size() -> usize { - 0 $(+ <$ident>::size())+ - } - } - - impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {} - - impl<$($ident),+> AbiRead for ($($ident,)+) - where - $($ident: AbiRead,)+ - ($($ident,)+): TypeHelper, - { - fn abi_read(reader: &mut AbiReader) -> Result<($($ident,)+)> { - let size = if !<($($ident,)+)>::is_dynamic() { Some(<($($ident,)+)>::size()) } else { None }; - let mut subresult = reader.subresult(size)?; - Ok(( - $(<$ident>::abi_read(&mut subresult)?,)+ - )) - } - } - - #[allow(non_snake_case)] - impl<$($ident),+> AbiWrite for ($($ident,)+) - where - $($ident: AbiWrite,)+ - { - fn abi_write(&self, writer: &mut AbiWriter) { - let ($($ident,)+) = self; - if writer.is_dynamic { - let mut sub = AbiWriter::new(); - $($ident.abi_write(&mut sub);)+ - writer.write_subresult(sub); - } else { - $($ident.abi_write(writer);)+ - } - } - } - - impl<$($ident),+> Signature for ($($ident,)+) - where - $($ident: Signature,)+ - { - const SIGNATURE: SignatureUnit = make_signature!( - new fixed("(") - $(nameof(<$ident>::SIGNATURE) fixed(","))+ - shift_left(1) - fixed(")") - ); - } - }; -} - -impl_tuples! {A} -impl_tuples! {A B} -impl_tuples! {A B C} -impl_tuples! {A B C D} -impl_tuples! {A B C D E} -impl_tuples! {A B C D E F} -impl_tuples! {A B C D E F G} -impl_tuples! {A B C D E F G H} -impl_tuples! {A B C D E F G H I} -impl_tuples! {A B C D E F G H I J} - -/// For questions about inability to provide custom implementations, -/// see [`AbiRead`] -pub trait AbiWrite { - /// Write value to end of specified encoder - fn abi_write(&self, writer: &mut AbiWriter); - /// Specialization for [`crate::solidity_interface`] implementation, - /// see comment in `impl AbiWrite for ResultWithPostInfo` - fn to_result(&self) -> ResultWithPostInfo { - let mut writer = AbiWriter::new(); - self.abi_write(&mut writer); - Ok(writer.into()) - } -} - -/// This particular AbiWrite implementation should be split to another trait, -/// which only implements `to_result`, but due to lack of specialization feature -/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`, -/// so here we abusing default trait methods for it -impl AbiWrite for ResultWithPostInfo { - fn abi_write(&self, _writer: &mut AbiWriter) { - debug_assert!(false, "shouldn't be called, see comment") - } - fn to_result(&self) -> ResultWithPostInfo { - match self { - Ok(v) => Ok(WithPostDispatchInfo { - post_info: v.post_info.clone(), - data: { - let mut out = AbiWriter::new(); - v.data.abi_write(&mut out); - out - }, - }), - Err(e) => Err(e.clone()), - } - } -} - -macro_rules! impl_abi_writeable { - ($ty:ty, $method:ident) => { - impl AbiWrite for $ty { - fn abi_write(&self, writer: &mut AbiWriter) { - writer.$method(&self) - } - } - }; -} - -impl_abi_writeable!(u8, uint8); -impl_abi_writeable!(u32, uint32); -impl_abi_writeable!(u128, uint128); -impl_abi_writeable!(U256, uint256); -impl_abi_writeable!(H160, address); -impl_abi_writeable!(bool, bool); -impl_abi_writeable!(&str, string); - -impl AbiWrite for string { - fn abi_write(&self, writer: &mut AbiWriter) { - writer.string(self) - } -} - -impl AbiWrite for bytes { - fn abi_write(&self, writer: &mut AbiWriter) { - writer.bytes(self.0.as_slice()) - } -} - -impl AbiWrite for Vec { - fn abi_write(&self, writer: &mut AbiWriter) { - let is_dynamic = T::is_dynamic(); - let mut sub = if is_dynamic { - AbiWriter::new_dynamic(is_dynamic) - } else { - AbiWriter::new() - }; - - // Write items count - (self.len() as u32).abi_write(&mut sub); - - for item in self { - item.abi_write(&mut sub); - } - writer.write_subresult(sub); - } -} - -impl AbiWrite for () { - fn abi_write(&self, _writer: &mut AbiWriter) {} -} - -/// Helper macros to parse reader into variables -#[deprecated] -#[macro_export] -macro_rules! abi_decode { - ($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => { - $( - let $name = $reader.$typ()?; - )+ - } -} - -/// Helper macros to construct RLP-encoded buffer -#[deprecated] -#[macro_export] -macro_rules! abi_encode { - ($($typ:ident($value:expr)),* $(,)?) => {{ - #[allow(unused_mut)] - let mut writer = ::evm_coder::abi::AbiWriter::new(); - $( - writer.$typ($value); - )* - writer - }}; - (call $val:expr; $($typ:ident($value:expr)),* $(,)?) => {{ - #[allow(unused_mut)] - let mut writer = ::evm_coder::abi::AbiWriter::new_call($val); - $( - writer.$typ($value); - )* - writer - }} -} - -#[cfg(test)] -pub mod test { - use crate::{ - abi::{AbiRead, AbiWrite}, - types::*, - }; - - use super::{AbiReader, AbiWriter}; - use hex_literal::hex; - use primitive_types::{H160, U256}; - use concat_idents::concat_idents; - - macro_rules! test_impl { - ($name:ident, $type:ty, $function_identifier:expr, $decoded_data:expr, $encoded_data:expr) => { - concat_idents!(test_name = encode_decode_, $name { - #[test] - fn test_name() { - let function_identifier: u32 = $function_identifier; - let decoded_data = $decoded_data; - let encoded_data = $encoded_data; - - let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap(); - assert_eq!(call, u32::to_be_bytes(function_identifier)); - let data = <$type>::abi_read(&mut decoder).unwrap(); - assert_eq!(data, decoded_data); - - let mut writer = AbiWriter::new_call(function_identifier); - decoded_data.abi_write(&mut writer); - let ed = writer.finish(); - similar_asserts::assert_eq!(encoded_data, ed.as_slice()); - } - }); - }; - } - - macro_rules! test_impl_uint { - ($type:ident) => { - test_impl!( - $type, - $type, - 0xdeadbeef, - 255 as $type, - &hex!( - " - deadbeef - 00000000000000000000000000000000000000000000000000000000000000ff - " - ) - ); - }; - } - - test_impl_uint!(uint8); - test_impl_uint!(uint32); - test_impl_uint!(uint128); - - test_impl!( - uint256, - uint256, - 0xdeadbeef, - U256([255, 0, 0, 0]), - &hex!( - " - deadbeef - 00000000000000000000000000000000000000000000000000000000000000ff - " - ) - ); - - test_impl!( - vec_tuple_address_uint256, - Vec<(address, uint256)>, - 0x1ACF2D55, - vec![ - ( - H160(hex!("2D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC")), - U256([10, 0, 0, 0]), - ), - ( - H160(hex!("AB8E3D9134955566483B11E6825C9223B6737B10")), - U256([20, 0, 0, 0]), - ), - ( - H160(hex!("8C582BDF2953046705FC56F189385255EFC1BE18")), - U256([30, 0, 0, 0]), - ), - ], - &hex!( - " - 1ACF2D55 - 0000000000000000000000000000000000000000000000000000000000000020 // offset of (address, uint256)[] - 0000000000000000000000000000000000000000000000000000000000000003 // length of (address, uint256)[] - - 0000000000000000000000002D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC // address - 000000000000000000000000000000000000000000000000000000000000000A // uint256 - - 000000000000000000000000AB8E3D9134955566483B11E6825C9223B6737B10 // address - 0000000000000000000000000000000000000000000000000000000000000014 // uint256 - - 0000000000000000000000008C582BDF2953046705FC56F189385255EFC1BE18 // address - 000000000000000000000000000000000000000000000000000000000000001E // uint256 - " - ) - ); - - test_impl!( - vec_tuple_uint256_string, - Vec<(uint256, string)>, - 0xdeadbeef, - vec![ - (1.into(), "Test URI 0".to_string()), - (11.into(), "Test URI 1".to_string()), - (12.into(), "Test URI 2".to_string()), - ], - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 // offset of (uint256, string)[] - 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[] - - 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem - 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem - 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem - - 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203000000000000000000000000000000000000000000000 // string - - 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203100000000000000000000000000000000000000000000 // string - - 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203200000000000000000000000000000000000000000000 // string - " - ) - ); - - test_impl!( - vec_tuple_string_bytes, - Vec<(string, bytes)>, - 0xdeadbeef, - vec![ - ( - "Test URI 0".to_string(), - bytes(vec![ - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 - ]) - ), - ( - "Test URI 1".to_string(), - bytes(vec![ - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 - ]) - ), - ("Test URI 2".to_string(), bytes(vec![0x33, 0x33])), - ], - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000003 - - 0000000000000000000000000000000000000000000000000000000000000060 - 0000000000000000000000000000000000000000000000000000000000000140 - 0000000000000000000000000000000000000000000000000000000000000220 - - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000080 - 000000000000000000000000000000000000000000000000000000000000000a - 5465737420555249203000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000030 - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111100000000000000000000000000000000 - - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000080 - 000000000000000000000000000000000000000000000000000000000000000a - 5465737420555249203100000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000002f - 2222222222222222222222222222222222222222222222222222222222222222 - 2222222222222222222222222222220000000000000000000000000000000000 - - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000080 - 000000000000000000000000000000000000000000000000000000000000000a - 5465737420555249203200000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000002 - 3333000000000000000000000000000000000000000000000000000000000000 - " - ) - ); - - #[test] - fn dynamic_after_static() { - let mut encoder = AbiWriter::new(); - encoder.bool(&true); - encoder.string("test"); - let encoded = encoder.finish(); - - let mut encoder = AbiWriter::new(); - encoder.bool(&true); - // Offset to subresult - encoder.uint32(&(32 * 2)); - // Len of "test" - encoder.uint32(&4); - encoder.write_padright(&[b't', b'e', b's', b't']); - let alternative_encoded = encoder.finish(); - - assert_eq!(encoded, alternative_encoded); - - let mut decoder = AbiReader::new(&encoded); - assert!(decoder.bool().unwrap()); - assert_eq!(decoder.string().unwrap(), "test"); - } - - #[test] - fn mint_sample() { - let (call, mut decoder) = AbiReader::new_call(&hex!( - " - 50bb4e7f - 000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374 - 0000000000000000000000000000000000000000000000000000000000000001 - 0000000000000000000000000000000000000000000000000000000000000060 - 0000000000000000000000000000000000000000000000000000000000000008 - 5465737420555249000000000000000000000000000000000000000000000000 - " - )) - .unwrap(); - assert_eq!(call, u32::to_be_bytes(0x50bb4e7f)); - assert_eq!( - format!("{:?}", decoder.address().unwrap()), - "0xad2c0954693c2b5404b7e50967d3481bea432374" - ); - assert_eq!(decoder.uint32().unwrap(), 1); - assert_eq!(decoder.string().unwrap(), "Test URI"); - } - - #[test] - fn parse_vec_with_dynamic_type() { - let decoded_data = ( - 0x36543006, - vec![ - (1.into(), "Test URI 0".to_string()), - (11.into(), "Test URI 1".to_string()), - (12.into(), "Test URI 2".to_string()), - ], - ); - - let encoded_data = &hex!( - " - 36543006 - 00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address - 0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[] - 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[] - - 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem - 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem - 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem - - 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203000000000000000000000000000000000000000000000 // string - - 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203100000000000000000000000000000000000000000000 // string - - 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203200000000000000000000000000000000000000000000 // string - " - ); - - let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap(); - assert_eq!(call, u32::to_be_bytes(decoded_data.0)); - let address = decoder.address().unwrap(); - let data = >::abi_read(&mut decoder).unwrap(); - assert_eq!(data, decoded_data.1); - - let mut writer = AbiWriter::new_call(decoded_data.0); - address.abi_write(&mut writer); - decoded_data.1.abi_write(&mut writer); - let ed = writer.finish(); - similar_asserts::assert_eq!(encoded_data, ed.as_slice()); - } -} --- /dev/null +++ b/crates/evm-coder/src/abi/impls.rs @@ -0,0 +1,303 @@ +use crate::{ + execution::{Result, ResultWithPostInfo, WithPostDispatchInfo}, + types::*, + make_signature, + custom_signature::SignatureUnit, +}; +use super::{traits::*, ABI_ALIGNMENT, AbiReader, AbiWriter}; +use primitive_types::{U256, H160}; + +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +macro_rules! impl_abi_readable { + ($ty:ty, $method:ident, $dynamic:literal) => { + impl sealed::CanBePlacedInVec for $ty {} + + impl AbiType for $ty { + const SIGNATURE: SignatureUnit = make_signature!(new fixed(stringify!($ty))); + + fn is_dynamic() -> bool { + $dynamic + } + + fn size() -> usize { + ABI_ALIGNMENT + } + } + + impl AbiRead for $ty { + fn abi_read(reader: &mut AbiReader) -> Result<$ty> { + reader.$method() + } + } + }; +} + +impl_abi_readable!(uint32, uint32, false); +impl_abi_readable!(uint64, uint64, false); +impl_abi_readable!(uint128, uint128, false); +impl_abi_readable!(uint256, uint256, false); +impl_abi_readable!(bytes4, bytes4, false); +impl_abi_readable!(address, address, false); +impl_abi_readable!(string, string, true); + +impl sealed::CanBePlacedInVec for bool {} + +impl AbiType for bool { + const SIGNATURE: SignatureUnit = make_signature!(new fixed("bool")); + + fn is_dynamic() -> bool { + false + } + fn size() -> usize { + ABI_ALIGNMENT + } +} +impl AbiRead for bool { + fn abi_read(reader: &mut AbiReader) -> Result { + reader.bool() + } +} + +impl AbiType for uint8 { + const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint8")); + + fn is_dynamic() -> bool { + false + } + fn size() -> usize { + ABI_ALIGNMENT + } +} +impl AbiRead for uint8 { + fn abi_read(reader: &mut AbiReader) -> Result { + reader.uint8() + } +} + +impl AbiType for bytes { + const SIGNATURE: SignatureUnit = make_signature!(new fixed("bytes")); + + fn is_dynamic() -> bool { + true + } + fn size() -> usize { + ABI_ALIGNMENT + } +} +impl AbiRead for bytes { + fn abi_read(reader: &mut AbiReader) -> Result { + Ok(bytes(reader.bytes()?)) + } +} + +impl AbiRead for Vec { + fn abi_read(reader: &mut AbiReader) -> Result> { + let mut sub = reader.subresult(None)?; + let size = sub.uint32()? as usize; + sub.subresult_offset = sub.offset; + let mut out = Vec::with_capacity(size); + for _ in 0..size { + out.push(::abi_read(&mut sub)?); + } + Ok(out) + } +} + +impl AbiType for Vec { + const SIGNATURE: SignatureUnit = make_signature!(new nameof(R::SIGNATURE) fixed("[]")); + + fn is_dynamic() -> bool { + true + } + + fn size() -> usize { + ABI_ALIGNMENT + } +} + +impl sealed::CanBePlacedInVec for EthCrossAccount {} + +impl AbiType for EthCrossAccount { + const SIGNATURE: SignatureUnit = make_signature!(new fixed("(address,uint256)")); + + fn is_dynamic() -> bool { + address::is_dynamic() || uint256::is_dynamic() + } + + fn size() -> usize { +
::size() + ::size() + } +} + +impl AbiRead for EthCrossAccount { + fn abi_read(reader: &mut AbiReader) -> Result { + let size = if !EthCrossAccount::is_dynamic() { + Some(::size()) + } else { + None + }; + let mut subresult = reader.subresult(size)?; + let eth =
::abi_read(&mut subresult)?; + let sub = ::abi_read(&mut subresult)?; + + Ok(EthCrossAccount { eth, sub }) + } +} + +impl AbiWrite for EthCrossAccount { + fn abi_write(&self, writer: &mut AbiWriter) { + self.eth.abi_write(writer); + self.sub.abi_write(writer); + } +} + +macro_rules! impl_abi_writeable { + ($ty:ty, $method:ident) => { + impl AbiWrite for $ty { + fn abi_write(&self, writer: &mut AbiWriter) { + writer.$method(&self) + } + } + }; +} + +impl_abi_writeable!(u8, uint8); +impl_abi_writeable!(u32, uint32); +impl_abi_writeable!(u128, uint128); +impl_abi_writeable!(U256, uint256); +impl_abi_writeable!(H160, address); +impl_abi_writeable!(bool, bool); +impl_abi_writeable!(&str, string); + +impl AbiWrite for string { + fn abi_write(&self, writer: &mut AbiWriter) { + writer.string(self) + } +} + +impl AbiWrite for bytes { + fn abi_write(&self, writer: &mut AbiWriter) { + writer.bytes(self.0.as_slice()) + } +} + +impl AbiWrite for Vec { + fn abi_write(&self, writer: &mut AbiWriter) { + let is_dynamic = T::is_dynamic(); + let mut sub = if is_dynamic { + AbiWriter::new_dynamic(is_dynamic) + } else { + AbiWriter::new() + }; + + // Write items count + (self.len() as u32).abi_write(&mut sub); + + for item in self { + item.abi_write(&mut sub); + } + writer.write_subresult(sub); + } +} + +impl AbiWrite for () { + fn abi_write(&self, _writer: &mut AbiWriter) {} +} + +/// This particular AbiWrite implementation should be split to another trait, +/// which only implements `to_result`, but due to lack of specialization feature +/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`, +/// so here we abusing default trait methods for it +impl AbiWrite for ResultWithPostInfo { + fn abi_write(&self, _writer: &mut AbiWriter) { + debug_assert!(false, "shouldn't be called, see comment") + } + fn to_result(&self) -> ResultWithPostInfo { + match self { + Ok(v) => Ok(WithPostDispatchInfo { + post_info: v.post_info.clone(), + data: { + let mut out = AbiWriter::new(); + v.data.abi_write(&mut out); + out + }, + }), + Err(e) => Err(e.clone()), + } + } +} + +macro_rules! impl_tuples { + ($($ident:ident)+) => { + impl<$($ident: AbiType,)+> AbiType for ($($ident,)+) + where + $( + $ident: AbiType, + )+ + { + const SIGNATURE: SignatureUnit = make_signature!( + new fixed("(") + $(nameof(<$ident>::SIGNATURE) fixed(","))+ + shift_left(1) + fixed(")") + ); + + fn is_dynamic() -> bool { + false + $( + || <$ident>::is_dynamic() + )* + } + + fn size() -> usize { + 0 $(+ <$ident>::size())+ + } + } + + impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {} + + impl<$($ident),+> AbiRead for ($($ident,)+) + where + $($ident: AbiRead,)+ + ($($ident,)+): AbiType, + { + fn abi_read(reader: &mut AbiReader) -> Result<($($ident,)+)> { + let size = if !<($($ident,)+)>::is_dynamic() { Some(<($($ident,)+)>::size()) } else { None }; + let mut subresult = reader.subresult(size)?; + Ok(( + $(<$ident>::abi_read(&mut subresult)?,)+ + )) + } + } + + #[allow(non_snake_case)] + impl<$($ident),+> AbiWrite for ($($ident,)+) + where + $($ident: AbiWrite,)+ + { + fn abi_write(&self, writer: &mut AbiWriter) { + let ($($ident,)+) = self; + if writer.is_dynamic { + let mut sub = AbiWriter::new(); + $($ident.abi_write(&mut sub);)+ + writer.write_subresult(sub); + } else { + $($ident.abi_write(writer);)+ + } + } + } + }; +} + +impl_tuples! {A} +impl_tuples! {A B} +impl_tuples! {A B C} +impl_tuples! {A B C D} +impl_tuples! {A B C D E} +impl_tuples! {A B C D E F} +impl_tuples! {A B C D E F G} +impl_tuples! {A B C D E F G H} +impl_tuples! {A B C D E F G H I} +impl_tuples! {A B C D E F G H I J} --- /dev/null +++ b/crates/evm-coder/src/abi/mod.rs @@ -0,0 +1,339 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +//! Implementation of EVM RLP reader/writer + +#![allow(dead_code)] + +mod traits; +pub use traits::*; +mod impls; + +#[cfg(test)] +mod test; + +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; +use evm_core::ExitError; +use primitive_types::{H160, U256}; + +use crate::{ + execution::{Result, Error}, + types::*, +}; + +const ABI_ALIGNMENT: usize = 32; + +/// View into RLP data, which provides method to read typed items from it +#[derive(Clone)] +pub struct AbiReader<'i> { + buf: &'i [u8], + subresult_offset: usize, + offset: usize, +} +impl<'i> AbiReader<'i> { + /// Start reading RLP buffer, assuming there is no padding bytes + pub fn new(buf: &'i [u8]) -> Self { + Self { + buf, + subresult_offset: 0, + offset: 0, + } + } + /// Start reading RLP buffer, parsing first 4 bytes as selector + pub fn new_call(buf: &'i [u8]) -> Result<(bytes4, Self)> { + if buf.len() < 4 { + return Err(Error::Error(ExitError::OutOfOffset)); + } + let mut method_id = [0; 4]; + method_id.copy_from_slice(&buf[0..4]); + + Ok(( + method_id, + Self { + buf, + subresult_offset: 4, + offset: 4, + }, + )) + } + + fn read_pad( + buf: &[u8], + offset: usize, + pad_start: usize, + pad_size: usize, + block_start: usize, + block_size: usize, + ) -> Result<[u8; S]> { + if buf.len() - offset < ABI_ALIGNMENT { + return Err(Error::Error(ExitError::OutOfOffset)); + } + let mut block = [0; S]; + let is_pad_zeroed = buf[pad_start..pad_size].iter().all(|&v| v == 0); + if !is_pad_zeroed { + return Err(Error::Error(ExitError::InvalidRange)); + } + block.copy_from_slice(&buf[block_start..block_size]); + Ok(block) + } + + fn read_padleft(&mut self) -> Result<[u8; S]> { + let offset = self.offset; + self.offset += ABI_ALIGNMENT; + Self::read_pad( + self.buf, + offset, + offset, + offset + ABI_ALIGNMENT - S, + offset + ABI_ALIGNMENT - S, + offset + ABI_ALIGNMENT, + ) + } + + fn read_padright(&mut self) -> Result<[u8; S]> { + let offset = self.offset; + self.offset += ABI_ALIGNMENT; + Self::read_pad( + self.buf, + offset, + offset + S, + offset + ABI_ALIGNMENT, + offset, + offset + S, + ) + } + + /// Read [`H160`] at current position, then advance + pub fn address(&mut self) -> Result { + Ok(H160(self.read_padleft()?)) + } + + /// Read [`bool`] at current position, then advance + pub fn bool(&mut self) -> Result { + let data: [u8; 1] = self.read_padleft()?; + match data[0] { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(Error::Error(ExitError::InvalidRange)), + } + } + + /// Read [`[u8; 4]`] at current position, then advance + pub fn bytes4(&mut self) -> Result<[u8; 4]> { + self.read_padright() + } + + /// Read [`Vec`] at current position, then advance + pub fn bytes(&mut self) -> Result> { + let mut subresult = self.subresult(None)?; + let length = subresult.uint32()? as usize; + if subresult.buf.len() < subresult.offset + length { + return Err(Error::Error(ExitError::OutOfOffset)); + } + Ok(subresult.buf[subresult.offset..subresult.offset + length].into()) + } + + /// Read [`string`] at current position, then advance + pub fn string(&mut self) -> Result { + string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange)) + } + + /// Read [`u8`] at current position, then advance + pub fn uint8(&mut self) -> Result { + Ok(self.read_padleft::<1>()?[0]) + } + + /// Read [`u32`] at current position, then advance + pub fn uint32(&mut self) -> Result { + Ok(u32::from_be_bytes(self.read_padleft()?)) + } + + /// Read [`u128`] at current position, then advance + pub fn uint128(&mut self) -> Result { + Ok(u128::from_be_bytes(self.read_padleft()?)) + } + + /// Read [`U256`] at current position, then advance + pub fn uint256(&mut self) -> Result { + let buf: [u8; 32] = self.read_padleft()?; + Ok(U256::from_big_endian(&buf)) + } + + /// Read [`u64`] at current position, then advance + pub fn uint64(&mut self) -> Result { + Ok(u64::from_be_bytes(self.read_padleft()?)) + } + + /// Read [`usize`] at current position, then advance + #[deprecated = "dangerous, as usize may have different width in wasm and native execution"] + pub fn read_usize(&mut self) -> Result { + Ok(usize::from_be_bytes(self.read_padleft()?)) + } + + /// Slice recursive buffer, advance one word for buffer offset + /// If `size` is [`None`] then [`Self::offset`] and [`Self::subresult_offset`] evals from [`Self::buf`]. + fn subresult(&mut self, size: Option) -> Result> { + let subresult_offset = self.subresult_offset; + let offset = if let Some(size) = size { + self.offset += size; + self.subresult_offset += size; + 0 + } else { + self.uint32()? as usize + }; + + if offset + self.subresult_offset > self.buf.len() { + return Err(Error::Error(ExitError::InvalidRange)); + } + + let new_offset = offset + subresult_offset; + Ok(AbiReader { + buf: self.buf, + subresult_offset: new_offset, + offset: new_offset, + }) + } + + /// Is this parser reached end of buffer? + pub fn is_finished(&self) -> bool { + self.buf.len() == self.offset + } +} + +/// Writer for RLP encoded data +#[derive(Default)] +pub struct AbiWriter { + static_part: Vec, + dynamic_part: Vec<(usize, AbiWriter)>, + had_call: bool, + is_dynamic: bool, +} +impl AbiWriter { + /// Initialize internal buffers for output data, assuming no padding required + pub fn new() -> Self { + Self::default() + } + + /// Initialize internal buffers with data size + pub fn new_dynamic(is_dynamic: bool) -> Self { + Self { + is_dynamic, + ..Default::default() + } + } + /// Initialize internal buffers, inserting method selector at beginning + pub fn new_call(method_id: u32) -> Self { + let mut val = Self::new(); + val.static_part.extend(&method_id.to_be_bytes()); + val.had_call = true; + val + } + + fn write_padleft(&mut self, block: &[u8]) { + assert!(block.len() <= ABI_ALIGNMENT); + self.static_part + .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]); + self.static_part.extend(block); + } + + fn write_padright(&mut self, block: &[u8]) { + assert!(block.len() <= ABI_ALIGNMENT); + self.static_part.extend(block); + self.static_part + .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]); + } + + /// Write [`H160`] to end of buffer + pub fn address(&mut self, address: &H160) { + self.write_padleft(&address.0) + } + + /// Write [`bool`] to end of buffer + pub fn bool(&mut self, value: &bool) { + self.write_padleft(&[if *value { 1 } else { 0 }]) + } + + /// Write [`u8`] to end of buffer + pub fn uint8(&mut self, value: &u8) { + self.write_padleft(&[*value]) + } + + /// Write [`u32`] to end of buffer + pub fn uint32(&mut self, value: &u32) { + self.write_padleft(&u32::to_be_bytes(*value)) + } + + /// Write [`u128`] to end of buffer + pub fn uint128(&mut self, value: &u128) { + self.write_padleft(&u128::to_be_bytes(*value)) + } + + /// Write [`U256`] to end of buffer + pub fn uint256(&mut self, value: &U256) { + let mut out = [0; 32]; + value.to_big_endian(&mut out); + self.write_padleft(&out) + } + + /// Write [`usize`] to end of buffer + #[deprecated = "dangerous, as usize may have different width in wasm and native execution"] + pub fn write_usize(&mut self, value: &usize) { + self.write_padleft(&usize::to_be_bytes(*value)) + } + + /// Append recursive data, writing pending offset at end of buffer + pub fn write_subresult(&mut self, result: Self) { + self.dynamic_part.push((self.static_part.len(), result)); + // Empty block, to be filled later + self.write_padleft(&[]); + } + + fn memory(&mut self, value: &[u8]) { + let mut sub = Self::new(); + sub.uint32(&(value.len() as u32)); + for chunk in value.chunks(ABI_ALIGNMENT) { + sub.write_padright(chunk); + } + self.write_subresult(sub); + } + + /// Append recursive [`str`] at end of buffer + pub fn string(&mut self, value: &str) { + self.memory(value.as_bytes()) + } + + /// Append recursive [`[u8]`] at end of buffer + pub fn bytes(&mut self, value: &[u8]) { + self.memory(value) + } + + /// Finish writer, concatenating all internal buffers + pub fn finish(mut self) -> Vec { + for (static_offset, part) in self.dynamic_part { + let part_offset = self.static_part.len() + - if self.had_call { 4 } else { 0 } + - if self.is_dynamic { ABI_ALIGNMENT } else { 0 }; + + let encoded_dynamic_offset = usize::to_be_bytes(part_offset); + let start = static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len(); + let stop = static_offset + ABI_ALIGNMENT; + self.static_part[start..stop].copy_from_slice(&encoded_dynamic_offset); + self.static_part.extend(part.finish()) + } + self.static_part + } +} --- /dev/null +++ b/crates/evm-coder/src/abi/test.rs @@ -0,0 +1,297 @@ +use crate::{ + abi::{AbiRead, AbiWrite}, + types::*, +}; + +use super::{AbiReader, AbiWriter}; +use hex_literal::hex; +use primitive_types::{H160, U256}; +use concat_idents::concat_idents; + +macro_rules! test_impl { + ($name:ident, $type:ty, $function_identifier:expr, $decoded_data:expr, $encoded_data:expr) => { + concat_idents!(test_name = encode_decode_, $name { + #[test] + fn test_name() { + let function_identifier: u32 = $function_identifier; + let decoded_data = $decoded_data; + let encoded_data = $encoded_data; + + let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap(); + assert_eq!(call, u32::to_be_bytes(function_identifier)); + let data = <$type>::abi_read(&mut decoder).unwrap(); + assert_eq!(data, decoded_data); + + let mut writer = AbiWriter::new_call(function_identifier); + decoded_data.abi_write(&mut writer); + let ed = writer.finish(); + similar_asserts::assert_eq!(encoded_data, ed.as_slice()); + } + }); + }; +} + +macro_rules! test_impl_uint { + ($type:ident) => { + test_impl!( + $type, + $type, + 0xdeadbeef, + 255 as $type, + &hex!( + " + deadbeef + 00000000000000000000000000000000000000000000000000000000000000ff + " + ) + ); + }; +} + +test_impl_uint!(uint8); +test_impl_uint!(uint32); +test_impl_uint!(uint128); + +test_impl!( + uint256, + uint256, + 0xdeadbeef, + U256([255, 0, 0, 0]), + &hex!( + " + deadbeef + 00000000000000000000000000000000000000000000000000000000000000ff + " + ) +); + +test_impl!( + vec_tuple_address_uint256, + Vec<(address, uint256)>, + 0x1ACF2D55, + vec![ + ( + H160(hex!("2D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC")), + U256([10, 0, 0, 0]), + ), + ( + H160(hex!("AB8E3D9134955566483B11E6825C9223B6737B10")), + U256([20, 0, 0, 0]), + ), + ( + H160(hex!("8C582BDF2953046705FC56F189385255EFC1BE18")), + U256([30, 0, 0, 0]), + ), + ], + &hex!( + " + 1ACF2D55 + 0000000000000000000000000000000000000000000000000000000000000020 // offset of (address, uint256)[] + 0000000000000000000000000000000000000000000000000000000000000003 // length of (address, uint256)[] + + 0000000000000000000000002D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC // address + 000000000000000000000000000000000000000000000000000000000000000A // uint256 + + 000000000000000000000000AB8E3D9134955566483B11E6825C9223B6737B10 // address + 0000000000000000000000000000000000000000000000000000000000000014 // uint256 + + 0000000000000000000000008C582BDF2953046705FC56F189385255EFC1BE18 // address + 000000000000000000000000000000000000000000000000000000000000001E // uint256 + " + ) +); + +test_impl!( + vec_tuple_uint256_string, + Vec<(uint256, string)>, + 0xdeadbeef, + vec![ + (1.into(), "Test URI 0".to_string()), + (11.into(), "Test URI 1".to_string()), + (12.into(), "Test URI 2".to_string()), + ], + &hex!( + " + deadbeef + 0000000000000000000000000000000000000000000000000000000000000020 // offset of (uint256, string)[] + 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[] + + 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem + 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem + 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem + + 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60 + 0000000000000000000000000000000000000000000000000000000000000040 // offset of string + 000000000000000000000000000000000000000000000000000000000000000a // size of string + 5465737420555249203000000000000000000000000000000000000000000000 // string + + 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0 + 0000000000000000000000000000000000000000000000000000000000000040 // offset of string + 000000000000000000000000000000000000000000000000000000000000000a // size of string + 5465737420555249203100000000000000000000000000000000000000000000 // string + + 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160 + 0000000000000000000000000000000000000000000000000000000000000040 // offset of string + 000000000000000000000000000000000000000000000000000000000000000a // size of string + 5465737420555249203200000000000000000000000000000000000000000000 // string + " + ) +); + +#[test] +fn dynamic_after_static() { + let mut encoder = AbiWriter::new(); + encoder.bool(&true); + encoder.string("test"); + let encoded = encoder.finish(); + + let mut encoder = AbiWriter::new(); + encoder.bool(&true); + // Offset to subresult + encoder.uint32(&(32 * 2)); + // Len of "test" + encoder.uint32(&4); + encoder.write_padright(&[b't', b'e', b's', b't']); + let alternative_encoded = encoder.finish(); + + assert_eq!(encoded, alternative_encoded); + + let mut decoder = AbiReader::new(&encoded); + assert!(decoder.bool().unwrap()); + assert_eq!(decoder.string().unwrap(), "test"); +} + +#[test] +fn mint_sample() { + let (call, mut decoder) = AbiReader::new_call(&hex!( + " + 50bb4e7f + 000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374 + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000060 + 0000000000000000000000000000000000000000000000000000000000000008 + 5465737420555249000000000000000000000000000000000000000000000000 + " + )) + .unwrap(); + assert_eq!(call, u32::to_be_bytes(0x50bb4e7f)); + assert_eq!( + format!("{:?}", decoder.address().unwrap()), + "0xad2c0954693c2b5404b7e50967d3481bea432374" + ); + assert_eq!(decoder.uint32().unwrap(), 1); + assert_eq!(decoder.string().unwrap(), "Test URI"); +} + +#[test] +fn parse_vec_with_dynamic_type() { + let decoded_data = ( + 0x36543006, + vec![ + (1.into(), "Test URI 0".to_string()), + (11.into(), "Test URI 1".to_string()), + (12.into(), "Test URI 2".to_string()), + ], + ); + + let encoded_data = &hex!( + " + 36543006 + 00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address + 0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[] + 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[] + + 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem + 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem + 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem + + 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60 + 0000000000000000000000000000000000000000000000000000000000000040 // offset of string + 000000000000000000000000000000000000000000000000000000000000000a // size of string + 5465737420555249203000000000000000000000000000000000000000000000 // string + + 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0 + 0000000000000000000000000000000000000000000000000000000000000040 // offset of string + 000000000000000000000000000000000000000000000000000000000000000a // size of string + 5465737420555249203100000000000000000000000000000000000000000000 // string + + 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160 + 0000000000000000000000000000000000000000000000000000000000000040 // offset of string + 000000000000000000000000000000000000000000000000000000000000000a // size of string + 5465737420555249203200000000000000000000000000000000000000000000 // string + " + ); + + let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap(); + assert_eq!(call, u32::to_be_bytes(decoded_data.0)); + let address = decoder.address().unwrap(); + let data = >::abi_read(&mut decoder).unwrap(); + assert_eq!(data, decoded_data.1); + + let mut writer = AbiWriter::new_call(decoded_data.0); + address.abi_write(&mut writer); + decoded_data.1.abi_write(&mut writer); + let ed = writer.finish(); + similar_asserts::assert_eq!(encoded_data, ed.as_slice()); +} + +test_impl!( + vec_tuple_string_bytes, + Vec<(string, bytes)>, + 0xdeadbeef, + vec![ + ( + "Test URI 0".to_string(), + bytes(vec![ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 + ]) + ), + ( + "Test URI 1".to_string(), + bytes(vec![ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22 + ]) + ), + ("Test URI 2".to_string(), bytes(vec![0x33, 0x33])), + ], + &hex!( + " + deadbeef + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000003 + + 0000000000000000000000000000000000000000000000000000000000000060 + 0000000000000000000000000000000000000000000000000000000000000140 + 0000000000000000000000000000000000000000000000000000000000000220 + + 0000000000000000000000000000000000000000000000000000000000000040 + 0000000000000000000000000000000000000000000000000000000000000080 + 000000000000000000000000000000000000000000000000000000000000000a + 5465737420555249203000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000030 + 1111111111111111111111111111111111111111111111111111111111111111 + 1111111111111111111111111111111100000000000000000000000000000000 + + 0000000000000000000000000000000000000000000000000000000000000040 + 0000000000000000000000000000000000000000000000000000000000000080 + 000000000000000000000000000000000000000000000000000000000000000a + 5465737420555249203100000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000002f + 2222222222222222222222222222222222222222222222222222222222222222 + 2222222222222222222222222222220000000000000000000000000000000000 + + 0000000000000000000000000000000000000000000000000000000000000040 + 0000000000000000000000000000000000000000000000000000000000000080 + 000000000000000000000000000000000000000000000000000000000000000a + 5465737420555249203200000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000002 + 3333000000000000000000000000000000000000000000000000000000000000 + " + ) +); --- /dev/null +++ b/crates/evm-coder/src/abi/traits.rs @@ -0,0 +1,51 @@ +use super::{AbiReader, AbiWriter}; +use crate::{ + custom_signature::*, + execution::{Result, ResultWithPostInfo}, +}; +use core::str::from_utf8; + +/// Helper for type. +pub trait AbiType { + /// Signature for Etherium ABI. + const SIGNATURE: SignatureUnit; + + /// Signature as str. + fn as_str() -> &'static str { + from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8") + } + + /// Is type dynamic sized. + fn is_dynamic() -> bool; + + /// Size for type aligned to [`ABI_ALIGNMENT`]. + fn size() -> usize; +} + +/// Sealed traits. +pub mod sealed { + /// Not all types can be placed in vec, i.e `Vec` is restricted, `bytes` should be used instead + pub trait CanBePlacedInVec {} +} + +/// [`AbiReader`] implements reading of many types. +pub trait AbiRead { + /// Read item from current position, advanding decoder + fn abi_read(reader: &mut AbiReader) -> Result + where + Self: Sized; +} + +/// For questions about inability to provide custom implementations, +/// see [`AbiRead`] +pub trait AbiWrite { + /// Write value to end of specified encoder + fn abi_write(&self, writer: &mut AbiWriter); + /// Specialization for [`crate::solidity_interface`] implementation, + /// see comment in `impl AbiWrite for ResultWithPostInfo` + fn to_result(&self) -> ResultWithPostInfo { + let mut writer = AbiWriter::new(); + self.abi_write(&mut writer); + Ok(writer.into()) + } +} --- a/crates/evm-coder/src/lib.rs +++ b/crates/evm-coder/src/lib.rs @@ -121,53 +121,24 @@ use alloc::{vec::Vec}; use pallet_evm::account::CrossAccountId; use primitive_types::{U256, H160, H256}; - use core::str::from_utf8; - - use crate::custom_signature::SignatureUnit; - - pub trait Signature { - const SIGNATURE: SignatureUnit; - - fn as_str() -> &'static str { - from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8") - } - } - - impl Signature for bool { - const SIGNATURE: SignatureUnit = make_signature!(new fixed("bool")); - } - - macro_rules! define_simple_type { - (type $ident:ident = $ty:ty) => { - pub type $ident = $ty; - impl Signature for $ty { - const SIGNATURE: SignatureUnit = make_signature!(new fixed(stringify!($ident))); - } - }; - } - define_simple_type!(type address = H160); - - define_simple_type!(type uint8 = u8); - define_simple_type!(type uint16 = u16); - define_simple_type!(type uint32 = u32); - define_simple_type!(type uint64 = u64); - define_simple_type!(type uint128 = u128); - define_simple_type!(type uint256 = U256); - define_simple_type!(type bytes4 = [u8; 4]); - - define_simple_type!(type topic = H256); + pub type address = H160; + pub type uint8 = u8; + pub type uint16 = u16; + pub type uint32 = u32; + pub type uint64 = u64; + pub type uint128 = u128; + pub type uint256 = U256; + pub type bytes4 = [u8; 4]; + pub type topic = H256; #[cfg(not(feature = "std"))] - define_simple_type!(type string = ::alloc::string::String); + pub type string = ::alloc::string::String; #[cfg(feature = "std")] - define_simple_type!(type string = ::std::string::String); + pub type string = ::std::string::String; #[derive(Default, Debug, PartialEq)] pub struct bytes(pub Vec); - impl Signature for bytes { - const SIGNATURE: SignatureUnit = make_signature!(new fixed("bytes")); - } /// Solidity doesn't have `void` type, however we have special implementation /// for empty tuple return type @@ -257,10 +228,6 @@ Err("All fields of cross account is non zeroed".into()) } } - } - - impl Signature for EthCrossAccount { - const SIGNATURE: SignatureUnit = make_signature!(new fixed("(address,uint256)")); } /// Convert `CrossAccountId` to `uint256`. --- a/crates/evm-coder/tests/random.rs +++ b/crates/evm-coder/tests/random.rs @@ -16,8 +16,9 @@ #![allow(dead_code)] // This test only checks that macros is not panicking -use evm_coder::{ToLog, execution::Result, solidity_interface, types::*, solidity, weight}; -use evm_coder::{types::Signature}; +use evm_coder::{ + abi::AbiType, ToLog, execution::Result, solidity_interface, types::*, solidity, weight, +}; pub struct Impls; --- a/crates/evm-coder/tests/solidity_generation.rs +++ b/crates/evm-coder/tests/solidity_generation.rs @@ -14,8 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -use evm_coder::{execution::Result, generate_stubgen, solidity_interface, types::*}; -use evm_coder::{types::Signature}; +use evm_coder::{abi::AbiType, execution::Result, generate_stubgen, solidity_interface, types::*}; pub struct ERC20; --- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -17,6 +17,7 @@ //! This module contains the implementation of pallet methods for evm. use evm_coder::{ + abi::AbiType, solidity_interface, solidity, ToLog, types::*, execution::{Result, Error}, --- a/pallets/evm-contract-helpers/src/eth.rs +++ b/pallets/evm-contract-helpers/src/eth.rs @@ -19,7 +19,11 @@ extern crate alloc; use core::marker::PhantomData; use evm_coder::{ - abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog, + abi::{AbiWriter, AbiType}, + execution::Result, + generate_stubgen, solidity_interface, + types::*, + ToLog, }; use pallet_evm::{ ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle, --- a/pallets/fungible/src/erc.rs +++ b/pallets/fungible/src/erc.rs @@ -19,7 +19,9 @@ extern crate alloc; use core::char::{REPLACEMENT_CHARACTER, decode_utf16}; use core::convert::TryInto; -use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight}; +use evm_coder::{ + abi::AbiType, ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight, +}; use up_data_structs::CollectionMode; use pallet_common::erc::{CommonEvmHandler, PrecompileResult}; use sp_std::vec::Vec; --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -24,7 +24,10 @@ char::{REPLACEMENT_CHARACTER, decode_utf16}, convert::TryInto, }; -use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight}; +use evm_coder::{ + abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, + weight, +}; use frame_support::BoundedVec; use up_data_structs::{ TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey, --- a/pallets/refungible/src/erc.rs +++ b/pallets/refungible/src/erc.rs @@ -25,7 +25,10 @@ char::{REPLACEMENT_CHARACTER, decode_utf16}, convert::TryInto, }; -use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight}; +use evm_coder::{ + abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, + weight, +}; use frame_support::{BoundedBTreeMap, BoundedVec}; use pallet_common::{ CollectionHandle, CollectionPropertyPermissions, --- a/pallets/refungible/src/erc_token.rs +++ b/pallets/refungible/src/erc_token.rs @@ -29,7 +29,9 @@ convert::TryInto, ops::Deref, }; -use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight}; +use evm_coder::{ + abi::AbiType, ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight, +}; use pallet_common::{ CommonWeightInfo, erc::{CommonEvmHandler, PrecompileResult}, --- a/pallets/unique/Cargo.toml +++ b/pallets/unique/Cargo.toml @@ -35,6 +35,7 @@ try-runtime = ["frame-support/try-runtime"] limit-testing = ["up-data-structs/limit-testing"] stubgen = ["evm-coder/stubgen", "pallet-common/stubgen"] + ################################################################################ # Standart Dependencies --- a/pallets/unique/src/eth/mod.rs +++ b/pallets/unique/src/eth/mod.rs @@ -18,14 +18,16 @@ use core::marker::PhantomData; use ethereum as _; -use evm_coder::{execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight}; +use evm_coder::{ + abi::AbiType, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight, +}; use frame_support::traits::Get; use crate::Pallet; use pallet_common::{ CollectionById, dispatch::CollectionDispatch, - erc::{static_property::key, CollectionHelpersEvents}, + erc::{CollectionHelpersEvents, static_property::key}, Pallet as PalletCommon, }; use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};