Skip to content

Commit

Permalink
Merge pull request Magport#78 from toints/upstream-polkadot-v1.7.0
Browse files Browse the repository at this point in the history
perf(*): optimized evm contract bound with substrate precompile contract
  • Loading branch information
Acaishiba authored May 28, 2024
2 parents 03d19c1 + c2ad635 commit f3ca801
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 44 deletions.
57 changes: 53 additions & 4 deletions pallets/assets-bridge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,6 @@ pub mod pallet {
/// How much should be locked up in order to claim account.
#[pallet::constant]
type ClaimBond: Get<ReserveBalanceOf<Self>>;

/// The assets-bridge's evm contract deployer.
#[pallet::constant]
type EvmAdmins: Get<BTreeSet<H160>>;
}

/// The Substrate Account for Evm Addresses
Expand Down Expand Up @@ -263,6 +259,10 @@ pub mod pallet {
#[pallet::getter(fn emergencies)]
pub(super) type Emergencies<T: Config> = StorageValue<_, Vec<T::AssetId>, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn evm_contracts)]
pub type EvmContracts<T: Config> = StorageValue<_, BTreeSet<H160>, ValueQuery>;

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
Expand Down Expand Up @@ -315,6 +315,11 @@ pub mod pallet {
UnPausedAll,
// (asset_id, remove)
BackForeign(T::AssetId, bool),

///(new_evm_contract)
AddNewContract(H160),
///(evm contract)
RemoveContract(H160),
}

/// Error for evm accounts module.
Expand Down Expand Up @@ -630,6 +635,50 @@ pub mod pallet {

Ok(Pays::No.into())
}

/// Add evm token contracts which can call precompile
/// Note: for admin
///
/// - `new_contract`:
#[pallet::call_index(8)]
#[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))]
pub fn add_evm_contract(
origin: OriginFor<T>,
new_contract: H160,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
ensure!(Some(who) == Self::admin_key(), Error::<T>::RequireAdmin);

EvmContracts::<T>::mutate(|contracts| {
contracts.insert(new_contract);
});

Self::deposit_event(Event::AddNewContract(new_contract.clone()));

Ok(Pays::No.into())
}

/// Remove evm token contracts which can call precompile
/// Note: for admin
///
/// - `new_contract`:
#[pallet::call_index(9)]
#[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))]
pub fn remove_evm_contract(
origin: OriginFor<T>,
contract: H160,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
ensure!(Some(who) == Self::admin_key(), Error::<T>::RequireAdmin);

EvmContracts::<T>::mutate(|contracts| {
contracts.remove(&contract);
});

Self::deposit_event(Event::RemoveContract(contract));

Ok(Pays::No.into())
}
}
}

Expand Down
12 changes: 1 addition & 11 deletions pallets/assets-bridge/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

pub use crate as assets_bridge;
pub use assets_bridge::{Config, Error, Event as AssetsBridgeEvent};

use sp_std::collections::btree_set::BTreeSet;
pub use assets_bridge::{Error, Event as AssetsBridgeEvent};

use frame_support::{
derive_impl,
Expand All @@ -27,7 +25,6 @@ use frame_system::EnsureSigned;

use sp_core::{H160, H256};
pub use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
AccountId32, BuildStorage,
};
Expand Down Expand Up @@ -129,12 +126,6 @@ parameter_types! {
// 0x1111111111111111111111111111111111111111
pub EvmCaller: H160 = H160::from_slice(&[17u8;20][..]);
pub ClaimBond: u128 = 2;
//pub EvmAdmin: H160 = H160([0x05, 0xF9, 0xb8, 0xC7, 0x6E, 0x89, 0x87, 0xB8, 0x15, 0xC9, 0x3C, 0x27, 0xD1, 0x45, 0x20, 0xb6, 0xeD, 0x57, 0x39, 0x02]);
pub EvmAdmins: BTreeSet<H160> = {
let mut set = BTreeSet::new();
set.insert(H160([0x05, 0xF9, 0xb8, 0xC7, 0x6E, 0x89, 0x87, 0xB8, 0x15, 0xC9, 0x3C, 0x27, 0xD1, 0x45, 0x20, 0xb6, 0xeD, 0x57, 0x39, 0x02]));
set
};
pub const WeightPerGas: Weight = Weight::from_parts(20_000, 0);

pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE);
Expand Down Expand Up @@ -193,7 +184,6 @@ impl assets_bridge::Config for Test {
type RuntimeEvent = RuntimeEvent;
type EvmCaller = EvmCaller;
type ClaimBond = ClaimBond;
type EvmAdmins = EvmAdmins;
}

pub const ALICE: [u8; 32] = [1u8; 32];
Expand Down
25 changes: 12 additions & 13 deletions pallets/evm/precompile/transfer-to-magnet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,25 +86,24 @@ where
let target_gas = handle.gas_limit();
let context = handle.context();

log::info!(
log::debug!(
"codeAddress:{:?}, input:{:?}, targetGas:{:?}",
&code_address,
&input,
&target_gas
);
let caller = context.caller.clone();
if T::EvmAdmins::get().contains(&caller) == false {
log::error!("Caller is not the admin: {:?}", caller);
if pallet_assets_bridge::EvmContracts::<T>::get().contains(&caller) == false {
log::error!("Caller {:?} is not in the admin allow set.", caller);
//log::error!("EvmContracts:{:?}", pallet_assets_bridge::EvmContracts::<T>::get());
return Err(PrecompileFailure::Error {
exit_status: ExitError::Other("Caller is not the admin".into()),
exit_status: ExitError::Other("Caller is not in the admin allow set".into()),
});
}

let token_addr = solidity::decode_arguments::<Address>(&input[4..36])?;
let amount = solidity::decode_arguments::<u128>(&input[36..68])?;
log::info!("Caller:{:?}, tokenAddr:{:?}, amount:{:?}", &caller, &token_addr, &amount);

log::info!("who bstr data len:{:?}", &input[100..].len());
log::debug!("Caller:{:?}, tokenAddr:{:?}, amount:{:?}", &caller, &token_addr, &amount);

if handle.is_static() {
log::error!("Can't be static call error");
Expand All @@ -122,12 +121,12 @@ where
});
},
};
log::info!("to who ss58:{:?}", &to_who_ss58);
log::debug!("to who ss58:{:?}", &to_who_ss58);
let to_who_id32 =
AccountId32::from_ss58check(&to_who_ss58).map_err(|_| PrecompileFailure::Error {
exit_status: ExitError::Other("AccountId32 from ss58check(string) failed".into()),
})?;
log::info!("to_who_ss58:{:?}, to_who_id32:{:?}", &to_who_ss58, &to_who_id32);
log::debug!("to_who_ss58:{:?}, to_who_id32:{:?}", &to_who_ss58, &to_who_id32);

let to_who: <T>::AccountId = to_who_id32.clone().into();

Expand All @@ -152,7 +151,7 @@ where

let amount_saturated: T::Balance = amount.into();

log::info!(
log::debug!(
"Preparing to mint: AssetId: {:?}, Beneficiary: {:?}, Amount: {:?}",
&asset_id,
&to_who_ss58,
Expand Down Expand Up @@ -237,9 +236,9 @@ where
}

let length_bytes = &input[offset..offset + 32];
log::info!("length_bytes:{:?}", &length_bytes);
log::debug!("length_bytes:{:?}", &length_bytes);
let length = u32::from_be_bytes(length_bytes[28..32].try_into().unwrap()) as usize;
log::info!("ss58 string len:{:?}", length);
log::debug!("ss58 string len:{:?}", length);

if input.len() < offset + 32 + length {
return Err("Input too short to contain string data");
Expand All @@ -248,7 +247,7 @@ where
let string_data_start = offset + 32;
let string_data_end = string_data_start + length;
let string_data = &input[string_data_start..string_data_end];
log::info!("ss58 string data:{:?}", &string_data);
log::debug!("ss58 string data:{:?}", &string_data);

let mut result_string = from_utf8(string_data)
.map_err(|_| "String data is not valid UTF-8")?
Expand Down
7 changes: 0 additions & 7 deletions pallets/evm/precompile/transfer-to-magnet/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
BuildStorage,
};
use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
use std::str::FromStr;

use xcm::latest::prelude::BodyId;
Expand Down Expand Up @@ -137,18 +136,12 @@ parameter_types! {
// 0x1111111111111111111111111111111111111111
pub EvmCaller: H160 = H160::from_slice(&[17u8;20][..]);
pub ClaimBond: u64 = 10_000_000_000_000_000;
pub EvmAdmins: BTreeSet<H160> = {
let mut set = BTreeSet::new();
set.insert(H160([0x05, 0xF9, 0xb8, 0xC7, 0x6E, 0x89, 0x87, 0xB8, 0x15, 0xC9, 0x3C, 0x27, 0xD1, 0x45, 0x20, 0xb6, 0xeD, 0x57, 0x39, 0x02]));
set
};
}

impl pallet_assets_bridge::Config for Test {
type RuntimeEvent = RuntimeEvent;
type EvmCaller = EvmCaller;
type ClaimBond = ClaimBond;
type EvmAdmins = EvmAdmins;
}

pub const UNIT: u64 = 1_000_000_000_000_000_000;
Expand Down
68 changes: 67 additions & 1 deletion pallets/evm/precompile/transfer-to-magnet/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use super::*;
use crate::mock::*;
use frame_support::assert_ok;
use frame_support::traits::Currency;
use frame_system::RawOrigin;
use pallet_evm::{AddressMapping, CallInfo, Error, ExitError, ExitReason, Runner};

use codec::Encode;
Expand Down Expand Up @@ -280,6 +281,19 @@ fn transfer_to_substrate_works() {
let asset_id = create_and_register_asset(token_addr);
log::info!("asset id:{:?}", asset_id);

let admin_key = AccountId32::from([1u8; 32]);
let root_origin: frame_system::Origin<Test> = frame_system::RawOrigin::Root;
let _ =
pallet_assets_bridge::Pallet::<Test>::set_admin(root_origin.into(), admin_key.clone());
let r = pallet_assets_bridge::Pallet::<Test>::add_evm_contract(
RawOrigin::Signed(admin_key.clone()).into(),
bob_evm,
);
log::info!("add evm contract result:{:?}", r);

let callers = pallet_assets_bridge::EvmContracts::<Test>::get();
log::info!("callers:{:?}", callers);

let alice_token_amount_before = Assets::balance(asset_id, &ALICE);
log::info!("alice token amount before mint:{:?}", alice_token_amount_before);

Expand Down Expand Up @@ -390,6 +404,19 @@ fn gas_not_enough_error_works() {
let token_addr = deploy_contract(bob_evm);
log::info!("token addr:{:?}", token_addr);

let admin_key = AccountId32::from([1u8; 32]);
let root_origin: frame_system::Origin<Test> = frame_system::RawOrigin::Root;
let _ =
pallet_assets_bridge::Pallet::<Test>::set_admin(root_origin.into(), admin_key.clone());
let r = pallet_assets_bridge::Pallet::<Test>::add_evm_contract(
RawOrigin::Signed(admin_key.clone()).into(),
bob_evm,
);
log::info!("add evm contract result:{:?}", r);

let callers = pallet_assets_bridge::EvmContracts::<Test>::get();
log::info!("callers:{:?}", callers);

let asset_id = create_and_register_asset(token_addr);
log::info!("asset id:{:?}", asset_id);

Expand Down Expand Up @@ -491,6 +518,19 @@ fn gas_price_too_low_error_works() {
let token_addr = deploy_contract(bob_evm);
log::info!("token addr:{:?}", token_addr);

let admin_key = AccountId32::from([1u8; 32]);
let root_origin: frame_system::Origin<Test> = frame_system::RawOrigin::Root;
let _ =
pallet_assets_bridge::Pallet::<Test>::set_admin(root_origin.into(), admin_key.clone());
let r = pallet_assets_bridge::Pallet::<Test>::add_evm_contract(
RawOrigin::Signed(admin_key.clone()).into(),
bob_evm,
);
log::info!("add evm contract result:{:?}", r);

let callers = pallet_assets_bridge::EvmContracts::<Test>::get();
log::info!("callers:{:?}", callers);

let asset_id = create_and_register_asset(token_addr);
log::info!("asset id:{:?}", asset_id);

Expand Down Expand Up @@ -745,6 +785,19 @@ fn ss58address_error_works() {
let token_addr = deploy_contract(bob_evm);
log::info!("token addr:{:?}", token_addr);

let admin_key = AccountId32::from([1u8; 32]);
let root_origin: frame_system::Origin<Test> = frame_system::RawOrigin::Root;
let _ =
pallet_assets_bridge::Pallet::<Test>::set_admin(root_origin.into(), admin_key.clone());
let r = pallet_assets_bridge::Pallet::<Test>::add_evm_contract(
RawOrigin::Signed(admin_key.clone()).into(),
bob_evm,
);
log::info!("add evm contract result:{:?}", r);

let callers = pallet_assets_bridge::EvmContracts::<Test>::get();
log::info!("callers:{:?}", callers);

let asset_id = create_and_register_asset(token_addr);
log::info!("asset id:{:?}", asset_id);

Expand Down Expand Up @@ -929,7 +982,7 @@ fn not_evm_admin_works() {
assert_eq!(
reason,
ExitReason::Error(ExitError::Other(std::borrow::Cow::Borrowed(
"Caller is not the admin"
"Caller is not in the admin allow set"
)))
);
},
Expand Down Expand Up @@ -967,6 +1020,19 @@ fn token_and_assets_not_bound_works() {
let token_addr = deploy_contract(bob_evm);
log::info!("token addr:{:?}", token_addr);

let admin_key = AccountId32::from([1u8; 32]);
let root_origin: frame_system::Origin<Test> = frame_system::RawOrigin::Root;
let _ =
pallet_assets_bridge::Pallet::<Test>::set_admin(root_origin.into(), admin_key.clone());
let r = pallet_assets_bridge::Pallet::<Test>::add_evm_contract(
RawOrigin::Signed(admin_key.clone()).into(),
bob_evm,
);
log::info!("add evm contract result:{:?}", r);

let callers = pallet_assets_bridge::EvmContracts::<Test>::get();
log::info!("callers:{:?}", callers);

let asset_id = create_without_register_asset();
log::info!("asset id:{:?}", asset_id);

Expand Down
7 changes: 0 additions & 7 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -901,18 +901,11 @@ parameter_types! {
// 0x1111111111111111111111111111111111111111
pub EvmCaller: H160 = H160::from_slice(&[17u8;20][..]);
pub ClaimBond: Balance = 10 * EXISTENTIAL_DEPOSIT;
//pub EvmAdmin: H160 = H160([0x05, 0xF9, 0xb8, 0xC7, 0x6E, 0x89, 0x87, 0xB8, 0x15, 0xC9, 0x3C, 0x27, 0xD1, 0x45, 0x20, 0xb6, 0xeD, 0x57, 0x39, 0x02]);
pub EvmAdmins: BTreeSet<H160> = {
let mut set = BTreeSet::new();
set.insert(H160([0x05, 0xF9, 0xb8, 0xC7, 0x6E, 0x89, 0x87, 0xB8, 0x15, 0xC9, 0x3C, 0x27, 0xD1, 0x45, 0x20, 0xb6, 0xeD, 0x57, 0x39, 0x02]));
set
};
}
impl pallet_assets_bridge::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type EvmCaller = EvmCaller;
type ClaimBond = ClaimBond;
type EvmAdmins = EvmAdmins;
}

impl pallet_evm_utils::Config for Runtime {
Expand Down
18 changes: 17 additions & 1 deletion runtime/src/precompiles.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use pallet_evm::{
IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult, PrecompileSet,
ExitRevert, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle,
PrecompileResult, PrecompileSet,
};
use sp_core::{crypto::AccountId32, H160, U256};
use sp_runtime::traits::UniqueSaturatedInto;
Expand Down Expand Up @@ -45,6 +46,21 @@ where
U256: UniqueSaturatedInto<pallet_evm::BalanceOf<R>>,
{
fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
let remaining_gas = handle.remaining_gas();

let is_precompile_result = self.is_precompile(handle.code_address(), remaining_gas);
if let IsPrecompileResult::Answer { is_precompile, .. } = is_precompile_result {
if is_precompile
&& handle.code_address() > hash(5)
&& handle.code_address() != handle.context().address
{
return Some(Err(PrecompileFailure::Revert {
exit_status: ExitRevert::Reverted,
output: "cannot be called with DELEGATECALL or CALLCODE".into(),
}));
}
}

match handle.code_address() {
// Ethereum precompiles :
a if a == hash(1) => Some(ECRecover::execute(handle)),
Expand Down

0 comments on commit f3ca801

Please sign in to comment.