Skip to content

Commit

Permalink
Automatic EVM revert code registration for XC20 (#1001)
Browse files Browse the repository at this point in the history
* Automatic EVM revert code registration for XC20

* Add negative test

* Fmt fix

* Add EVM module

* Asset benchmarks

* Cleanup xc asset config

* Update asset benchmarks

* Fix tests features
  • Loading branch information
Dinonard authored Aug 11, 2023
1 parent c0d3999 commit 4a2c3af
Show file tree
Hide file tree
Showing 34 changed files with 2,323 additions and 290 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

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

21 changes: 0 additions & 21 deletions pallets/xc-asset-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,21 +85,6 @@ pub mod pallet {
#[pallet::without_storage_info]
pub struct Pallet<T>(PhantomData<T>);

/// Callback definition trait for cross-chain asset registration/deregistration notifications.
pub trait XcAssetChanged<T: Config> {
/// Will be called by pallet when new asset Id has been registered
fn xc_asset_registered(asset_id: T::AssetId);

/// Will be called by pallet when asset Id has been unregistered
fn xc_asset_unregistered(asset_id: T::AssetId);
}

/// Implementation that does nothing
impl<T: Config> XcAssetChanged<T> for () {
fn xc_asset_registered(_: T::AssetId) {}
fn xc_asset_unregistered(_: T::AssetId) {}
}

/// Defines conversion between asset Id and cross-chain asset location
pub trait XcAssetLocation<AssetId> {
/// Get asset type from assetId
Expand Down Expand Up @@ -139,9 +124,6 @@ pub mod pallet {
/// a AssetLocation
type AssetId: Member + Parameter + Default + Copy + HasCompact + MaxEncodedLen;

/// Callback handling for cross-chain asset registration or unregistration.
type XcAssetChanged: XcAssetChanged<Self>;

/// The required origin for managing cross-chain asset configuration
///
/// Should most likely be root.
Expand Down Expand Up @@ -242,8 +224,6 @@ pub mod pallet {
AssetIdToLocation::<T>::insert(&asset_id, asset_location.clone());
AssetLocationToId::<T>::insert(&asset_location, asset_id);

T::XcAssetChanged::xc_asset_registered(asset_id);

Self::deposit_event(Event::AssetRegistered {
asset_location,
asset_id,
Expand Down Expand Up @@ -354,7 +334,6 @@ pub mod pallet {
AssetIdToLocation::<T>::remove(&asset_id);
AssetLocationToId::<T>::remove(&asset_location);
AssetLocationUnitsPerSecond::<T>::remove(&asset_location);
T::XcAssetChanged::xc_asset_unregistered(asset_id);

Self::deposit_event(Event::AssetRemoved {
asset_id,
Expand Down
1 change: 0 additions & 1 deletion pallets/xc-asset-config/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ type AssetId = u128;
impl pallet_xc_asset_config::Config for Test {
type RuntimeEvent = RuntimeEvent;
type AssetId = AssetId;
type XcAssetChanged = ();
type ManagerOrigin = frame_system::EnsureRoot<AccountId>;
type WeightInfo = ();
}
Expand Down
1 change: 1 addition & 0 deletions precompiles/assets-erc20/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ std = [
"sp-runtime/std",
"sp-std/std",
]
runtime-benchmarks = []
2 changes: 2 additions & 0 deletions precompiles/assets-erc20/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ impl pallet_assets::Config for Runtime {
type RemoveItemsLimit = ConstU32<0>;
type AssetIdParameter = AssetId;
type CallbackHandle = ();
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}

// Configure a mock runtime to test the pallet.
Expand Down
2 changes: 2 additions & 0 deletions precompiles/xcm/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ impl pallet_assets::Config for Runtime {
type RemoveItemsLimit = ConstU32<0>;
type AssetIdParameter = AssetId;
type CallbackHandle = ();
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}

pub struct AssetIdConverter<AssetId>(PhantomData<AssetId>);
Expand Down
12 changes: 10 additions & 2 deletions primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ fp-evm = { workspace = true }

# Substrate dependencies
frame-support = { workspace = true }
pallet-assets = { workspace = true }
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }
Expand All @@ -30,7 +31,11 @@ xcm = { workspace = true }
xcm-builder = { workspace = true }
xcm-executor = { workspace = true }

# Astar pallets
# Frontier dependencies
pallet-evm = { workspace = true }

# Astar pallets & dependencies
pallet-evm-precompile-assets-erc20 = { workspace = true }
pallet-xc-asset-config = { workspace = true }

[features]
Expand All @@ -52,5 +57,8 @@ std = [
"xcm-executor/std",
"pallet-xc-asset-config/std",
"fp-evm/std",
"pallet-assets/std",
"pallet-evm/std",
"pallet-evm-precompile-assets-erc20/std",
]
runtime-benchmarks = ["xcm-builder/runtime-benchmarks"]
runtime-benchmarks = ["xcm-builder/runtime-benchmarks", "pallet-assets/runtime-benchmarks"]
30 changes: 30 additions & 0 deletions primitives/src/benchmarks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// This file is part of Astar.

// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later

// Astar 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.

// Astar 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 Astar. If not, see <http://www.gnu.org/licenses/>.

use crate::AssetId;

#[cfg(feature = "runtime-benchmarks")]
/// Benchmark helper for `pallet-assets`.
pub struct AssetsBenchmarkHelper;
impl<AssetIdParameter: From<u128>> pallet_assets::BenchmarkHelper<AssetIdParameter>
for AssetsBenchmarkHelper
{
fn create_asset_id_parameter(id: u32) -> AssetIdParameter {
AssetId::from(id).into()
}
}
56 changes: 56 additions & 0 deletions primitives/src/evm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// This file is part of Astar.

// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later

// Astar 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.

// Astar 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 Astar. If not, see <http://www.gnu.org/licenses/>.

use crate::{AccountId, AssetId};

use frame_support::ensure;
use sp_std::marker::PhantomData;

use pallet_assets::AssetsCallback;
use pallet_evm_precompile_assets_erc20::AddressToAssetId;

/// Revert opt code. It's inserted at the precompile addresses, to make them functional in EVM.
pub const EVM_REVERT_CODE: &[u8] = &[0x60, 0x00, 0x60, 0x00, 0xfd];

/// Handler for automatic revert code registration.
///
/// When an asset is created, it automatically becomes available to the EVM via an `ERC20-like` interface.
/// In order for the precompile to work, dedicated asset address needs to have the revert code registered, otherwise the call will fail.
///
/// It is important to note that if the dedicated asset EVM address is already taken, asset creation should fail.
/// After asset has been destroyed, it is also safe to remove the revert code and free the address for future usage.
pub struct EvmRevertCodeHandler<A, R>(PhantomData<(A, R)>);
impl<A, R> AssetsCallback<AssetId, AccountId> for EvmRevertCodeHandler<A, R>
where
A: AddressToAssetId<AssetId>,
R: pallet_evm::Config,
{
fn created(id: &AssetId, _: &AccountId) -> Result<(), ()> {
let address = A::asset_id_to_address(*id);
// In case of collision, we need to cancel the asset creation.
ensure!(!pallet_evm::AccountCodes::<R>::contains_key(&address), ());
pallet_evm::AccountCodes::<R>::insert(address, EVM_REVERT_CODE.to_vec());
Ok(())
}

fn destroyed(id: &AssetId) -> Result<(), ()> {
let address = A::asset_id_to_address(*id);
pallet_evm::AccountCodes::<R>::remove(address);
Ok(())
}
}
10 changes: 8 additions & 2 deletions primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,16 @@ pub mod ethereum_checked;
/// XVM primitives.
pub mod xvm;

use sp_runtime::traits::BlakeTwo256;
/// EVM primitives.
pub mod evm;

/// Benchmark primitives
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarks;

use sp_runtime::{
generic,
traits::{IdentifyAccount, Verify},
traits::{BlakeTwo256, IdentifyAccount, Verify},
};

/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
Expand Down
51 changes: 2 additions & 49 deletions primitives/src/xcm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@
#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{
ensure,
traits::{tokens::fungibles, Contains, ContainsPair, Get, ProcessMessageError},
traits::{tokens::fungibles, ContainsPair, Get},
weights::constants::WEIGHT_REF_TIME_PER_SECOND,
};
use sp_runtime::traits::{Bounded, Zero};
Expand All @@ -43,7 +42,7 @@ use sp_std::{borrow::Borrow, marker::PhantomData, vec::Vec};
// Polkadot imports
use xcm::latest::{prelude::*, Weight};
use xcm_builder::TakeRevenue;
use xcm_executor::traits::{MatchesFungibles, ShouldExecute, WeightTrader};
use xcm_executor::traits::{MatchesFungibles, WeightTrader};

use pallet_xc_asset_config::{ExecutionPaymentRate, XcAssetLocation};

Expand Down Expand Up @@ -271,52 +270,6 @@ impl<
}
}

/// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking
/// payments into account.
///
/// Only allows for sequence `DescendOrigin` -> `WithdrawAsset` -> `BuyExecution`
pub struct AllowPaidExecWithDescendOriginFrom<T>(PhantomData<T>);
impl<T: Contains<MultiLocation>> ShouldExecute for AllowPaidExecWithDescendOriginFrom<T> {
fn should_execute<RuntimeCall>(
origin: &MultiLocation,
message: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ProcessMessageError> {
log::trace!(
target: "xcm::barriers",
"AllowPaidExecWithDescendOriginFrom origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, message, max_weight, _weight_credit,
);
ensure!(T::contains(origin), ProcessMessageError::Unsupported);

match message
.iter_mut()
.take(3)
.collect::<Vec<_>>()
.as_mut_slice()
{
[DescendOrigin(..), WithdrawAsset(..), BuyExecution {
weight_limit: Limited(ref mut limit),
..
}] if limit.all_gte(max_weight) => {
*limit = max_weight;
Ok(())
}

[DescendOrigin(..), WithdrawAsset(..), BuyExecution {
weight_limit: ref mut limit @ Unlimited,
..
}] => {
*limit = Limited(max_weight);
Ok(())
}

_ => return Err(ProcessMessageError::Unsupported),
}
}
}

// TODO: remove this after uplift to `polkadot-v0.9.44` or beyond, and replace it with code in XCM builder.

use parity_scale_codec::{Compact, Encode};
Expand Down
Loading

0 comments on commit 4a2c3af

Please sign in to comment.