Skip to content

Commit

Permalink
Add macro function selector (#1172)
Browse files Browse the repository at this point in the history
* add generate_function_selector macro

* replace Debug to RuntimeDebug
  • Loading branch information
zjb0807 authored Jul 5, 2021
1 parent deda3f6 commit 5f2624f
Show file tree
Hide file tree
Showing 17 changed files with 209 additions and 126 deletions.
16 changes: 14 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"modules/*",
"inspect",
"primitives",
"primitives/proc-macro",
"rpc",

"runtime/common",
Expand Down
2 changes: 1 addition & 1 deletion modules/evm-bridge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ primitive-types = { version = "0.9.0", default-features = false, features = ["rl
impl-trait-for-tuples = "0.2.1"
ethereum-types = { version = "0.11.0", default-features = false }
primitives = { package = "acala-primitives", path = "../../primitives", default-features = false }
primitives-proc-macro = { path = "../../primitives/proc-macro" }
support = { package = "module-support", path = "../support", default-features = false }
module-evm = { path = "../evm", default-features = false }
num_enum = { version = "0.5.1", default-features = false }

[dev-dependencies]
sha3 = { version = "0.9.1" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.7" }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.7" }

Expand Down
22 changes: 10 additions & 12 deletions modules/evm-bridge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ use frame_support::{
use module_evm::{ExitReason, ExitSucceed};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use primitive_types::H256;
use primitives::create_function_selector;
use sp_core::{H160, U256};
use sp_runtime::SaturatedConversion;
use sp_std::vec::Vec;
Expand All @@ -36,17 +35,16 @@ use support::{EVMBridge as EVMBridgeTrait, ExecutionMode, InvokeContext, EVM};
type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
type BalanceOf<T> = <<T as Config>::EVM as EVM<AccountIdOf<T>>>::Balance;

create_function_selector! {
#[derive(Debug, Eq, PartialEq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Action {
Name("name()") = 0x06fdde03_u32,
Symbol("symbol()") = 0x95d89b41_u32,
Decimals("decimals()") = 0x313ce567_u32,
TotalSupply("totalSupply()") = 0x18160ddd_u32,
BalanceOf("balanceOf(address)") = 0x70a08231_u32,
Transfer("transfer(address,uint256)") = 0xa9059cbb_u32,
}
#[primitives_proc_macro::generate_function_selector]
#[derive(RuntimeDebug, Eq, PartialEq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Action {
Name = "name()",
Symbol = "symbol()",
Decimals = "decimals()",
TotalSupply = "totalSupply()",
BalanceOf = "balanceOf(address)",
Transfer = "transfer(address,uint256)",
}

mod mock;
Expand Down
1 change: 1 addition & 0 deletions primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ chainbridge = { git = "https://github.com/AcalaNetwork/chainbridge-substrate", d
[dev-dependencies]
sha3 = { version = "0.9.1" }
serde_json = { version = "1.0.64" }
primitives-proc-macro = { version = "1.1.0", path = "./proc-macro" }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.7" }

[features]
Expand Down
15 changes: 15 additions & 0 deletions primitives/proc-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "primitives-proc-macro"
version = "1.1.1"
authors = ["Acala Developers"]
edition = "2018"

[lib]
proc-macro = true

[dependencies]
sha3 = { version = "0.9.1" }
quote = "1.0.3"
syn = { version = "1.0.58", features = ["full", "fold", "extra-traits", "visit"] }
proc-macro2 = "1.0.6"
proc-macro-crate = "1.0.0"
78 changes: 78 additions & 0 deletions primitives/proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// This file is part of Acala.

// Copyright (C) 2020-2021 Acala Foundation.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program 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.

// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.

use proc_macro::TokenStream;
use proc_macro2::Literal;
use quote::quote;
use sha3::{Digest, Keccak256};
use std::convert::TryInto;
use syn::{parse_macro_input, Expr, ExprLit, Ident, ItemEnum, Lit};

#[proc_macro_attribute]
pub fn generate_function_selector(_: TokenStream, input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as ItemEnum);

let ItemEnum {
attrs,
vis,
enum_token,
ident,
variants,
..
} = item;

let mut ident_expressions: Vec<Ident> = vec![];
let mut variant_expressions: Vec<Expr> = vec![];
for variant in variants {
if let Some((_, Expr::Lit(ExprLit { lit, .. }))) = variant.discriminant {
if let Lit::Str(token) = lit {
let selector = get_function_selector(&token.value());
// println!("method: {:?}, selector: {:?}", token.value(), selector);
ident_expressions.push(variant.ident);
variant_expressions.push(Expr::Lit(ExprLit {
lit: Lit::Verbatim(Literal::u32_suffixed(selector)),
attrs: Default::default(),
}));
} else {
panic!("Not method string: `{:?}`", lit);
}
} else {
panic!("Not enum: `{:?}`", variant);
}
}

(quote! {
#(#attrs)*
#vis #enum_token #ident {
#(
#ident_expressions = #variant_expressions,
)*
}
})
.into()
}

fn get_function_selector(s: &str) -> u32 {
// create a SHA3-256 object
let mut hasher = Keccak256::new();
// write input message
hasher.update(s);
// read hash digest
let result = hasher.finalize();
u32::from_be_bytes(result[..4].try_into().unwrap())
}
48 changes: 2 additions & 46 deletions primitives/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ pub use evm::Config;
/// Evm Address.
pub type EvmAddress = sp_core::H160;

#[derive(Clone, Eq, PartialEq, Encode, Decode, Default)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
#[derive(Clone, Eq, PartialEq, Encode, Decode, Default, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
/// External input from the transaction.
pub struct Vicinity {
/// Current transaction gas price.
Expand Down Expand Up @@ -85,47 +85,3 @@ pub struct EstimateResourcesRequest {
/// Data
pub data: Option<Vec<u8>>,
}

#[macro_export]
macro_rules! create_function_selector {
($(#[$meta:meta])*
$vis:vis enum Action {
$($(#[$vmeta:meta])* $symbol:ident($method:expr) = $val:literal,)*
}) => {
$(#[$meta])*
$vis enum Action {
$($(#[$vmeta])* $symbol = $val,)*
}

#[cfg(test)]
mod function_selector_tests {
use sha3::{Digest, Keccak256};
use sp_std::convert::TryInto;

fn get_function_selector(s: &str) -> u32 {
// create a SHA3-256 object
let mut hasher = Keccak256::new();
// write input message
hasher.update(s);
// read hash digest
let result = hasher.finalize();
u32::from_be_bytes(result[..4].try_into().unwrap())
}

#[test]
fn method_hash_works() {
$(assert_eq!(get_function_selector($method), $val, "method: {:?}", $method);)*
}
}
}
}

#[cfg(test)]
create_function_selector! {
#[derive(Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum Action {
QueryName("name()") = 0x06fdde03_u32,
QuerySymbol("symbol()") = 0x95d89b41_u32,
}
}
22 changes: 22 additions & 0 deletions primitives/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,25 @@ fn currency_id_try_into_evm_address_works() {
let erc20 = EvmAddress::from_str("0x1111111111111111111111111111111111111111").unwrap();
assert_eq!(EvmAddress::try_from(CurrencyId::Erc20(erc20)), Ok(erc20));
}

#[test]
fn generate_function_selector_works() {
#[primitives_proc_macro::generate_function_selector]
#[derive(RuntimeDebug, Eq, PartialEq)]
#[repr(u32)]
pub enum Action {
Name = "name()",
Symbol = "symbol()",
Decimals = "decimals()",
TotalSupply = "totalSupply()",
BalanceOf = "balanceOf(address)",
Transfer = "transfer(address,uint256)",
}

assert_eq!(Action::Name as u32, 0x06fdde03_u32);
assert_eq!(Action::Symbol as u32, 0x95d89b41_u32);
assert_eq!(Action::Decimals as u32, 0x313ce567_u32);
assert_eq!(Action::TotalSupply as u32, 0x18160ddd_u32);
assert_eq!(Action::BalanceOf as u32, 0x70a08231_u32);
assert_eq!(Action::Transfer as u32, 0xa9059cbb_u32);
}
2 changes: 1 addition & 1 deletion runtime/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ module-evm = { path = "../../modules/evm", default-features = false }
module-staking-pool = { path = "../../modules/staking-pool", default-features = false }
module-support = { path = "../../modules/support", default-features = false }
primitives = { package = "acala-primitives", path = "../../primitives", default-features = false }
primitives-proc-macro = { path = "../../primitives/proc-macro" }

[dev-dependencies]
serde_json = "1.0.64"
sha3 = { version = "0.9.1" }
hex-literal = "0.3.1"

sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.7" }
Expand Down
28 changes: 14 additions & 14 deletions runtime/common/src/precompile/dex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ use frame_support::log;
use module_evm::{Context, ExitError, ExitSucceed, Precompile};
use module_support::{AddressMapping as AddressMappingT, CurrencyIdMapping as CurrencyIdMappingT, DEXManager};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use primitives::{create_function_selector, Balance, CurrencyId};
use primitives::{Balance, CurrencyId};
use sp_core::U256;
use sp_runtime::RuntimeDebug;
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*, result};

/// The `DEX` impl precompile.
Expand All @@ -38,19 +39,18 @@ pub struct DexPrecompile<AccountId, AddressMapping, CurrencyIdMapping, Dex>(
PhantomData<(AccountId, AddressMapping, CurrencyIdMapping, Dex)>,
);

create_function_selector! {
#[derive(Debug, Eq, PartialEq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Action {
GetLiquidityPool("getLiquidityPool(address,address)") = 0xf4f31ede_u32,
GetLiquidityTokenAddress("getLiquidityTokenAddress(address,address)") = 0xffd73c4a_u32,
GetSwapTargetAmount("getSwapTargetAmount(address[],uint256)") = 0x4d60beb1_u32,
GetSwapSupplyAmount("getSwapSupplyAmount(address[],uint256)") = 0xdbcd19a2_u32,
SwapWithExactSupply("swapWithExactSupply(address,address[],uint256,uint256)") = 0x579baa18_u32,
SwapWithExactTarget("swapWithExactTarget(address,address[],uint256,uint256)") = 0x9782ac81_u32,
AddLiquidity("addLiquidity(address,address,address,uint256,uint256,uint256)") = 0x67088D59_u32,
RemoveLiquidity("removeLiquidity(address,address,address,uint256,uint256,uint256)") = 0x35315332_u32,
}
#[primitives_proc_macro::generate_function_selector]
#[derive(RuntimeDebug, Eq, PartialEq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Action {
GetLiquidityPool = "getLiquidityPool(address,address)",
GetLiquidityTokenAddress = "getLiquidityTokenAddress(address,address)",
GetSwapTargetAmount = "getSwapTargetAmount(address[],uint256)",
GetSwapSupplyAmount = "getSwapSupplyAmount(address[],uint256)",
SwapWithExactSupply = "swapWithExactSupply(address,address[],uint256,uint256)",
SwapWithExactTarget = "swapWithExactTarget(address,address[],uint256,uint256)",
AddLiquidity = "addLiquidity(address,address,address,uint256,uint256,uint256)",
RemoveLiquidity = "removeLiquidity(address,address,address,uint256,uint256,uint256)",
}

impl<AccountId, AddressMapping, CurrencyIdMapping, Dex> Precompile
Expand Down
3 changes: 2 additions & 1 deletion runtime/common/src/precompile/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,12 @@ mod tests {
use frame_support::{assert_err, assert_ok};
use num_enum::TryFromPrimitive;
use sp_core::H160;
use sp_runtime::RuntimeDebug;

use module_support::mocks::{MockAddressMapping, MockCurrencyIdMapping};
use primitives::{AccountId, CurrencyId, TokenSymbol};

#[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
#[derive(RuntimeDebug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u32)]
pub enum Action {
QueryBalance = 0,
Expand Down
Loading

0 comments on commit 5f2624f

Please sign in to comment.