From e7090813beb3174d9e855cd385b922c53a4b6912 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Wed, 02 Jun 2021 17:48:41 +0000 Subject: [PATCH] refactor: move evm-coder impl to its own crate --- --- /dev/null +++ b/crates/evm-coder/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "evm-coder" +version = "0.1.0" +edition = "2018" + +[dependencies] +evm-coder-macros = { path = "../evm-coder-macros" } +primitive-types = { version = "0.9", default-features = false } +hex-literal = "0.3" +ethereum = { version = "0.7.1", default-features = false } + +[dev-dependencies] +hex = "0.4.3" + +[features] +default = ["std"] +std = ["ethereum/std", "primitive-types/std"] \ No newline at end of file --- /dev/null +++ b/crates/evm-coder/src/abi.rs @@ -0,0 +1,311 @@ +//! TODO: I misunterstood therminology, abi IS rlp encoded, so +//! this module should be replaced with rlp crate + +#![allow(dead_code)] + +#[cfg(not(feature = "std"))] +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; +use primitive_types::{H160, U256}; + +use crate::types::string; + +const ABI_ALIGNMENT: usize = 32; + +pub type Result = core::result::Result; + +#[derive(Clone)] +pub struct AbiReader<'i> { + buf: &'i [u8], + offset: usize, +} +impl<'i> AbiReader<'i> { + pub fn new(buf: &'i [u8]) -> Self { + Self { buf, offset: 0 } + } + pub fn new_call(buf: &'i [u8]) -> Result<(u32, Self)> { + if buf.len() < 4 { + return Err("missing method id"); + } + let mut method_id = [0; 4]; + method_id.copy_from_slice(&buf[0..4]); + + Ok((u32::from_be_bytes(method_id), Self { buf, offset: 4 })) + } + + fn read_padleft(&mut self) -> Result<[u8; S]> { + if self.buf.len() - self.offset < 32 { + return Err("missing padding"); + } + let mut block = [0; S]; + // Verify padding is empty + if !self.buf[self.offset..self.offset + ABI_ALIGNMENT - S] + .iter() + .all(|&v| v == 0) + { + return Err("non zero padding (wrong types?)"); + } + block.copy_from_slice( + &self.buf[self.offset + ABI_ALIGNMENT - S..self.offset + ABI_ALIGNMENT], + ); + self.offset += ABI_ALIGNMENT; + Ok(block) + } + + pub fn address(&mut self) -> Result { + Ok(H160(self.read_padleft()?)) + } + + pub fn bool(&mut self) -> Result { + let data: [u8; 1] = self.read_padleft()?; + match data[0] { + 0 => Ok(false), + 1 => Ok(true), + _ => Err("wrong bool value"), + } + } + + pub fn bytes4(&mut self) -> Result<[u8; 4]> { + self.read_padleft() + } + + pub fn bytes(&mut self) -> Result> { + let mut subresult = self.subresult()?; + let length = subresult.read_usize()?; + if subresult.buf.len() <= subresult.offset + length { + return Err("bytes out of bounds"); + } + Ok(subresult.buf[subresult.offset..subresult.offset + length].into()) + } + + pub fn uint32(&mut self) -> Result { + Ok(u32::from_be_bytes(self.read_padleft()?)) + } + + pub fn uint128(&mut self) -> Result { + Ok(u128::from_be_bytes(self.read_padleft()?)) + } + + pub fn uint256(&mut self) -> Result { + let buf: [u8; 32] = self.read_padleft()?; + Ok(U256::from_big_endian(&buf)) + } + + pub fn uint64(&mut self) -> Result { + Ok(u64::from_be_bytes(self.read_padleft()?)) + } + + pub fn read_usize(&mut self) -> Result { + Ok(usize::from_be_bytes(self.read_padleft()?)) + } + + fn subresult(&mut self) -> Result> { + let offset = self.read_usize()?; + Ok(AbiReader { + buf: &self.buf, + offset: offset + self.offset, + }) + } + + pub fn is_finished(&self) -> bool { + self.buf.len() == self.offset + } +} + +#[derive(Default)] +pub struct AbiWriter { + static_part: Vec, + dynamic_part: Vec<(usize, AbiWriter)>, +} +impl AbiWriter { + pub fn new() -> Self { + Self::default() + } + pub fn new_call(method_id: u32) -> Self { + let mut val = Self::new(); + val.static_part.extend(&method_id.to_be_bytes()); + 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, bytes: &[u8]) { + assert!(bytes.len() <= ABI_ALIGNMENT); + self.static_part.extend(bytes); + self.static_part + .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]); + } + + pub fn address(&mut self, address: &H160) { + self.write_padleft(&address.0) + } + + pub fn bool(&mut self, value: &bool) { + self.write_padleft(&[if *value { 1 } else { 0 }]) + } + + pub fn uint8(&mut self, value: &u8) { + self.write_padleft(&[*value]) + } + + pub fn uint32(&mut self, value: &u32) { + self.write_padleft(&u32::to_be_bytes(*value)) + } + + pub fn uint128(&mut self, value: &u128) { + self.write_padleft(&u128::to_be_bytes(*value)) + } + + /// This method writes u128, and exists only for convenience, because there is + /// no u256 support in rust + pub fn uint256(&mut self, value: &U256) { + let mut out = [0; 32]; + value.to_big_endian(&mut out); + self.write_padleft(&out) + } + + pub fn write_usize(&mut self, value: &usize) { + self.write_padleft(&usize::to_be_bytes(*value)) + } + + 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(&[]); + } + + pub fn memory(&mut self, value: &[u8]) { + let mut sub = Self::new(); + sub.write_usize(&value.len()); + for chunk in value.chunks(ABI_ALIGNMENT) { + sub.write_padright(chunk); + } + self.write_subresult(sub); + } + + pub fn string(&mut self, value: &str) { + self.memory(value.as_bytes()) + } + + pub fn finish(mut self) -> Vec { + for (static_offset, part) in self.dynamic_part { + let part_offset = self.static_part.len(); + + let encoded_dynamic_offset = usize::to_be_bytes(part_offset - static_offset); + self.static_part[static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len() + ..static_offset + ABI_ALIGNMENT] + .copy_from_slice(&encoded_dynamic_offset); + self.static_part.extend(part.finish()) + } + self.static_part + } +} + +pub trait AbiRead { + fn abi_read(&mut self) -> Result; +} + +macro_rules! impl_abi_readable { + ($ty:ty, $method:ident) => { + impl AbiRead<$ty> for AbiReader<'_> { + fn abi_read(&mut self) -> Result<$ty> { + self.$method() + } + } + }; +} + +impl_abi_readable!(u32, uint32); +impl_abi_readable!(u128, uint128); +impl_abi_readable!(U256, uint256); +impl_abi_readable!(H160, address); +impl_abi_readable!(Vec, bytes); +impl_abi_readable!(bool, bool); + +pub trait AbiWrite { + fn abi_write(&self, writer: &mut AbiWriter); +} + +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 () { + fn abi_write(&self, _writer: &mut AbiWriter) {} +} + +/// Error, which can be constructed from any ToString type +/// Encoded to Abi as Error(string) +#[derive(Debug)] +pub struct StringError(String); + +impl From for StringError +where + E: ToString, +{ + fn from(e: E) -> Self { + Self(e.to_string()) + } +} + +impl From for AbiWriter { + fn from(v: StringError) -> Self { + let mut out = AbiWriter::new_call(crate::fn_selector!(Error(string))); + out.string(&v.0); + out + } +} + +#[macro_export] +macro_rules! abi_decode { + ($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => { + $( + let $name = $reader.$typ()?; + )+ + } +} +#[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 + }} +} --- /dev/null +++ b/crates/evm-coder/src/events.rs @@ -0,0 +1,42 @@ +use ethereum::Log; +use primitive_types::{H160, H256}; + +use crate::types::*; + +pub trait ToLog { + fn to_log(&self, contract: H160) -> Log; +} + +pub trait ToTopic { + fn to_topic(&self) -> H256; +} + +impl ToTopic for H256 { + fn to_topic(&self) -> H256 { + *self + } +} + +impl ToTopic for uint256 { + fn to_topic(&self) -> H256 { + let mut out = [0u8; 32]; + self.to_big_endian(&mut out); + H256(out) + } +} + +impl ToTopic for address { + fn to_topic(&self) -> H256 { + let mut out = [0u8; 32]; + out[12..32].copy_from_slice(&self.0); + H256(out) + } +} + +impl ToTopic for uint32 { + fn to_topic(&self) -> H256 { + let mut out = [0u8; 32]; + out[28..32].copy_from_slice(&self.to_be_bytes()); + H256(out) + } +} --- /dev/null +++ b/crates/evm-coder/src/lib.rs @@ -0,0 +1,69 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(not(feature = "std"))] +extern crate alloc; + +pub use evm_coder_macros::{event_topic, fn_selector, solidity_interface, solidity, ToLog}; +pub mod abi; +pub mod events; +pub use events::ToLog; + +/// Solidity type definitions +pub mod types { + #![allow(non_camel_case_types)] + + #[cfg(not(feature = "std"))] + use alloc::{vec::Vec}; + use primitive_types::{U256, H160, 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 = u32; + + pub type topic = H256; + + #[cfg(not(feature = "std"))] + pub type string = ::alloc::string::String; + #[cfg(feature = "std")] + pub type string = ::std::string::String; + pub type bytes = Vec; + + pub type void = (); + + //#region Special types + /// Makes function payable + pub type value = U256; + /// Makes function caller-sensitive + pub type caller = address; + //#endregion + + pub struct Msg { + pub call: C, + pub caller: H160, + pub value: U256, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn function_selector_generation() { + assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb); + } + + #[test] + fn event_topic_generation() { + assert_eq!( + hex::encode(&event_topic!(Transfer(address, address, uint256))[..]), + "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + ); + } +} --- /dev/null +++ b/crates/evm-coder/tests/a.rs @@ -0,0 +1,69 @@ +use evm_coder::{solidity_interface, types::*, ToLog}; +use evm_coder_macros::solidity; + +#[solidity_interface] +trait OurInterface { + type Error; + fn fn_a(&self, input: uint256) -> Result; +} + +#[solidity_interface] +trait OurInterface1 { + type Error; + fn fn_b(&self, input: uint128) -> Result; +} + +#[solidity_interface(is(OurInterface), inline_is(OurInterface1), events(ERC721Log))] +trait OurInterface2 { + type Error; + #[solidity(rename_selector = "fnK")] + fn fn_c(&self, input: uint32) -> Result; + fn fn_d(&self, value: uint32) -> Result; + + fn caller_sensitive(&self, caller: caller) -> Result; + fn payable(&mut self, value: value) -> Result; +} + +#[derive(ToLog)] +enum ERC721Log { + Transfer { + #[indexed] + from: address, + #[indexed] + to: address, + value: uint256, + }, + Eee { + #[indexed] + aaa: address, + bbb: uint256, + }, +} + +#[solidity_interface] +trait ERC20 { + type Error; + + fn decimals(&self) -> Result; + fn balance_of(&self, owner: address) -> Result; + fn transfer( + &mut self, + caller: caller, + to: address, + value: uint256, + ) -> Result; + fn transfer_from( + &mut self, + caller: caller, + from: address, + to: address, + value: uint256, + ) -> Result; + fn approve( + &mut self, + caller: caller, + spender: address, + value: uint256, + ) -> Result; + fn allowance(&self, owner: address, spender: address) -> Result; +} --- a/pallets/nft/src/eth/abi.rs +++ /dev/null @@ -1,204 +0,0 @@ -//! TODO: I misunterstood therminology, abi IS rlp encoded, so -//! this module should be replaced with rlp crate - -use sp_core::H160; - -#[cfg(feature = "std")] -use std; -#[cfg(not(feature = "std"))] -use sp_std as std; - -use std::vec::Vec; - -const ABI_ALIGNMENT: usize = 32; - -type Result = std::result::Result; - -pub struct AbiReader<'i> { - buf: &'i [u8], - offset: usize, -} -impl<'i> AbiReader<'i> { - pub fn new_call(buf: &'i [u8]) -> Result<(u32, Self)> { - if buf.len() < 4 { - return Err("missing method id") - } - let mut method_id = [0; 4]; - method_id.copy_from_slice(&buf[0..4]); - - Ok((u32::from_be_bytes(method_id), Self { - buf, - offset: 4, - })) - } - - fn read_padleft(&mut self) -> Result<[u8; S]> { - if self.buf.len() - self.offset < 32 { - return Err("missing padding") - } - let mut block = [0; S]; - // Verify padding is empty - if !self.buf[self.offset..self.offset + ABI_ALIGNMENT - S].iter().all(|&v| v == 0) { - return Err("non zero padding (wrong types?)") - } - block.copy_from_slice(&self.buf[self.offset + ABI_ALIGNMENT - S..self.offset + ABI_ALIGNMENT]); - self.offset += ABI_ALIGNMENT; - Ok(block) - } - - pub fn address(&mut self) -> Result { - Ok(H160(self.read_padleft()?)) - } - - pub fn uint32(&mut self) -> Result { - Ok(u32::from_be_bytes(self.read_padleft()?)) - } - - pub fn uint128(&mut self) -> Result { - Ok(u128::from_be_bytes(self.read_padleft()?)) - } - - pub fn uint256(&mut self) -> Result { - self.uint128() - } - - pub fn uint64(&mut self) -> Result { - Ok(usize::from_be_bytes(self.read_padleft()?)) - } - - fn subresult(&mut self) -> Result> { - let offset = self.uint64()?; - if offset > usize::MAX { - return Err("subresult overflow"); - } - Ok(AbiReader { - buf: &self.buf, - offset: offset + self.offset, - }) - } - - pub fn is_finished(&self) -> bool { - self.buf.len() == self.offset - } -} - -pub struct AbiWriter { - static_part: Vec, - dynamic_part: Vec<(usize, AbiWriter)>, -} -impl AbiWriter { - pub fn new() -> Self { - Self { - static_part: Vec::new(), - dynamic_part: Vec::new(), - } - } - pub fn new_call(method_id: u32) -> Self { - let mut val = Self::new(); - val.static_part.extend(&method_id.to_be_bytes()); - 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, bytes: &[u8]) { - assert!(bytes.len() <= ABI_ALIGNMENT); - self.static_part.extend(bytes); - self.static_part.extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]); - } - - pub fn address(&mut self, address: H160) { - self.write_padleft(&address.0) - } - - pub fn bool(&mut self, value: bool) { - self.write_padleft(&[if value { 1 } else { 0 }]) - } - - pub fn uint8(&mut self, value: u8) { - self.write_padleft(&[value]) - } - - pub fn uint128(&mut self, value: u128) { - self.write_padleft(&u128::to_be_bytes(value)) - } - - /// This method writes u128, and exists only for convenience, because there is - /// no u256 support in rust - pub fn uint256(&mut self, value: u128) { - self.uint128(value) - } - - pub fn write_usize(&mut self, value: usize) { - self.write_padleft(&usize::to_be_bytes(value)) - } - - pub fn write_u8(&mut self, value: u8) { - self.write_padleft(&[value]) - } - - 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(&[]); - } - - pub fn memory(&mut self, value: &[u8]) { - let mut sub = Self::new(); - sub.write_usize(value.len()); - for chunk in value.chunks(ABI_ALIGNMENT) { - sub.write_padright(chunk); - } - self.write_subresult(sub); - } - - pub fn string(&mut self, value: &str) { - self.memory(value.as_bytes()) - } - - pub fn finish(mut self) -> Vec { - for (static_offset, part) in self.dynamic_part { - let part_offset = self.static_part.len(); - - let encoded_dynamic_offset = usize::to_be_bytes(part_offset - static_offset); - self.static_part - [static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len()..static_offset + ABI_ALIGNMENT] - .copy_from_slice(&encoded_dynamic_offset); - self.static_part.extend(part.finish()) - } - self.static_part - } -} - -#[macro_export] -macro_rules! abi_decode { - ($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => { - $( - let $name = $reader.$typ()?; - )+ - } -} - -#[macro_export] -macro_rules! abi_encode { - ($($typ:ident($value:expr)),* $(,)?) => {{ - #[allow(unused_mut)] - let mut writer = crate::eth::abi::AbiWriter::new(); - $( - writer.$typ($value); - )* - writer - }}; - (call $val:expr; $($typ:ident($value:expr)),* $(,)?) => {{ - #[allow(unused_mut)] - let mut writer = AbiWriter::new_call($val); - $( - writer.$typ($value); - )* - writer - }} -} \ No newline at end of file -- gitstuff