From 20960882017968c5891a72f6e2fdf628f22e7b31 Mon Sep 17 00:00:00 2001 From: PierreOssun Date: Tue, 15 Aug 2023 14:26:25 +0200 Subject: [PATCH 01/11] Added call_runtime filter --- runtime/local/src/lib.rs | 17 ++++++++++++++--- runtime/shibuya/src/lib.rs | 18 ++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/runtime/local/src/lib.rs b/runtime/local/src/lib.rs index 765dc47b84..78f0059026 100644 --- a/runtime/local/src/lib.rs +++ b/runtime/local/src/lib.rs @@ -27,8 +27,8 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use frame_support::{ construct_runtime, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstU128, ConstU32, ConstU64, Currency, EitherOfDiverse, - EqualPrivilegeOnly, FindAuthor, Get, InstanceFilter, Nothing, OnFinalize, WithdrawReasons, + AsEnsureOriginWithArg, ConstU128, ConstU32, ConstU64, Contains, Currency, EitherOfDiverse, + EqualPrivilegeOnly, FindAuthor, Get, InstanceFilter, OnFinalize, WithdrawReasons, }, weights::{ constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, @@ -807,6 +807,17 @@ parameter_types! { pub Schedule: pallet_contracts::Schedule = Default::default(); } +pub struct CallRuntimeFilter; +impl Contains for CallRuntimeFilter { + fn contains(c: &RuntimeCall) -> bool { + match *c { + RuntimeCall::Assets(pallet_assets::Call::transfer { .. }) => true, + RuntimeCall::Assets(pallet_assets::Call::transfer_approved { .. }) => true, + _ => false, + } + } +} + impl pallet_contracts::Config for Runtime { type Time = Timestamp; type Randomness = RandomnessCollectiveFlip; @@ -819,7 +830,7 @@ impl pallet_contracts::Config for Runtime { /// and make sure they are stable. Dispatchables exposed to contracts are not allowed to /// change because that would break already deployed contracts. The `Call` structure itself /// is not allowed to change the indices of existing pallets, too. - type CallFilter = Nothing; + type CallFilter = CallRuntimeFilter; type DepositPerItem = DepositPerItem; type DepositPerByte = DepositPerByte; type DefaultDepositLimit = DefaultDepositLimit; diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index bc94244c62..86a121d52e 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -29,8 +29,7 @@ use frame_support::{ parameter_types, traits::{ AsEnsureOriginWithArg, ConstU32, Contains, Currency, EitherOfDiverse, EqualPrivilegeOnly, - FindAuthor, Get, Imbalance, InstanceFilter, Nothing, OnFinalize, OnUnbalanced, - WithdrawReasons, + FindAuthor, Get, Imbalance, InstanceFilter, OnFinalize, OnUnbalanced, WithdrawReasons, }, weights::{ constants::{ @@ -662,6 +661,17 @@ parameter_types! { pub Schedule: pallet_contracts::Schedule = Default::default(); } +pub struct CallRuntimeFilter; +impl Contains for CallRuntimeFilter { + fn contains(c: &RuntimeCall) -> bool { + match *c { + RuntimeCall::Assets(pallet_assets::Call::transfer { .. }) => true, + RuntimeCall::Assets(pallet_assets::Call::transfer_approved { .. }) => true, + _ => false, + } + } +} + impl pallet_contracts::Config for Runtime { type Time = Timestamp; type Randomness = RandomnessCollectiveFlip; @@ -674,7 +684,7 @@ impl pallet_contracts::Config for Runtime { /// and make sure they are stable. Dispatchables exposed to contracts are not allowed to /// change because that would break already deployed contracts. The `Call` structure itself /// is not allowed to change the indices of existing pallets, too. - type CallFilter = Nothing; + type CallFilter = CallRuntimeFilter; type DepositPerItem = DepositPerItem; type DepositPerByte = DepositPerByte; type DefaultDepositLimit = DefaultDepositLimit; @@ -690,7 +700,7 @@ impl pallet_contracts::Config for Runtime { type AddressGenerator = pallet_contracts::DefaultAddressGenerator; type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; - type UnsafeUnstableInterface = ConstBool; + type UnsafeUnstableInterface = ConstBool; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; } From 57e5ac74974c4e8e73b8ef09139c0b9c8793fe03 Mon Sep 17 00:00:00 2001 From: PierreOssun Date: Tue, 15 Aug 2023 14:45:59 +0200 Subject: [PATCH 02/11] Added call_runtime filter --- runtime/local/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/local/src/lib.rs b/runtime/local/src/lib.rs index 78f0059026..083839ba66 100644 --- a/runtime/local/src/lib.rs +++ b/runtime/local/src/lib.rs @@ -36,6 +36,7 @@ use frame_support::{ }, ConsensusEngineId, PalletId, }; +use frame_support::traits::Nothing; use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, EnsureSigned, @@ -830,7 +831,7 @@ impl pallet_contracts::Config for Runtime { /// and make sure they are stable. Dispatchables exposed to contracts are not allowed to /// change because that would break already deployed contracts. The `Call` structure itself /// is not allowed to change the indices of existing pallets, too. - type CallFilter = CallRuntimeFilter; + type CallFilter = Nothing; type DepositPerItem = DepositPerItem; type DepositPerByte = DepositPerByte; type DefaultDepositLimit = DefaultDepositLimit; From 1fdbf783d8950e92d387c86f5382b57a6515dcbe Mon Sep 17 00:00:00 2001 From: PierreOssun Date: Thu, 13 Jul 2023 15:06:00 +0200 Subject: [PATCH 03/11] call_runtime filter to Shibuya --- runtime/shibuya/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index 86a121d52e..cfacb68e70 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -29,7 +29,7 @@ use frame_support::{ parameter_types, traits::{ AsEnsureOriginWithArg, ConstU32, Contains, Currency, EitherOfDiverse, EqualPrivilegeOnly, - FindAuthor, Get, Imbalance, InstanceFilter, OnFinalize, OnUnbalanced, WithdrawReasons, + FindAuthor, Get, Imbalance, InstanceFilter, OnUnbalanced, WithdrawReasons, }, weights::{ constants::{ From 050a272526f9221b29f23b54070f61582ef60e13 Mon Sep 17 00:00:00 2001 From: Shaun Wang Date: Mon, 7 Aug 2023 10:57:36 +1200 Subject: [PATCH 04/11] Cross-VM payable calls & XVM tests (#988) * Add value param to 'XvmCall'. * Fix precompile tests. * Update types in CE & precompiles. * Add EVM call tests for XVM. * New type idiom for EthereumTxInput. * Add XVM integration tests. * More XVM integration tests. * Fix runtime tests. * Move contract deploy helpers to setup.rs. * Update value adjustment for wasm payable calls. * More pallet-xvm unit tests. * Update pallet-xvm weight info. * Apply review suggestions. * Fix runtime tests. * Calling WASM payable from EVM fails if caller contract balance below ED. * Update weight info. * Fix unit tests. --- Cargo.lock | 8 + chain-extensions/types/xvm/src/lib.rs | 7 +- chain-extensions/xvm/src/lib.rs | 36 +- pallets/ethereum-checked/src/benchmarking.rs | 4 +- pallets/ethereum-checked/src/mock.rs | 2 +- pallets/ethereum-checked/src/tests.rs | 10 +- pallets/xvm/Cargo.toml | 1 + pallets/xvm/src/benchmarking.rs | 14 +- pallets/xvm/src/lib.rs | 67 ++- pallets/xvm/src/mock.rs | 23 +- pallets/xvm/src/tests.rs | 202 ++++++++ pallets/xvm/src/weights.rs | 18 +- precompiles/xvm/evm_sdk/XVM.sol | 12 +- precompiles/xvm/evm_sdk/flipper.sol | 7 +- precompiles/xvm/src/lib.rs | 15 +- precompiles/xvm/src/mock.rs | 1 + precompiles/xvm/src/tests.rs | 3 + primitives/src/ethereum_checked.rs | 4 +- primitives/src/xvm.rs | 6 +- tests/integration/Cargo.toml | 13 +- tests/integration/ink-contracts/README.md | 8 + .../ink-contracts/call_xvm_payable.json | 438 ++++++++++++++++++ .../ink-contracts/call_xvm_payable.wasm | Bin 0 -> 13469 bytes tests/integration/ink-contracts/payable.json | 287 ++++++++++++ tests/integration/ink-contracts/payable.wasm | Bin 0 -> 11235 bytes tests/integration/src/lib.rs | 3 + tests/integration/src/setup.rs | 82 +++- tests/integration/src/xvm.rs | 334 +++++++++++++ 28 files changed, 1514 insertions(+), 91 deletions(-) create mode 100644 pallets/xvm/src/tests.rs create mode 100644 tests/integration/ink-contracts/README.md create mode 100644 tests/integration/ink-contracts/call_xvm_payable.json create mode 100644 tests/integration/ink-contracts/call_xvm_payable.wasm create mode 100644 tests/integration/ink-contracts/payable.json create mode 100644 tests/integration/ink-contracts/payable.wasm create mode 100644 tests/integration/src/xvm.rs diff --git a/Cargo.lock b/Cargo.lock index 155d1231eb..03bb4473f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4456,13 +4456,20 @@ dependencies = [ name = "integration-tests" version = "0.1.0" dependencies = [ + "astar-primitives", "astar-runtime", "frame-support", "frame-system", + "hex", "pallet-balances", + "pallet-contracts", + "pallet-contracts-primitives", "pallet-dapps-staking", + "pallet-ethereum-checked", + "pallet-evm", "pallet-proxy", "pallet-utility", + "parity-scale-codec", "shibuya-runtime", "shiden-runtime", "sp-core", @@ -8238,6 +8245,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "hex", "log", "pallet-balances", "pallet-contracts", diff --git a/chain-extensions/types/xvm/src/lib.rs b/chain-extensions/types/xvm/src/lib.rs index d23ccd9102..c026fc52dc 100644 --- a/chain-extensions/types/xvm/src/lib.rs +++ b/chain-extensions/types/xvm/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use astar_primitives::xvm::CallError; +use astar_primitives::{xvm::CallError, Balance}; use parity_scale_codec::{Decode, Encode}; use sp_std::vec::Vec; @@ -41,8 +41,7 @@ impl From for XvmExecutionResult { SameVmCallNotAllowed => 2, InvalidTarget => 3, InputTooLarge => 4, - BadOrigin => 5, - ExecutionFailed(_) => 6, + ExecutionFailed(_) => 5, }; Self::Err(error_code) } @@ -65,4 +64,6 @@ pub struct XvmCallArgs { pub to: Vec, /// Encoded call params pub input: Vec, + /// Value to transfer + pub value: Balance, } diff --git a/chain-extensions/xvm/src/lib.rs b/chain-extensions/xvm/src/lib.rs index 9696fc6d7a..8b6c48b8f6 100644 --- a/chain-extensions/xvm/src/lib.rs +++ b/chain-extensions/xvm/src/lib.rs @@ -18,19 +18,15 @@ #![cfg_attr(not(feature = "std"), no_std)] -use astar_primitives::xvm::{CallError, Context, VmId, XvmCall}; +use astar_primitives::xvm::{Context, VmId, XvmCall}; use frame_support::dispatch::Encode; -use pallet_contracts::{ - chain_extension::{ChainExtension, Environment, Ext, InitState, RetVal}, - Origin, -}; +use pallet_contracts::chain_extension::{ChainExtension, Environment, Ext, InitState, RetVal}; use sp_runtime::DispatchError; use sp_std::marker::PhantomData; use xvm_chain_extension_types::{XvmCallArgs, XvmExecutionResult}; enum XvmFuncId { Call, - // TODO: expand with other calls too } impl TryFrom for XvmFuncId { @@ -76,28 +72,22 @@ where // So we will charge a 32KB dummy value as a temporary replacement. let charged_weight = env.charge_weight(weight_limit.set_proof_size(32 * 1024))?; - let caller = match env.ext().caller().clone() { - Origin::Signed(address) => address, - Origin::Root => { - log::trace!( - target: "xvm-extension::xvm_call", - "root origin not supported" - ); - return Ok(RetVal::Converging( - XvmExecutionResult::from(CallError::BadOrigin).into(), - )); - } - }; + let XvmCallArgs { + vm_id, + to, + input, + value, + } = env.read_as_unbounded(env.in_len())?; - let XvmCallArgs { vm_id, to, input } = env.read_as_unbounded(env.in_len())?; + // Similar to EVM behavior, the `source` should be (limited to) the + // contract address. Otherwise contracts would be able to do arbitrary + // things on behalf of the caller via XVM. + let source = env.ext().address(); - let _origin_address = env.ext().address().clone(); - let _value = env.ext().value_transferred(); let xvm_context = Context { source_vm_id: VmId::Wasm, weight_limit, }; - let vm_id = { match TryInto::::try_into(vm_id) { Ok(id) => id, @@ -108,7 +98,7 @@ where } } }; - let call_result = XC::call(xvm_context, vm_id, caller, to, input); + let call_result = XC::call(xvm_context, vm_id, source.clone(), to, input, value); let actual_weight = match call_result { Ok(ref info) => info.used_weight, diff --git a/pallets/ethereum-checked/src/benchmarking.rs b/pallets/ethereum-checked/src/benchmarking.rs index b45458afe6..6abf9f8075 100644 --- a/pallets/ethereum-checked/src/benchmarking.rs +++ b/pallets/ethereum-checked/src/benchmarking.rs @@ -18,7 +18,7 @@ use super::*; -use astar_primitives::ethereum_checked::MAX_ETHEREUM_TX_INPUT_SIZE; +use astar_primitives::ethereum_checked::EthereumTxInput; use frame_benchmarking::v2::*; #[benchmarks] @@ -31,7 +31,7 @@ mod benchmarks { let target = H160::from_slice(&hex::decode("dfb975d018f03994a3b943808e3aa0964bd78463").unwrap()); // Calling `store(3)` - let input = BoundedVec::>::try_from( + let input = EthereumTxInput::try_from( hex::decode("6057361d0000000000000000000000000000000000000000000000000000000000000003") .unwrap(), ) diff --git a/pallets/ethereum-checked/src/mock.rs b/pallets/ethereum-checked/src/mock.rs index b63a34c23d..f4575e956d 100644 --- a/pallets/ethereum-checked/src/mock.rs +++ b/pallets/ethereum-checked/src/mock.rs @@ -294,7 +294,7 @@ impl ExtBuilder { assert_ok!(Evm::create2( RuntimeOrigin::root(), ALICE_H160, - hex::decode(STORAGE_CONTRACT).unwrap(), + hex::decode(STORAGE_CONTRACT).expect("invalid code hex"), H256::zero(), U256::zero(), 1_000_000, diff --git a/pallets/ethereum-checked/src/tests.rs b/pallets/ethereum-checked/src/tests.rs index 7d77601fd9..e5c9b8f41c 100644 --- a/pallets/ethereum-checked/src/tests.rs +++ b/pallets/ethereum-checked/src/tests.rs @@ -21,14 +21,14 @@ use super::*; use mock::*; -use astar_primitives::ethereum_checked::MAX_ETHEREUM_TX_INPUT_SIZE; +use astar_primitives::ethereum_checked::EthereumTxInput; use ethereum::{ReceiptV3, TransactionV2 as Transaction}; -use frame_support::{assert_noop, assert_ok, traits::ConstU32}; +use frame_support::{assert_noop, assert_ok}; use sp_runtime::DispatchError; -fn bounded_input(data: &'static str) -> BoundedVec> { - BoundedVec::>::try_from(hex::decode(data).unwrap()) - .unwrap() +fn bounded_input(data: &'static str) -> EthereumTxInput { + EthereumTxInput::try_from(hex::decode(data).expect("invalid input hex")) + .expect("input too large") } #[test] diff --git a/pallets/xvm/Cargo.toml b/pallets/xvm/Cargo.toml index 6f1022bf24..6b83517d55 100644 --- a/pallets/xvm/Cargo.toml +++ b/pallets/xvm/Cargo.toml @@ -33,6 +33,7 @@ astar-primitives = { workspace = true } [dev-dependencies] fp-evm = { workspace = true } +hex = { workspace = true } pallet-balances = { workspace = true, features = ["std"] } pallet-insecure-randomness-collective-flip = { workspace = true, features = ["std"] } pallet-timestamp = { workspace = true, features = ["std"] } diff --git a/pallets/xvm/src/benchmarking.rs b/pallets/xvm/src/benchmarking.rs index 97edd7d0d5..38c41a1d76 100644 --- a/pallets/xvm/src/benchmarking.rs +++ b/pallets/xvm/src/benchmarking.rs @@ -24,7 +24,11 @@ use parity_scale_codec::Encode; use sp_core::H160; use sp_runtime::MultiAddress; -#[benchmarks] +use astar_primitives::Balance; + +#[benchmarks( + where ::Currency: Currency, +)] mod benchmarks { use super::*; @@ -38,10 +42,12 @@ mod benchmarks { let source = whitelisted_caller(); let target = H160::repeat_byte(1).encode(); let input = vec![1, 2, 3]; + let value = 1_000_000u128; #[block] { - Pallet::::call_without_execution(context, vm_id, source, target, input).unwrap(); + Pallet::::call_without_execution(context, vm_id, source, target, input, value) + .unwrap(); } } @@ -55,10 +61,12 @@ mod benchmarks { let source = whitelisted_caller(); let target = MultiAddress::::Id(whitelisted_caller()).encode(); let input = vec![1, 2, 3]; + let value = 1_000_000u128; #[block] { - Pallet::::call_without_execution(context, vm_id, source, target, input).unwrap(); + Pallet::::call_without_execution(context, vm_id, source, target, input, value) + .unwrap(); } } diff --git a/pallets/xvm/src/lib.rs b/pallets/xvm/src/lib.rs index 5da16856fc..d10d141520 100644 --- a/pallets/xvm/src/lib.rs +++ b/pallets/xvm/src/lib.rs @@ -37,19 +37,20 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{ensure, traits::ConstU32, BoundedVec}; +use frame_support::{ensure, traits::Currency}; use pallet_contracts::{CollectEvents, DebugInfo, Determinism}; use pallet_evm::GasWeightMapping; use parity_scale_codec::Decode; -use sp_core::U256; +use sp_core::{H160, U256}; use sp_runtime::traits::StaticLookup; use sp_std::{marker::PhantomData, prelude::*}; use astar_primitives::{ ethereum_checked::{ - AccountMapping, CheckedEthereumTransact, CheckedEthereumTx, MAX_ETHEREUM_TX_INPUT_SIZE, + AccountMapping, CheckedEthereumTransact, CheckedEthereumTx, EthereumTxInput, }, xvm::{CallError, CallErrorWithWeight, CallInfo, CallResult, Context, VmId, XvmCall}, + Balance, }; #[cfg(feature = "runtime-benchmarks")] @@ -58,8 +59,8 @@ mod benchmarking; pub mod weights; pub use weights::WeightInfo; -#[cfg(test)] mod mock; +mod tests; pub use pallet::*; @@ -88,25 +89,35 @@ pub mod pallet { } } -impl XvmCall for Pallet { +impl XvmCall for Pallet +where + T: Config, + T::Currency: Currency, +{ fn call( context: Context, vm_id: VmId, source: T::AccountId, target: Vec, input: Vec, + value: Balance, ) -> CallResult { - Pallet::::do_call(context, vm_id, source, target, input, false) + Pallet::::do_call(context, vm_id, source, target, input, value, false) } } -impl Pallet { +impl Pallet +where + T: Config, + T::Currency: Currency, +{ fn do_call( context: Context, vm_id: VmId, source: T::AccountId, target: Vec, input: Vec, + value: Balance, skip_execution: bool, ) -> CallResult { ensure!( @@ -121,8 +132,12 @@ impl Pallet { ); match vm_id { - VmId::Evm => Pallet::::evm_call(context, source, target, input, skip_execution), - VmId::Wasm => Pallet::::wasm_call(context, source, target, input, skip_execution), + VmId::Evm => { + Pallet::::evm_call(context, source, target, input, value, skip_execution) + } + VmId::Wasm => { + Pallet::::wasm_call(context, source, target, input, value, skip_execution) + } } } @@ -131,26 +146,33 @@ impl Pallet { source: T::AccountId, target: Vec, input: Vec, + value: Balance, skip_execution: bool, ) -> CallResult { log::trace!( target: "xvm::evm_call", - "Calling EVM: {:?} {:?}, {:?}, {:?}", - context, source, target, input, + "Calling EVM: {:?} {:?}, {:?}, {:?}, {:?}", + context, source, target, input, value, ); + ensure!( + target.len() == H160::len_bytes(), + CallErrorWithWeight { + error: CallError::InvalidTarget, + used_weight: WeightInfoOf::::evm_call_overheads(), + } + ); let target_decoded = Decode::decode(&mut target.as_ref()).map_err(|_| CallErrorWithWeight { error: CallError::InvalidTarget, used_weight: WeightInfoOf::::evm_call_overheads(), })?; - let bounded_input = BoundedVec::>::try_from(input) - .map_err(|_| CallErrorWithWeight { - error: CallError::InputTooLarge, - used_weight: WeightInfoOf::::evm_call_overheads(), - })?; + let bounded_input = EthereumTxInput::try_from(input).map_err(|_| CallErrorWithWeight { + error: CallError::InputTooLarge, + used_weight: WeightInfoOf::::evm_call_overheads(), + })?; - let value = U256::zero(); + let value_u256 = U256::from(value); // With overheads, less weight is available. let weight_limit = context .weight_limit @@ -161,7 +183,7 @@ impl Pallet { let tx = CheckedEthereumTx { gas_limit, target: target_decoded, - value, + value: value_u256, input: bounded_input, maybe_access_list: None, }; @@ -210,12 +232,13 @@ impl Pallet { source: T::AccountId, target: Vec, input: Vec, + value: Balance, skip_execution: bool, ) -> CallResult { log::trace!( target: "xvm::wasm_call", - "Calling WASM: {:?} {:?}, {:?}, {:?}", - context, source, target, input, + "Calling WASM: {:?} {:?}, {:?}, {:?}, {:?}", + context, source, target, input, value, ); let dest = { @@ -231,7 +254,6 @@ impl Pallet { let weight_limit = context .weight_limit .saturating_sub(WeightInfoOf::::wasm_call_overheads()); - let value = Default::default(); // Note the skip execution check should be exactly before `pallet_contracts::bare_call` // to benchmark the correct overheads. @@ -277,7 +299,8 @@ impl Pallet { source: T::AccountId, target: Vec, input: Vec, + value: Balance, ) -> CallResult { - Self::do_call(context, vm_id, source, target, input, true) + Self::do_call(context, vm_id, source, target, input, value, true) } } diff --git a/pallets/xvm/src/mock.rs b/pallets/xvm/src/mock.rs index a02472c3b7..7f134b4c3c 100644 --- a/pallets/xvm/src/mock.rs +++ b/pallets/xvm/src/mock.rs @@ -36,6 +36,7 @@ use sp_runtime::{ traits::{AccountIdLookup, BlakeTwo256}, AccountId32, }; +use sp_std::cell::RefCell; parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = @@ -131,12 +132,23 @@ impl astar_primitives::ethereum_checked::AccountMapping for HashedAcc } } +thread_local! { + static TRANSACTED: RefCell> = RefCell::new(None); +} + pub struct MockEthereumTransact; +impl MockEthereumTransact { + pub(crate) fn assert_transacted(source: H160, checked_tx: CheckedEthereumTx) { + let transacted = TRANSACTED.with(|v| v.borrow().clone()); + assert_eq!(transacted, Some((source, checked_tx))); + } +} impl CheckedEthereumTransact for MockEthereumTransact { fn xvm_transact( - _source: H160, - _checked_tx: CheckedEthereumTx, + source: H160, + checked_tx: CheckedEthereumTx, ) -> Result<(PostDispatchInfo, EvmCallInfo), DispatchErrorWithPostInfo> { + TRANSACTED.with(|v| *v.borrow_mut() = Some((source, checked_tx))); Ok(( PostDispatchInfo { actual_weight: Default::default(), @@ -170,12 +182,11 @@ impl pallet_xvm::Config for TestRuntime { type GasWeightMapping = MockGasWeightMapping; type AccountMapping = HashedAccountMapping; type EthereumTransact = MockEthereumTransact; - type WeightInfo = (); + type WeightInfo = weights::SubstrateWeight; } pub(crate) type AccountId = AccountId32; pub(crate) type BlockNumber = u64; -pub(crate) type Balance = u128; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -196,12 +207,16 @@ construct_runtime!( } ); +pub(crate) const ALICE: AccountId = AccountId32::new([0u8; 32]); + #[derive(Default)] pub struct ExtBuilder; impl ExtBuilder { #[allow(dead_code)] pub fn build(self) -> TestExternalities { + TRANSACTED.with(|v| *v.borrow_mut() = None); + let t = frame_system::GenesisConfig::default() .build_storage::() .unwrap(); diff --git a/pallets/xvm/src/tests.rs b/pallets/xvm/src/tests.rs new file mode 100644 index 0000000000..aedb8b46d4 --- /dev/null +++ b/pallets/xvm/src/tests.rs @@ -0,0 +1,202 @@ +// 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 . + +#![cfg(test)] + +use super::*; +use mock::*; + +use frame_support::{assert_noop, assert_ok, weights::Weight}; +use parity_scale_codec::Encode; +use sp_core::H160; +use sp_runtime::MultiAddress; + +#[test] +fn calling_into_same_vm_is_not_allowed() { + ExtBuilder::default().build().execute_with(|| { + // Calling EVM from EVM + let evm_context = Context { + source_vm_id: VmId::Evm, + weight_limit: Weight::from_parts(1_000_000, 1_000_000), + }; + let evm_vm_id = VmId::Evm; + let evm_target = H160::repeat_byte(1).encode(); + let input = vec![1, 2, 3]; + let value = 1_000_000u128; + let evm_used_weight: Weight = weights::SubstrateWeight::::evm_call_overheads(); + assert_noop!( + Xvm::call( + evm_context, + evm_vm_id, + ALICE, + evm_target, + input.clone(), + value + ), + CallErrorWithWeight { + error: CallError::SameVmCallNotAllowed, + used_weight: evm_used_weight + }, + ); + + // Calling WASM from WASM + let wasm_context = Context { + source_vm_id: VmId::Wasm, + weight_limit: Weight::from_parts(1_000_000, 1_000_000), + }; + let wasm_vm_id = VmId::Wasm; + let wasm_target = MultiAddress::::Id(ALICE).encode(); + let wasm_used_weight: Weight = + weights::SubstrateWeight::::wasm_call_overheads(); + assert_noop!( + Xvm::call(wasm_context, wasm_vm_id, ALICE, wasm_target, input, value), + CallErrorWithWeight { + error: CallError::SameVmCallNotAllowed, + used_weight: wasm_used_weight + }, + ); + }); +} + +#[test] +fn evm_call_fails_if_target_not_h160() { + ExtBuilder::default().build().execute_with(|| { + let context = Context { + source_vm_id: VmId::Wasm, + weight_limit: Weight::from_parts(1_000_000, 1_000_000), + }; + let vm_id = VmId::Evm; + let input = vec![1; 65_536]; + let value = 1_000_000u128; + let used_weight: Weight = weights::SubstrateWeight::::evm_call_overheads(); + + assert_noop!( + Xvm::call( + context.clone(), + vm_id, + ALICE, + ALICE.encode(), + input.clone(), + value + ), + CallErrorWithWeight { + error: CallError::InvalidTarget, + used_weight + }, + ); + + assert_noop!( + Xvm::call(context, vm_id, ALICE, vec![1, 2, 3], input, value), + CallErrorWithWeight { + error: CallError::InvalidTarget, + used_weight + }, + ); + }); +} + +#[test] +fn evm_call_fails_if_input_too_large() { + ExtBuilder::default().build().execute_with(|| { + let context = Context { + source_vm_id: VmId::Wasm, + weight_limit: Weight::from_parts(1_000_000, 1_000_000), + }; + let vm_id = VmId::Evm; + let target = H160::repeat_byte(0xFF); + let value = 1_000_000u128; + let used_weight: Weight = weights::SubstrateWeight::::evm_call_overheads(); + + assert_noop!( + Xvm::call( + context, + vm_id, + ALICE, + target.encode(), + vec![1; 65_537], + value + ), + CallErrorWithWeight { + error: CallError::InputTooLarge, + used_weight + }, + ); + }); +} + +#[test] +fn evm_call_works() { + ExtBuilder::default().build().execute_with(|| { + let context = Context { + source_vm_id: VmId::Wasm, + weight_limit: Weight::from_parts(1_000_000, 1_000_000), + }; + let vm_id = VmId::Evm; + let target = H160::repeat_byte(0xFF); + let input = vec![1; 65_536]; + let value = 1_000_000u128; + + assert_ok!(Xvm::call( + context, + vm_id, + ALICE, + target.encode(), + input.clone(), + value + )); + let source = Decode::decode( + &mut hex::decode("f0bd9ffde7f9f4394d8cc1d86bf24d87e5d5a9a9") + .expect("invalid source hex") + .as_ref(), + ) + .expect("invalid source"); + MockEthereumTransact::assert_transacted( + source, + CheckedEthereumTx { + gas_limit: U256::from(246000), + target: H160::repeat_byte(0xFF), + value: U256::from(value), + input: EthereumTxInput::try_from(input).expect("input too large"), + maybe_access_list: None, + }, + ); + }); +} + +#[test] +fn wasm_call_fails_if_invalid_target() { + ExtBuilder::default().build().execute_with(|| { + let context = Context { + source_vm_id: VmId::Evm, + weight_limit: Weight::from_parts(1_000_000, 1_000_000), + }; + let vm_id = VmId::Wasm; + let target = vec![1, 2, 3]; + let input = vec![1, 2, 3]; + let value = 1_000_000u128; + let used_weight: Weight = weights::SubstrateWeight::::wasm_call_overheads(); + + assert_noop!( + Xvm::call(context, vm_id, ALICE, target.encode(), input, value), + CallErrorWithWeight { + error: CallError::InvalidTarget, + used_weight + }, + ); + }); +} diff --git a/pallets/xvm/src/weights.rs b/pallets/xvm/src/weights.rs index 0d458675c2..c25a3b20d5 100644 --- a/pallets/xvm/src/weights.rs +++ b/pallets/xvm/src/weights.rs @@ -19,7 +19,7 @@ //! Autogenerated weights for pallet_xvm //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-08-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `devserver-01`, CPU: `Intel(R) Xeon(R) E-2236 CPU @ 3.40GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("shibuya-dev"), DB CACHE: 1024 @@ -59,15 +59,15 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 771_000 picoseconds. - Weight::from_parts(818_000, 0) + // Minimum execution time: 730_000 picoseconds. + Weight::from_parts(754_000, 0) } fn wasm_call_overheads() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 304_000 picoseconds. - Weight::from_parts(337_000, 0) + // Minimum execution time: 309_000 picoseconds. + Weight::from_parts(347_000, 0) } } @@ -77,14 +77,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 771_000 picoseconds. - Weight::from_parts(818_000, 0) + // Minimum execution time: 730_000 picoseconds. + Weight::from_parts(754_000, 0) } fn wasm_call_overheads() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 304_000 picoseconds. - Weight::from_parts(337_000, 0) + // Minimum execution time: 309_000 picoseconds. + Weight::from_parts(347_000, 0) } } diff --git a/precompiles/xvm/evm_sdk/XVM.sol b/precompiles/xvm/evm_sdk/XVM.sol index 652582ff08..5008896afe 100644 --- a/precompiles/xvm/evm_sdk/XVM.sol +++ b/precompiles/xvm/evm_sdk/XVM.sol @@ -6,15 +6,17 @@ pragma solidity ^0.8.0; interface XVM { /** * @dev Execute external VM call - * @param context - execution context - * @param to - call recepient + * @param vm_id - target VM id + * @param to - call recipient * @param input - SCALE-encoded call arguments + * @param value - value to transfer * @return success - operation outcome * @return data - output data if successful, error data on error */ function xvm_call( - bytes calldata context, + uint8 vm_id, bytes calldata to, - bytes calldata input - ) external returns (bool success, bytes memory data); + bytes calldata input, + uint256 value + ) external payable returns (bool success, bytes memory data); } diff --git a/precompiles/xvm/evm_sdk/flipper.sol b/precompiles/xvm/evm_sdk/flipper.sol index fcbfdd3d40..058f649baa 100644 --- a/precompiles/xvm/evm_sdk/flipper.sol +++ b/precompiles/xvm/evm_sdk/flipper.sol @@ -2,10 +2,11 @@ pragma solidity ^0.8.0; interface XVM { function xvm_call( - uint8 calldata vm_id, + uint8 vm_id, bytes calldata to, bytes calldata input, - ) external; + uint256 value + ) external payable returns (bool success, bytes memory data); } library Flipper { @@ -13,6 +14,6 @@ library Flipper { function flip(bytes to) { bytes input = "0xcafecafe"; - XVM_PRECOMPILE.xvm_call(0x1F, to, input); + XVM_PRECOMPILE.xvm_call(0x1F, to, input, 1000000); } } diff --git a/precompiles/xvm/src/lib.rs b/precompiles/xvm/src/lib.rs index 78772c5244..5ccdfcb342 100644 --- a/precompiles/xvm/src/lib.rs +++ b/precompiles/xvm/src/lib.rs @@ -18,13 +18,15 @@ #![cfg_attr(not(feature = "std"), no_std)] -use astar_primitives::xvm::{Context, VmId, XvmCall}; +use astar_primitives::{ + xvm::{Context, VmId, XvmCall}, + Balance, +}; use fp_evm::{PrecompileHandle, PrecompileOutput}; use frame_support::dispatch::Dispatchable; use pallet_evm::{AddressMapping, GasWeightMapping, Precompile}; use sp_runtime::codec::Encode; -use sp_std::marker::PhantomData; -use sp_std::prelude::*; +use sp_std::{marker::PhantomData, prelude::*}; use precompile_utils::{ revert, succeed, Bytes, EvmDataWriter, EvmResult, FunctionModifier, PrecompileHandleExt, @@ -38,7 +40,7 @@ mod tests; #[precompile_utils::generate_function_selector] #[derive(Debug, PartialEq)] pub enum Action { - XvmCall = "xvm_call(uint8,bytes,bytes)", + XvmCall = "xvm_call(uint8,bytes,bytes,uint256)", } /// A precompile that expose XVM related functions. @@ -74,7 +76,7 @@ where { fn xvm_call(handle: &mut impl PrecompileHandle) -> EvmResult { let mut input = handle.read_input()?; - input.expect_arguments(3)?; + input.expect_arguments(4)?; let vm_id = { let id = input.read::()?; @@ -90,9 +92,10 @@ where let call_to = input.read::()?.0; let call_input = input.read::()?.0; + let value = input.read::()?; let from = R::AddressMapping::into_account_id(handle.context().caller); - let call_result = XC::call(xvm_context, vm_id, from, call_to, call_input); + let call_result = XC::call(xvm_context, vm_id, from, call_to, call_input, value); let used_weight = match &call_result { Ok(s) => s.used_weight, diff --git a/precompiles/xvm/src/mock.rs b/precompiles/xvm/src/mock.rs index bf209b7480..7115a43467 100644 --- a/precompiles/xvm/src/mock.rs +++ b/precompiles/xvm/src/mock.rs @@ -244,6 +244,7 @@ impl XvmCall for MockXvmWithArgsCheck { _source: AccountId, target: Vec, input: Vec, + _value: Balance, ) -> CallResult { ensure!( vm_id != VmId::Evm, diff --git a/precompiles/xvm/src/tests.rs b/precompiles/xvm/src/tests.rs index 844267fc40..dc09f55f99 100644 --- a/precompiles/xvm/src/tests.rs +++ b/precompiles/xvm/src/tests.rs @@ -23,6 +23,7 @@ use astar_primitives::xvm::CallError; use parity_scale_codec::Encode; use precompile_utils::testing::*; use precompile_utils::EvmDataWriter; +use sp_core::U256; fn precompiles() -> TestPrecompileSet { PrecompilesValue::get() @@ -50,6 +51,7 @@ fn wrong_argument_reverts() { .write(0u8) .write(Bytes(b"".to_vec())) .write(Bytes(b"".to_vec())) + .write(U256::one()) .build(), ) .expect_no_logs() @@ -68,6 +70,7 @@ fn correct_arguments_works() { .write(0x1Fu8) .write(Bytes(b"".to_vec())) .write(Bytes(b"".to_vec())) + .write(U256::one()) .build(), ) .expect_no_logs() diff --git a/primitives/src/ethereum_checked.rs b/primitives/src/ethereum_checked.rs index e50edc318c..29af13c229 100644 --- a/primitives/src/ethereum_checked.rs +++ b/primitives/src/ethereum_checked.rs @@ -35,6 +35,8 @@ use sp_std::{prelude::*, result::Result}; /// Max Ethereum tx input size: 65_536 bytes pub const MAX_ETHEREUM_TX_INPUT_SIZE: u32 = 2u32.pow(16); +pub type EthereumTxInput = BoundedVec>; + /// The checked Ethereum transaction. Only contracts `call` is support(no `create`). #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct CheckedEthereumTx { @@ -45,7 +47,7 @@ pub struct CheckedEthereumTx { /// Amount to transfer. pub value: U256, /// Input of a contract call. - pub input: BoundedVec>, + pub input: EthereumTxInput, /// Optional access list, specified in EIP-2930. pub maybe_access_list: Option)>>, } diff --git a/primitives/src/xvm.rs b/primitives/src/xvm.rs index e3ae06c526..38de257e27 100644 --- a/primitives/src/xvm.rs +++ b/primitives/src/xvm.rs @@ -16,6 +16,8 @@ // You should have received a copy of the GNU General Public License // along with Astar. If not, see . +use crate::Balance; + use frame_support::weights::Weight; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; @@ -64,8 +66,6 @@ pub enum CallError { InvalidTarget, /// Input is too large. InputTooLarge, - /// Bad origin. - BadOrigin, /// The call failed on EVM or WASM execution. ExecutionFailed(Vec), } @@ -102,11 +102,13 @@ pub trait XvmCall { /// - `source`: Caller Id. /// - `target`: Target contract address. /// - `input`: call input data. + /// - `value`: value to transfer. fn call( context: Context, vm_id: VmId, source: AccountId, target: Vec, input: Vec, + value: Balance, ) -> CallResult; } diff --git a/tests/integration/Cargo.toml b/tests/integration/Cargo.toml index 9e291b6295..26ca026e01 100644 --- a/tests/integration/Cargo.toml +++ b/tests/integration/Cargo.toml @@ -8,10 +8,18 @@ homepage.workspace = true repository.workspace = true [dependencies] +hex = { workspace = true } +parity-scale-codec = { workspace = true } + +# frontier +pallet-evm = { workspace = true } + # frame dependencies frame-support = { workspace = true } frame-system = { workspace = true } pallet-balances = { workspace = true } +pallet-contracts = { workspace = true } +pallet-contracts-primitives = { workspace = true } pallet-dapps-staking = { workspace = true } pallet-proxy = { workspace = true } pallet-utility = { workspace = true } @@ -19,7 +27,10 @@ sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } -# runtime +# astar dependencies +pallet-ethereum-checked = { workspace = true } + +astar-primitives = { workspace = true } astar-runtime = { workspace = true, features = ["std"], optional = true } shibuya-runtime = { workspace = true, features = ["std"], optional = true } shiden-runtime = { workspace = true, features = ["std"], optional = true } diff --git a/tests/integration/ink-contracts/README.md b/tests/integration/ink-contracts/README.md new file mode 100644 index 0000000000..24b9891009 --- /dev/null +++ b/tests/integration/ink-contracts/README.md @@ -0,0 +1,8 @@ +## Ink! Contracts + +This directory contains Ink! contracts which are used in integration tests. + +`.wasm` files in this directory are used by `pallet-contracts` scenarios, typically `src/xvm.rs`. The json +files are for informational purposes only and are not consumed by the tests. + +The source code for the contracts can be found at https://github.com/AstarNetwork/ink-test-contracts diff --git a/tests/integration/ink-contracts/call_xvm_payable.json b/tests/integration/ink-contracts/call_xvm_payable.json new file mode 100644 index 0000000000..774d549f04 --- /dev/null +++ b/tests/integration/ink-contracts/call_xvm_payable.json @@ -0,0 +1,438 @@ +{ + "source": { + "hash": "0x2f161d6093ea4272bde71b8e3c8e81efbd7b15bc3b482ebd8bac2f3f43950af9", + "language": "ink! 4.2.1", + "compiler": "rustc 1.68.0-nightly", + "build_info": { + "build_mode": "Debug", + "cargo_contract_version": "3.0.1", + "rust_toolchain": "nightly-aarch64-apple-darwin", + "wasm_opt_settings": { + "keep_debug_symbols": false, + "optimization_passes": "Z" + } + } + }, + "contract": { + "name": "call_xvm_payable", + "version": "0.1.0", + "authors": [ + "[your_name] <[your_email]>" + ] + }, + "spec": { + "constructors": [ + { + "args": [], + "default": false, + "docs": [], + "label": "new", + "payable": false, + "returnType": { + "displayName": [ + "ink_primitives", + "ConstructorResult" + ], + "type": 0 + }, + "selector": "0x9bae9d5e" + } + ], + "docs": [], + "environment": { + "accountId": { + "displayName": [ + "AccountId" + ], + "type": 8 + }, + "balance": { + "displayName": [ + "Balance" + ], + "type": 10 + }, + "blockNumber": { + "displayName": [ + "BlockNumber" + ], + "type": 13 + }, + "chainExtension": { + "displayName": [ + "ChainExtension" + ], + "type": 14 + }, + "hash": { + "displayName": [ + "Hash" + ], + "type": 11 + }, + "maxEventTopics": 4, + "timestamp": { + "displayName": [ + "Timestamp" + ], + "type": 12 + } + }, + "events": [], + "lang_error": { + "displayName": [ + "ink", + "LangError" + ], + "type": 2 + }, + "messages": [ + { + "args": [ + { + "label": "target", + "type": { + "displayName": [ + "Vec" + ], + "type": 3 + } + }, + { + "label": "input", + "type": { + "displayName": [ + "Vec" + ], + "type": 3 + } + } + ], + "default": false, + "docs": [], + "label": "call_xvm_payable", + "mutates": false, + "payable": true, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 5 + }, + "selector": "0x0000002a" + } + ] + }, + "storage": { + "root": { + "layout": { + "struct": { + "fields": [], + "name": "CallXvmPayable" + } + }, + "root_key": "0x00000000" + } + }, + "types": [ + { + "id": 0, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 1 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 2 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 1 + }, + { + "name": "E", + "type": 2 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 1, + "type": { + "def": { + "tuple": [] + } + } + }, + { + "id": 2, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 1, + "name": "CouldNotReadInput" + } + ] + } + }, + "path": [ + "ink_primitives", + "LangError" + ] + } + }, + { + "id": 3, + "type": { + "def": { + "sequence": { + "type": 4 + } + } + } + }, + { + "id": 4, + "type": { + "def": { + "primitive": "u8" + } + } + }, + { + "id": 5, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 6 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 2 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 6 + }, + { + "name": "E", + "type": 2 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 6, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 3 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 7 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 3 + }, + { + "name": "E", + "type": 7 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 7, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "InvalidVmId" + }, + { + "index": 1, + "name": "SameVmCallNotAllowed" + }, + { + "index": 2, + "name": "InvalidTarget" + }, + { + "index": 3, + "name": "InputTooLarge" + }, + { + "index": 4, + "name": "BadOrigin" + }, + { + "index": 5, + "name": "ExecutionFailed" + } + ] + } + }, + "path": [ + "call_xvm_payable", + "XvmCallError" + ] + } + }, + { + "id": 8, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 9, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "AccountId" + ] + } + }, + { + "id": 9, + "type": { + "def": { + "array": { + "len": 32, + "type": 4 + } + } + } + }, + { + "id": 10, + "type": { + "def": { + "primitive": "u128" + } + } + }, + { + "id": 11, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 9, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "Hash" + ] + } + }, + { + "id": 12, + "type": { + "def": { + "primitive": "u64" + } + } + }, + { + "id": 13, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 14, + "type": { + "def": { + "variant": {} + }, + "path": [ + "call_xvm_payable", + "XvmCall" + ] + } + } + ], + "version": "4" +} \ No newline at end of file diff --git a/tests/integration/ink-contracts/call_xvm_payable.wasm b/tests/integration/ink-contracts/call_xvm_payable.wasm new file mode 100644 index 0000000000000000000000000000000000000000..9d2b538e7c952068ed03675791f2463e3644b71d GIT binary patch literal 13469 zcmc(mdu&|Cb;jpD_Cekyb?wTDA}R6RwGvacC@x>Sr0h1lZfsd`T^VgsxQzp8MXp3H z$tAhFq^#BnWyeYLa1iI|I&s_t3E=$U7A}w$1{xp<-~vJ6wg#FYDe3?Q8lXmDxBeq2 z0yL=l`(}1mWc)}1G^&N=otZl`=gfD$bLPzK+SJ;so-xLIXW_KJz3pwE4z}%sim_#L z+M^WGs<-xOP8+p)??0VUGq2r~_q1;Nk9+w{yIxxvkIvOQr`nyhR_$EfWNcgcs;%0} zX8lyBRcp4-)?2Omg7Mv(z4Nt|l~ePJwWa2%`aPX`v%R#|G=uJCW~sTp+39s1TBx7d zJa=lf-fnAZWBW`*?N^GUt+sNiRqt%JnkES9&8_TeeRZvM!T0<)s8&2L>jk0j2T@@B z!1r^Zp9^xv^8#bCewYhPXu`ZP1DR~bm|#F)nQWNx*1bJ@vU%2YM%9 zh5Gu++68lPV6SIVFB-mja^%KepPHFHd~LCG?8xB19k1NrdE3Eu;wQna;dWv&iAjP+ z;_+wHm}7=o>NSc%!Hc5kjOTA(XVQ4N6dW>%8S}zon3$Aqp}(Dkskaf%F*Ho`TN-Ib zd=1YxQjht)cpwQz{Jj1Y;z1^K9e5WHM)ZpthfH+DFoAyu^B9d;uJ}l>>QsEQC(`=T zV*1=8VH}z0v={EwXT%RnVd9@CWo}`-6|705MkyQLq=_$UkbdT|Fs>v4ZDFFfsCqas zRp0bWi!%}d{F@YkJyHghJ+Vb>jz&5rR&?=;<2`mT(qqC1z{)@ z!gvn_Yq7h%VB4Q_O9A~xDM-ST!e32cDRcXf=N1z-XbYF^6yHHa1-a;x$Eq(s5~c^! zuhEEA>6J;JqI{K!4oKJgE_#~KV0;LEzkQLIXw&HjE@ERK;6q968_41kq&-}Mw1-QO ze(9mhM#DG`7g@{UqLKg^VLnt$sOB5af}iyIVl6B(faQcQLEX|QzS)55c@C3UR-yj!GleKAv2PSEv(Ld6eZdH}zTSG=ehw z7>zD_HM`j9r_YKB1qzn0c{jxkCc3w8Wl+S9Uix-7`3XjbKb;>a}k4 zhUpQc!%!~X)9Z8TQlMV8kKY2WVZHIwARdy@<6$Z&@4=m}w?F4Y9_L_AbwWw(&q)o& z#o!qa4xv@=U*eCNxP-YjQg3-U2z<|atG`8n3goxJ?L&B)V2^uKgiJx(h_8W{_>Vrm zdb5CI<{El~mw*B<0mayZRycpKXc*^*z<kikBq8i6LjzB#7T2X?Y?mLkN{6Itzuwc?%B|RZzs9rr;ArrE+NFsVxce zM7(%UKU@Q@BoP)<&&G0$WPV3x7Oy>O+wJt)t@wH?x)h+G7r)g+@!P=H%C%8gp4IjG z={{|2P`Vk{tV*l&yN^jP3PicY#D&z8_vhm{ev^sb<@@Z^&fp$S>tW-Pf3v9ul=aA~ zNe1w&Vi+JsmBT^4h+`+d0yKTl$c}K!6f_cx%2^Zrcg`Q&_9f$@*NvEp8x?=i_A%)T zrV$5@P||F%d z)3BZ5Yc%)W>_loF@lO}CZrqNyE2Uib?RrtM87rPnj6-sTxSq1@saBt|GvzaCeF5v&beV9vN`N2es_*1WYzShy-Cb;4n!G zig{__R=cHDPbBkl%Z8A!5aZwyxvHZ-ffUPQCQQ z_qh$6T%li9NbR-17GYj^J+?jJ2y+WjNjNL<2yQ3ZJ^1-By)J#Bn^@ohiEmR%f{K=g zrPx-nd4iAhLR|ETz~zVYPyRB0Gg`?QahG=l&zbM z__sL+*;};HCFYGb7I<{P#hbjARfseD9S>h?({Q}wuSE9Q^X}aVj#3)2XP+d*jl(|f z!LvNJogpKsY4`>K!(_47h?eRYXFQepRj+9jiWJ)?PlLUAN|Aks(AUwzc z^+@231r+;&rccfbAK0K_k*wxp2vx|jI|>gr)$lfoIrWK)VQPwAFozMas?8@b6dvcY zy;7DLI2RG>jMX3#=y;K2uQG<6B$7uWEG*^H zOs8m)oOF0QDj=5*kR3#muu;nVtkldaq$-<2?m>471QsfL9kjv)+iO{+49$f`Jk5Jf zPjNK>CNv`qDv#~8=c~a;Ph-Pr3f<&>EW^VTtc=(UY$zr@?3yTry=B=X(#4$|PVIoQ zYsuMB7Bl_;$7R?&0uVV7hh8R}TP7XaV=0Ab!gG?E=zKpt3f=ie!bQ4Luq-ixn8-lL zh_4guKqm?xvVDpGmHW=G#4OUusVcH^tRkX>KEZ+07D0aRM`) z-wWv;uIwGVadr5-5?<|s0mot-P4U!%sU%lurn(exXg}!Ce&?=W!HR|HFCHq05N~FH zjSx?UUbcHYL2=p0-%goMX2#m&?EBq5f-M@QtXgnYxi9~+Cl1gFZ@7b}%dd+W=FoP^ zRf!@p5lLq>G6u-Y=|gyjGWI?_<3=&(P|FH6OA4K|Gok1bxCA~2F98LSzhej3^l-mMn_@0~*1k&e%93@6 zQjav=nj<+#w{Hb5nl=*ucuTBI;|`PLVd=daWcPL^&7j3%NXY`8Ojk&-Ka%7m&r4pA zFe5fYmNpRQ6wq*Loo*=Ekx_}|)0oi~V40*@arJ&z+iJ7b)N_Dxr2(d#NFphawY;&x zRH)c^3aupuYS~19Ozz^vd+cE0$*l#@fdu1~B-oyzr3mDE5?3##&wtRm`n6(-4it<( z&g_n{I!f)ahpC4=$4)LSY$~R`M4sKdqwvy0-Avm{pZXIx!L}{swPz#t2nu?=s{l#` z-BG|1E~xB|s;fAq%iqyZrMTK3HJU8bo&USZI@>WziaGz^+IJT>L+?7nNGHKY&j&sv zc2cm3z69{XO_4tMA)Bf{?b&M}tzICqh-G=o@9_1P(^uW)VahmSxS~nVsL>Cy ze&i01-K0R_qvZd2aY!M0=RcHu=pPK%Qt9Ba4wLj82`#E^9)gOCz5M$3jB$~!r* z)yRJ>(Tcdt;IT;ma!8U6K@1oA;&f$2JXh=W+zh58iCIps=MUkN zD7cT@#M>z44itUtgPW%zcgo@&6?=E3ay+Vd>oYz|Zy?S*O|El*Osk7~G4De|0weyN zdZ49bgFw{1S}S=(moP4~6rZ!8m8(pvsCS(i#(tzV;ena~XlN>B67c1MedekIE zE;ls0E|3x@#N|B_H`oQbDRQ0Z)nyc2hoW?U`gSp5V2Y z6zuE?!RH{DDAGBxzxbkTf18)>R-*vgP7T|}| z+4%zejDLRm@_<}$ewNpHpx#E{yzY(j8?uBP)An{cIj^7M-zx!Bbl*}8Feb1I)?ocM;0tFcN z>d(G7g!r79Paa4Ij@w(uZm#mv&skL5>B9xyN$mt&Tqtxawtn{k3ND80?pNWLy}|CG zMmO2r(oKyt+l|r>G9aNF!C5idu0`U@+9ia9-3(@IDNkFO7S5&@KVl=emLn#cNXv00 z&J^b6hyVI>-~aqeU;17YJsx_4%7@5YIWfs1%!P9kux6U-jo-csa| zdxr{*^yW_RD85cf2|!5i27iN%_^5J)tS!rq=nKl5OpeqyHwzEn}c6S z?kG*LgC_X-!tNLfyYBm()3jUr#;)2kyK0Yi*M#rIM~lkPz)MFdn^d^#X2OESP8yga z#-!ieerI=W>c1`lr2I}lo5 zCD|Jv!aD(G*1#d|gKUf=8%t$AP$%GCWz3MbAB}NiMM59K zBp&hJrt`tA2hvMyQ2}ZX2cGd*x39xVfbD7AvKti-n{l{^_*^$4YDe>xbx$cl0>X{087C@6+|0L1>tL{;W+6we#fU5KI?)>Bx{H^uk6e8y)4GQ<>VKS zk}@%1K<7XYdE_G|9#1{g96$&LcF6!qLO&T$>_rX0RLDV=y_51RP&NUOSzc8Ig)JKn zOtjbX3-_`#(;VzF?`HQ0vdi~qz&y;=h%iB%o!4Gx**hLYglPuMt*}Tmh04woZ~+US zJmG_$#PC}NXor#>7c;N=?RhtjaN4n_IeXf*2ddC?$CUQX$?m|yT$7C z<~G*L-<}hC_}b${|5U|BiMwX_eQ>>@5tkcMx~EB#9HA zz9AJ1E@H4c&1w%Q+SqsAg6eG7t8-=TZb1>^CidOLQO_vxWVFxDY|=*`24pM589I6- za9_}pqe;A^6@wJU_;c0}Y(md}EKC&zb~8jBh&%a`!4iai_ucc9OItJn+K{?fGx+Jt zI-HhivVi(&&V6+)$dq=mdDx?FEGkka|Edxk$^y^i{O-O+2SiZ>y}tpx5R?GrKhw+zLwV#`I;FL>F}<# z$y#pp5YYY}K&+Aq?mK_CX1)lk+IV3vGn1eHOPPnhsPy~CkJ?ZXee*4$!M(%izOdHm z)K}L#NoOr-Z=T_|PxGDR{8DEzS=*|&&aSMTAA5JZ-fEAv7i*i%v7_^~*15H@R{h*k zyVJTb)^5#@oul{WnWOV-tE1!f`T5Gk?Cips+4}s<#N^mgbNLiMY#N<9I&pN|_O3V2 zEj8<7Yfblasxf{}b+pwsrf57nm*u{!`?hL*60JnCp1foAYa% z%}$+PeeKO$RNNPh3P(%-%%(PRrt zP0jxf#uj<*TGySewFdLIh12N#TGPVc9<#qTyJu_l)Ozhg?aWGjY-Q<;bX{i-?gjKi zEHl8TSC1%X_;i2IhFENqoUgSLnf8KthH=tUW5Vudkf+8Kj1D2um#^7DgxbQwZVu4T z?0CPrxzbr$UqQr9qR>YI^dDG~Y5vS&kG3}3oz3;J&f40_ynag89&0Y0TkNb{7@a6j zOpcbvN5{*fwOVU_u`)GUTVG$Pk1o_&=a-sV|CmPWDaxs=##Rju^4qqxdDmP!e`>2f zZ~0^ve9v4!hmWF+j5c3eugx!YF7$~_+spJ}_v!b|`VPolM*7w3ZC8NS?&p7rt=2mH z#4uSqn z=2JYcMn5_Jiv1{x9(icz-OpJeSFO z1~Xh48_ch@>egN1!B}&16%&-p$h)8o?<)05Lv)6>(H>FV^%^lYVE8Lv!KCM#2w=}M(it;|$rtL5r= zb)q_1ovKb(E7fXsraC)Qo*AE+n3g0hj-!U zDEzLRXYRp%?`0QI4CvKgqCP{tM>>Ale6FtekhE8p=Ii1i+&jl^@7~m#3tjTTp}c2e z#=gkhC7xGyZC1arCH7%?Xc=Ap7#*HwJ#YPg(xn$HzJV@(gbx3M6LC*(+H0FFpmczC z(HuFPEUXcPx_)|v@$R(7Y~o`B;+N1x zYdp?JdrhzYF3Q?3d-Xr0thm#wYk%7L{w(z?mIw6xg3Pqf9Aj1nU2OkkSGOyRs~9|H zcj5L}ZGPTHJ7a!=PWPkFXZa{z?YM*Nc@&)my^hZt~@r~Z4r{5yFvLsKM?9Q8Iqn3AMp+t|8@NEj_#34}ixxaeOh1{>Iy3m{-r3c5D1gBE4Q~ zk?oJRIZV|#Tj2|%lSeB@r)=tB>*Cm74twSd;3$Ju*7PPm0{abobPQEJf`$JXdFG$_ L{4Jj+BJY0zJH-Da literal 0 HcmV?d00001 diff --git a/tests/integration/ink-contracts/payable.json b/tests/integration/ink-contracts/payable.json new file mode 100644 index 0000000000..5be916d5c7 --- /dev/null +++ b/tests/integration/ink-contracts/payable.json @@ -0,0 +1,287 @@ +{ + "source": { + "hash": "0xcfbb81744c83a08b460d939a434f4c9876a6901f6a4a48e1516004632c5d02f0", + "language": "ink! 4.2.1", + "compiler": "rustc 1.68.0-nightly", + "build_info": { + "build_mode": "Debug", + "cargo_contract_version": "3.0.1", + "rust_toolchain": "nightly-aarch64-apple-darwin", + "wasm_opt_settings": { + "keep_debug_symbols": false, + "optimization_passes": "Z" + } + } + }, + "contract": { + "name": "payable", + "version": "0.1.0", + "authors": [ + "[your_name] <[your_email]>" + ] + }, + "spec": { + "constructors": [ + { + "args": [], + "default": false, + "docs": [], + "label": "new", + "payable": false, + "returnType": { + "displayName": [ + "ink_primitives", + "ConstructorResult" + ], + "type": 0 + }, + "selector": "0x9bae9d5e" + } + ], + "docs": [], + "environment": { + "accountId": { + "displayName": [ + "AccountId" + ], + "type": 3 + }, + "balance": { + "displayName": [ + "Balance" + ], + "type": 6 + }, + "blockNumber": { + "displayName": [ + "BlockNumber" + ], + "type": 9 + }, + "chainExtension": { + "displayName": [ + "ChainExtension" + ], + "type": 10 + }, + "hash": { + "displayName": [ + "Hash" + ], + "type": 7 + }, + "maxEventTopics": 4, + "timestamp": { + "displayName": [ + "Timestamp" + ], + "type": 8 + } + }, + "events": [], + "lang_error": { + "displayName": [ + "ink", + "LangError" + ], + "type": 2 + }, + "messages": [ + { + "args": [], + "default": false, + "docs": [], + "label": "deposit", + "mutates": false, + "payable": true, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 0 + }, + "selector": "0x0000002a" + } + ] + }, + "storage": { + "root": { + "layout": { + "struct": { + "fields": [], + "name": "Payable" + } + }, + "root_key": "0x00000000" + } + }, + "types": [ + { + "id": 0, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 1 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 2 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 1 + }, + { + "name": "E", + "type": 2 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 1, + "type": { + "def": { + "tuple": [] + } + } + }, + { + "id": 2, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 1, + "name": "CouldNotReadInput" + } + ] + } + }, + "path": [ + "ink_primitives", + "LangError" + ] + } + }, + { + "id": 3, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 4, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "AccountId" + ] + } + }, + { + "id": 4, + "type": { + "def": { + "array": { + "len": 32, + "type": 5 + } + } + } + }, + { + "id": 5, + "type": { + "def": { + "primitive": "u8" + } + } + }, + { + "id": 6, + "type": { + "def": { + "primitive": "u128" + } + } + }, + { + "id": 7, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 4, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "Hash" + ] + } + }, + { + "id": 8, + "type": { + "def": { + "primitive": "u64" + } + } + }, + { + "id": 9, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 10, + "type": { + "def": { + "variant": {} + }, + "path": [ + "ink_env", + "types", + "NoChainExtension" + ] + } + } + ], + "version": "4" +} \ No newline at end of file diff --git a/tests/integration/ink-contracts/payable.wasm b/tests/integration/ink-contracts/payable.wasm new file mode 100644 index 0000000000000000000000000000000000000000..34587fc018b655f28846a57d186d2de6b91b7c59 GIT binary patch literal 11235 zcmc&)ZERduT0ZC8J0JF)Np6a}>n4GonR+m8i_u0ZNaTNZu*;zuQllpia!KqZ7)iCt8Q{ZXM7^+$#H z(Ndo0oI7K?X{n0XVC&BLxaXYr>v`Yvo--pF{Z%Q1kS4faZEeY|`@Jn&pdoBs-0!1@ zZ-lsCqSH26l@IY_>;44aZwd9JOeOkJqg@P^qQUw8pxbLKMZ&jzg#(+7_C|Dm&}(%1 z7o%P;S`^B?Ni28PHU`61b}_oJv2=bl>i0QY*fFJ`fBlN+YqZbzqQOS5BRntaY$jKu z)o$;Kk}C9$ACqIAQ20wIFCnD#geOuenNT7rl4&8v{gf|+N8!PvT$B6vC)1F}Y;XHP z>VWc_jdok?OD;xh?d}zEU~EDPBZGZoM}MZEe{SZ)trMS{oXwBF_{C$#q}=khw9?)g ze@lym7TRlRiH{i}P71V)Y~{R+41(a0(84^tE${IcP}$y==FNxwFf9Ttf(@*5n=ply zyf=kOT2_^hLR1x(m}nX0YKKR)<1OR4zE{8QS5=5%7@bCe?kUXmjBZucq^2 z8ZXiIdE*7YHLXDWIJ?c; z!{)S>#=gm6K`iOmVQm`hXe}`b`chyg#8gvgO^M)?<4CXM9Y^w{n8a-GGQrg;s50+9 z36?UbTzd|cv|A!4g4^BXFrIuGI<7&F$~^upj3XBkR#HZWStAoh1Ywp8OsjO53Ns?e z(n1)kWtb4bx206DDcCK)B9H5rPwh6$uBGCKH27jzXR^HqfQ<(t-@eud2fkkkF=Mkjx}%;LOBGL2WlO zEDC{2=Rp&gX+4NyV#(unC43wo`A=B$G&-#Rz_~zcT1|e`gUAs2hR}gYLjNFSL6oyT zKa6rQHNrtKuJO~LHHvQc?G3GrD~yekKp{jts;prY9a(~aR{C{R2u>&U+AnMPA+kTXPFy z17UE$xM4oTpIpzS7^TCUZ$wUd2w@rqMyP;nS|awjeI}oz<3WWKTIeL}hY^epNRjpF z#_3j0YGQ;RelDqfc44EX5|RgmxFaiqj0(80pHG>@ASZN+JlqZzKoGt35{7BnWDYN;2fnquV>=+3mlV|ecY`b34@q}lI}`qMRUddM-M4Bohf zLJSF0K(d5RLTzA-d{~)K^M_(dhy^Nqmx4<>MOj%=c9zu{KA?^rwaLH;BriyQu;9W9 z?7iPY3Z#)j+>9;sGB!zvMxQB!Q$0bK_f|-TDrUgL@LQ;Ms17Cx4_aX6pdd#Qpu>pd zK@LRrl$VS;fA%P2;ww+Mm(`<3|dy*AYghz_7u8OFj=iAZZLP_^3^}6!DjV zuC~K-Hf8{x_Yg$rx_upkdMCv`+sbU3H`(HDBu#R2^g3(2jN=&6SyBiy89;0I8rk8j zSM3@`L4SJ&>>m!ej_R<(EpgMPNv)fA-70<%-V9{!XnLqu>`{C5>RbI>uijxc3P*$Z zPML%RwQ0BmIc>k}w0-X=SU9g9227ibLXiZR&A#HqlNPoC(>XSgY@;O-QkQFfX|x~{ zI}5hY5_=GGZUVD5wVxkjz)R9S;z7)sFepuoc{znVWZUj&h!2n~ ztrJL;4sx!@rOadYl}sp=d5#lBsV39O-s|VLx$Y!|` zS8y4sr3KX_Vj|wbfRuF_W`L~LR=w~k`A(r%+!eu&TuJ`v4I8)BYoZ8e!xzqHjz?6aTt&J%s1>v z-@=2r6yiqCY=6bZja!KjroWhf7OOk>hldc#g7B~ip58eG+1!8|6T`$Xr;=uBlq|gQ zRp0@s0GVeV0J6uyryQPWP|F}im81Hl4Xb# z;uv#g%&j@>RbzE5sKC_X)KE=qB;9z2b)L z9RLd4X!o7|@NZt9{S^{MilDPEf!4o@yR(RP2H|B4SkI&b%Nt+Zh4Yj$V>4Eeq;OAGWD-0K?OI6f6@}i0^n}}!kvrsgtVZN#?-86u4ARpT za|9oViwJL%$YSJrK6NOkkXpfqjlT{Z15{|(^EKo8u9tGZQcu*=Ev+W{7A;M#kgh zkV!y7kB8HEEp^7Ot`S)dLnjPlpH(P3-ez!n*~mcKjt!uJsn91V3gFqTD1a$JErrtv z4iJxdn6_ueCzv0-{W~_!nRn63nU|lnEl=RS%K6hlZvl53BKQ~2WlV6jJ%J8^WC~)t z(4{G|!FILddaQZyHQ?dt=dDG#%?j&yIrgUMc@wxpu!^#KF>JsnV<0F0EqH8GA;mt3 zIpR*MYlZF6DuK||DnMYj!Ij4?1hfehh_MW{2pgwRAZ%1nfRU;yxUslrLWQ>_b(Hq_ zGL38@>*4s7m_bqxR6b#AfO<9+;FbX-u`~}`9voBf#=(r8g|{Y#Y0w`yHFASM{({ZI z#V8k$%iJM^`A>#)#@zRKso-KeueKQ5v1i!GXKx|m+NqXF#?kw0n4mEXGBS2ssmMry zy9ZS54xM#dOQWxVp2?PZ@o^j3DHxpq3Ws2>hJ%YZ{oG$Z`}()udGViu;3Z#<$LTTR zDou#B5El@*VHoRkUdueV&OUJttNVBLY(F9c?lCwJ83q!1PB1im%EgX`vvXGX)u z+^`gw#xRVa2}RE44xE?AmXEu~R&JcVz^0GDj=aoAQ&?7j2=JY>;U{30m)g_;Qu&^F z_>srb$bsbD;2u)*@ED%WN_aW)2J25h_&s;t@bx$`&E%aSD!pkweE9toH0H_{QVPF~ zozg`{J;uYM9s^%+AjcNDV7dm4Iu`srEZhYbNPz`JQf@PO!392@*xgw_=Jb8XY25nZ zp4QuYTCc=1Aq0n0Ic9Fq7ov!=$p&&FDtJGlF_cLT#Bm|aUvGUWmJM4uUUBz9Ao!l&PD=ZD*#5z8KG^8@0~ehaCwmT!1g{* z3JJLtB*}YV6P=rOk3-=x#yfG|kAo>UL&f7M#WfiXK-iJaEevEAjcx)2RSN#%YXk zkV)j={Sc5x9smmd?Zc5>3rT=Q3b!JxVy_Mb#-j>z5DKp4wde#3cOvVdqDT>1KSb3- zp{Nix77Em_g~lkc8R6;iZ@OR-&>Doz^<%O8Fafe}c^hRK2O^jNEXgH^0_t1?yUcyK&g;)_T!$K(okke<5`_*HiYg(Da57D(T@D)h#iJqpAyKU|7b0Kd`F2zT+) zJlNQUm0LI%Y9l-vOxl%&dF5G1XNfos2QTd8Rl}=|oCkr0TCf^G^xyuPZ|Ht;J{+5d z1K{xKynv~}g!@hl-*PZt-~!+VIfIR($i0{|6wrln(D_|MYl<%SjL2;LAcP3retZGLv#L2@OO1`r?1^Thx73~OMN7;5LGQ|Jzt@~yS{_{5xNxG`U7aaL z&1SVUKfickK5EuW<=N%V%6WXtJX1MQI#INvqt4QDCz|bc&NnYLmOHc21A`O2zTlq} z5k7y6X9CYpnC|lGTDwom4}k6x=uJr>ZbKP$n%#}gAi~$}dZQCPuog83sN(zgZtq=; zJ&ZBKIq#tUc|3b~y|>qGVNM^4*^FjZqnV2+Z3AD?pSO+n@&zmOl$87$OC<3m@Wej= z&`}4<)t4K69j&enu820qk2Lg zcK0&IiCynkH`;^cwKhn6WDp7P54u2B^%22N^fvl~jkVc9x7)VH>(6$Umo5$3S7u6u zQhBCOoGBJ&8jW7_QnfPESX*mHGmDMhWmp*k&vG_PVup2g_8I^Yel*Z+y6(p1^P5rA z0m4^OTmcVHf*C5k*;s2dmj_pN3-vU{AkvEA*ralb++&cRJl|FVaP59fp>4AmNs#JB!f+y1Ox;SuY?;EcQ?8!KFyIqmEwg!_s)gwi}3^ zn-bFMriAh)=m=*|aV>zM@%Lr)u{JD+e6nvJp0!42xw%3s5A=~E$Mw~C{8K1Byg#XT z)_M_rFvE?pL9^S7th)jSvz?7qn4nN76bq$7xlk$06{>|=p(5R!h}#wNjm{R;#sYy*ghj)QYuItz4_r z=4#bityZti*9-Mxy;LvPEA_d0wO*^&>+|zqaUSI7vGhEq&76pLiJti(gMm@tfe`zu-i;&KCXdMh~iV3XQcneN-=Y z5wttqfkr59Ueb$E9|76L-@QZM!g`G7AD}#j@<8mTH(1|;-mFbrY(QKXb26RqQOvE`Ha!(Xk_t$1O(9?jpAn^zO||S32Fx9o>)G$TYjX>t+mJ2>w1Q_*3;i?R||t z5`3GixD@52UInwKex9peJOPK8J~}>>V}Hi&S$l2pe|{*quyGMTcm H160 { + h160_from(ALICE) + } + + /// `AccountId32` mapped from `alith()`. + pub fn alicia() -> AccountId32 { + account_id_from(alith()) + } + + /// Convert `H160` to `AccountId32`. + pub fn account_id_from(address: H160) -> AccountId32 { + ::AddressMapping::into_account_id(address) + } + + /// Convert `AccountId32` to `H160`. + pub fn h160_from(account_id: AccountId32) -> H160 { + ::AccountMapping::into_h160(account_id) + } + + /// Deploy an EVM contract with code. + pub fn deploy_evm_contract(code: &str) -> H160 { + assert_ok!(EVM::create2( + RuntimeOrigin::root(), + alith(), + hex::decode(code).expect("invalid code hex"), + H256::zero(), + U256::zero(), + 1_000_000, + U256::from(DefaultBaseFeePerGas::get()), + None, + None, + vec![], + )); + match System::events() + .iter() + .last() + .expect("no event found") + .event + { + RuntimeEvent::EVM(pallet_evm::Event::Created { address }) => address, + _ => panic!("Deploy failed."), + } + } + + /// Deploy a WASM contract with its name. (The code is in `resource/`.) + pub fn deploy_wasm_contract(name: &str) -> AccountId32 { + let path = format!("ink-contracts/{}.wasm", name); + let code = std::fs::read(path).expect("invalid path"); + let instantiate_result = Contracts::bare_instantiate( + ALICE, + 0, + Weight::from_parts(10_000_000_000, 1024 * 1024), + None, + pallet_contracts_primitives::Code::Upload(code), + // `new` constructor + hex::decode("9bae9d5e").expect("invalid data hex"), + vec![], + pallet_contracts::DebugInfo::Skip, + pallet_contracts::CollectEvents::Skip, + ); + + let address = instantiate_result + .result + .expect("instantiation failed") + .account_id; + // On instantiation, the contract got existential deposit. + assert_eq!(Balances::free_balance(&address), ExistentialDeposit::get(),); + address + } } #[cfg(feature = "shiden")] @@ -107,6 +185,8 @@ pub fn new_test_ext() -> sp_io::TestExternalities { (ALICE, INITIAL_AMOUNT), (BOB, INITIAL_AMOUNT), (CAT, INITIAL_AMOUNT), + #[cfg(feature = "shibuya")] + (alicia(), INITIAL_AMOUNT), ]) .build() } diff --git a/tests/integration/src/xvm.rs b/tests/integration/src/xvm.rs new file mode 100644 index 0000000000..a92ff4c382 --- /dev/null +++ b/tests/integration/src/xvm.rs @@ -0,0 +1,334 @@ +// 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 . + +use crate::setup::*; + +use astar_primitives::xvm::{Context, VmId, XvmCall}; +use frame_support::{traits::Currency, weights::Weight}; +use parity_scale_codec::Encode; +use sp_runtime::MultiAddress; + +/* + +pragma solidity >=0.8.2 <0.9.0; + +contract Payable { + address payable public owner; + + constructor() payable { + owner = payable(msg.sender); + } + + // 0xd0e30db0 + function deposit() public payable {} + + // 0x3ccfd60b + function withdraw() public { + uint amount = address(this).balance; + (bool success, ) = owner.call{value: amount}(""); + require(success, "Failed to withdraw Ether"); + } +} + + */ +const EVM_PAYABLE: &str = "6080604052336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102d6806100536000396000f3fe6080604052600436106100345760003560e01c80633ccfd60b146100395780638da5cb5b14610050578063d0e30db01461007b575b600080fd5b34801561004557600080fd5b5061004e610085565b005b34801561005c57600080fd5b5061006561015b565b60405161007291906101c2565b60405180910390f35b61008361017f565b005b600047905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16826040516100d19061020e565b60006040518083038185875af1925050503d806000811461010e576040519150601f19603f3d011682016040523d82523d6000602084013e610113565b606091505b5050905080610157576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161014e90610280565b60405180910390fd5b5050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101ac82610181565b9050919050565b6101bc816101a1565b82525050565b60006020820190506101d760008301846101b3565b92915050565b600081905092915050565b50565b60006101f86000836101dd565b9150610203826101e8565b600082019050919050565b6000610219826101eb565b9150819050919050565b600082825260208201905092915050565b7f4661696c656420746f2077697468647261772045746865720000000000000000600082015250565b600061026a601883610223565b915061027582610234565b602082019050919050565b600060208201905081810360008301526102998161025d565b905091905056fea2646970667358221220bd8883b6a524d12ac9c29f105fdd1a0221a0436a79002f2a04e69d252596a62a64736f6c63430008120033"; + +/* WASM payable: + +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[ink::contract] +mod payable { + #[ink(storage)] + pub struct Payable {} + + impl Payable { + #[ink(constructor)] + pub fn new() -> Self { + Self {} + } + + #[ink(message, payable, selector = 42)] + pub fn deposit(&self) {} + } +} + +*/ + +/* Call WASM payable: + +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.8.2 <0.9.0; + +interface XVM { + function xvm_call( + uint8 vm_id, + bytes calldata to, + bytes calldata input, + uint256 value + ) external payable returns (bool success, bytes memory data); +} + +contract CallXVMPayble { + function call_xvm_payable(bytes calldata to, bytes calldata input, uint256 value) external payable returns (bool success, bytes memory data) { + return XVM(0x0000000000000000000000000000000000005005).xvm_call(0x1F, to, input, value); + } +} + + */ +const CALL_WASM_PAYBLE: &str = "608060405234801561001057600080fd5b506105e6806100206000396000f3fe60806040526004361061001e5760003560e01c80634012b91414610023575b600080fd5b61003d600480360381019061003891906101a3565b610054565b60405161004b9291906102e3565b60405180910390f35b6000606061500573ffffffffffffffffffffffffffffffffffffffff1663e5d9bac0601f89898989896040518763ffffffff1660e01b815260040161009e969594939291906103b0565b6000604051808303816000875af11580156100bd573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906100e69190610554565b915091509550959350505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f84011261012d5761012c610108565b5b8235905067ffffffffffffffff81111561014a5761014961010d565b5b60208301915083600182028301111561016657610165610112565b5b9250929050565b6000819050919050565b6101808161016d565b811461018b57600080fd5b50565b60008135905061019d81610177565b92915050565b6000806000806000606086880312156101bf576101be6100fe565b5b600086013567ffffffffffffffff8111156101dd576101dc610103565b5b6101e988828901610117565b9550955050602086013567ffffffffffffffff81111561020c5761020b610103565b5b61021888828901610117565b9350935050604061022b8882890161018e565b9150509295509295909350565b60008115159050919050565b61024d81610238565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561028d578082015181840152602081019050610272565b60008484015250505050565b6000601f19601f8301169050919050565b60006102b582610253565b6102bf818561025e565b93506102cf81856020860161026f565b6102d881610299565b840191505092915050565b60006040820190506102f86000830185610244565b818103602083015261030a81846102aa565b90509392505050565b6000819050919050565b600060ff82169050919050565b6000819050919050565b600061034f61034a61034584610313565b61032a565b61031d565b9050919050565b61035f81610334565b82525050565b82818337600083830152505050565b6000610380838561025e565b935061038d838584610365565b61039683610299565b840190509392505050565b6103aa8161016d565b82525050565b60006080820190506103c56000830189610356565b81810360208301526103d8818789610374565b905081810360408301526103ed818587610374565b90506103fc60608301846103a1565b979650505050505050565b61041081610238565b811461041b57600080fd5b50565b60008151905061042d81610407565b92915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61047082610299565b810181811067ffffffffffffffff8211171561048f5761048e610438565b5b80604052505050565b60006104a26100f4565b90506104ae8282610467565b919050565b600067ffffffffffffffff8211156104ce576104cd610438565b5b6104d782610299565b9050602081019050919050565b60006104f76104f2846104b3565b610498565b90508281526020810184848401111561051357610512610433565b5b61051e84828561026f565b509392505050565b600082601f83011261053b5761053a610108565b5b815161054b8482602086016104e4565b91505092915050565b6000806040838503121561056b5761056a6100fe565b5b60006105798582860161041e565b925050602083015167ffffffffffffffff81111561059a57610599610103565b5b6105a685828601610526565b915050925092905056fea264697066735822122047908cecfa9ace275a4ba96e787bb0d1541ec599c370983693c6cd9c1f5b7dbe64736f6c63430008120033"; + +/* Call EVM Payable: + +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +use ink::env::{DefaultEnvironment, Environment}; +use ink::prelude::vec::Vec; + +#[ink::contract(env = CustomEnvironment)] +mod call_xvm_payable { + use super::*; + + #[ink(storage)] + pub struct CallXvmPayable {} + + impl CallXvmPayable { + #[ink(constructor)] + pub fn new() -> Self { + Self {} + } + + #[ink(message, payable, selector = 42)] + pub fn call_xvm_payable( + &self, + target: Vec, + input: Vec, + ) -> CallResult { + let value = Self::env().transferred_value(); + // Calling EVM + Self::env().extension().call(0x0F, target, input, value) + } + } +} + +pub type CallResult = u32; + +#[ink::chain_extension] +pub trait XvmCall { + type ErrorCode = u32; + + #[ink(extension = 0x00010001, handle_status = false)] + fn call(vm_id: u8, target: Vec, input: Vec, value: u128) -> CallResult; +} + +pub enum CustomEnvironment {} +impl Environment for CustomEnvironment { + const MAX_EVENT_TOPICS: usize = ::MAX_EVENT_TOPICS; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Hash = ::Hash; + type BlockNumber = ::BlockNumber; + type Timestamp = ::Timestamp; + + type ChainExtension = XvmCall; +} + + */ + +#[test] +fn evm_payable_call_via_xvm_works() { + new_test_ext().execute_with(|| { + let evm_payable_addr = deploy_evm_contract(EVM_PAYABLE); + + let value = UNIT; + assert_ok!(Xvm::call( + Context { + source_vm_id: VmId::Wasm, + weight_limit: Weight::from_parts(1_000_000_000, 1024 * 1024), + }, + VmId::Evm, + ALICE, + evm_payable_addr.as_ref().to_vec(), + // Calling `deposit` + hex::decode("d0e30db0").expect("invalid selector hex"), + value, + )); + assert_eq!( + Balances::free_balance(account_id_from(evm_payable_addr)), + value + ); + + assert_ok!(Xvm::call( + Context { + source_vm_id: VmId::Wasm, + weight_limit: Weight::from_parts(10_000_000_000, 1024 * 1024), + }, + VmId::Evm, + ALICE, + evm_payable_addr.as_ref().to_vec(), + // `Calling withdraw` + hex::decode("3ccfd60b").expect("invalid selector hex"), + 0, + )); + assert_eq!( + Balances::free_balance(account_id_from(evm_payable_addr)), + ExistentialDeposit::get(), + ); + }); +} + +#[test] +fn wasm_payable_call_via_xvm_works() { + new_test_ext().execute_with(|| { + let contract_addr = deploy_wasm_contract("payable"); + + let prev_balance = Balances::free_balance(&contract_addr); + let value = UNIT; + assert_ok!(Xvm::call( + Context { + source_vm_id: VmId::Evm, + weight_limit: Weight::from_parts(10_000_000_000, 1024 * 1024), + }, + VmId::Wasm, + ALICE, + MultiAddress::::Id(contract_addr.clone()).encode(), + // Calling `deposit` + hex::decode("0000002a").expect("invalid selector hex"), + value + )); + assert_eq!( + Balances::free_balance(contract_addr.clone()), + value + prev_balance + ); + }); +} + +#[test] +fn calling_wasm_payable_from_evm_fails_if_caller_contract_balance_below_ed() { + new_test_ext().execute_with(|| { + let wasm_payable_addr = deploy_wasm_contract("payable"); + let call_wasm_payable_addr = deploy_evm_contract(CALL_WASM_PAYBLE); + + let value = 1_000_000_000; + assert_ok!(EVM::call( + RuntimeOrigin::root(), + alith(), + call_wasm_payable_addr.clone(), + // to: 0x00a8f69d59df362b69a8d4acdb9001eb3e1b8d067b8fdaa70081aed945bde5c48c + // input: 0x0000002a (deposit) + // value: 1000000000 + hex::decode("4012b914000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000002100a8f69d59df362b69a8d4acdb9001eb3e1b8d067b8fdaa70081aed945bde5c48c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000002a00000000000000000000000000000000000000000000000000000000").expect("invalid call input hex"), + U256::from(value), + 1_000_000, + U256::from(DefaultBaseFeePerGas::get()), + None, + None, + vec![], + )); + + // TODO: after XVM error propagation finished, assert `pallet-evm` execution error + // and update balance assertions. + + // Transfer to EVM contract ok. + assert_eq!( + Balances::free_balance(&account_id_from(call_wasm_payable_addr)), + value, + ); + // Transfer from EVM contract to wasm Contract err. + assert_eq!( + Balances::free_balance(&wasm_payable_addr), + ExistentialDeposit::get(), + ); + }); +} + +#[test] +fn calling_wasm_payable_from_evm_works() { + new_test_ext().execute_with(|| { + let wasm_payable_addr = deploy_wasm_contract("payable"); + let call_wasm_payable_addr = deploy_evm_contract(CALL_WASM_PAYBLE); + + let _ = Balances::deposit_creating(&account_id_from(call_wasm_payable_addr.clone()), ExistentialDeposit::get()); + + let prev_wasm_payable_balance = Balances::free_balance(&wasm_payable_addr); + let value = 1_000_000_000; + assert_ok!(EVM::call( + RuntimeOrigin::root(), + alith(), + call_wasm_payable_addr.clone(), + // to: 0x00a8f69d59df362b69a8d4acdb9001eb3e1b8d067b8fdaa70081aed945bde5c48c + // input: 0x0000002a (deposit) + // value: 1000000000 + hex::decode("4012b914000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000002100a8f69d59df362b69a8d4acdb9001eb3e1b8d067b8fdaa70081aed945bde5c48c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000002a00000000000000000000000000000000000000000000000000000000").expect("invalid call input hex"), + U256::from(value), + 1_000_000, + U256::from(DefaultBaseFeePerGas::get()), + None, + None, + vec![], + )); + let recieved = Balances::free_balance(&wasm_payable_addr) - prev_wasm_payable_balance; + assert_eq!(recieved, value); + }); +} + +#[test] +fn calling_evm_payable_from_wasm_works() { + new_test_ext().execute_with(|| { + let evm_payable_addr = deploy_evm_contract(EVM_PAYABLE); + let wasm_address = deploy_wasm_contract("call_xvm_payable"); + + let value = UNIT; + + // TODO: after Account Unification finished, remove this mock account. + // It is needed now because currently the `AccountMapping` and `AddressMapping` are + // both one way mapping. + let mock_unified_wasm_account = account_id_from(h160_from(wasm_address.clone())); + let _ = Balances::deposit_creating(&mock_unified_wasm_account, value); + + let evm_payable = evm_payable_addr.as_ref().to_vec(); + let deposit_func = hex::decode("d0e30db0").expect("invalid deposit function hex"); + let input = hex::decode("0000002a") + .expect("invalid selector hex") + .iter() + .chain(evm_payable.encode().iter()) + .chain(deposit_func.encode().iter()) + .cloned() + .collect::>(); + assert_ok!(Contracts::call( + RuntimeOrigin::signed(ALICE), + MultiAddress::Id(wasm_address.clone()), + value, + Weight::from_parts(10_000_000_000, 10 * 1024 * 1024), + None, + input, + )); + + assert_eq!( + Balances::free_balance(account_id_from(evm_payable_addr)), + value + ); + + // TODO: after Account Unification finished, enable the wasm address balance check + // and remove the mock account balance check. + // assert_eq!(Balances::free_balance(&wasm_address), ExistentialDeposit::get()); + assert_eq!(Balances::free_balance(&mock_unified_wasm_account), 0); + }); +} From 011567001da909295c21887fa43434d21a5864b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Pa=C4=8Dandi?= <3002868+Dinonard@users.noreply.github.com> Date: Mon, 7 Aug 2023 08:49:45 +0200 Subject: [PATCH 05/11] Temporary fix for rpc port (#994) --- rpc-tests/rpc-tests.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc-tests/rpc-tests.toml b/rpc-tests/rpc-tests.toml index c9e55c1d58..51c3784a34 100644 --- a/rpc-tests/rpc-tests.toml +++ b/rpc-tests/rpc-tests.toml @@ -25,7 +25,7 @@ cumulus_based = true [parachains.collator] name = "astar" command = "./astar-collator" - rpc_port = 9944 + ws_port = 9944 args = [ "-l=xcm=trace", "--enable-evm-rpc" ] [[parachains]] @@ -36,7 +36,7 @@ cumulus_based = true [parachains.collator] name = "shiden" command = "./astar-collator" - rpc_port = 9945 + ws_port = 9945 args = [ "-l=xcm=trace", "--enable-evm-rpc" ] [[hrmp_channels]] From 9946f2018e69e300f3c1595d7292027181c6e0f6 Mon Sep 17 00:00:00 2001 From: bLd759 <18489502+bLd75@users.noreply.github.com> Date: Mon, 7 Aug 2023 16:05:12 +0200 Subject: [PATCH 06/11] Add version to tracing runtimes (#995) --- .github/workflows/release.yml | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fa65561c46..ed3aad9f26 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -467,7 +467,8 @@ jobs: chain=${{ matrix.chain }} runtime_version=$(cat $chain-compressed-info.json | jq '.core_version' | tr -d '"' | cut -d ' ' -f 1) echo $runtime_version - echo "::set-output name=runtime_version::$runtime_version" + echo "runtime=$(echo $runtime_version)" >> $GITHUB_ENV + echo "${{ matrix.chain }}=$(echo $runtime_version)" >> $GITHUB_OUTPUT - name: Upload ${{ matrix.chain }} Wasm uses: actions/upload-release-asset@v1 @@ -476,7 +477,7 @@ jobs: with: upload_url: ${{ needs.publish-release-draft.outputs.upload_url }} asset_path: ${{ matrix.chain }}_runtime.compact.compressed.wasm - asset_name: ${{ steps.get-runtime-version.outputs.runtime_version }}.wasm + asset_name: ${{ env.runtime }}.wasm asset_content_type: application/wasm - name: Upload ${{ matrix.chain }} Metadata @@ -509,8 +510,13 @@ jobs: asset_name: ${{ matrix.chain }}-srtool-digest.json asset_content_type: application/json + outputs: + astar_runtime_version: ${{ steps.get-runtime-version.outputs.astar }} + shiden_runtime_version: ${{ steps.get-runtime-version.outputs.shiden }} + shibuya_runtime_version: ${{ steps.get-runtime-version.outputs.shibuya }} + upload-evm-tracing-artifacts: - needs: publish-release-draft + needs: [publish-release-draft,upload-runtimes] runs-on: ubuntu-latest steps: - name: Download pre-built collator binary for evm tracing @@ -518,11 +524,6 @@ jobs: with: name: astar-evm-tracing-ubuntu-latest-x86_64-unknown-linux-gnu path: evm-tracing-artifacts - - - name: Make evm tracing binary executable and tar gzip - run: | - cd evm-tracing-artifacts - chmod +x astar-collator - name: Download evm tracing runtime uses: actions/download-artifact@v3 @@ -542,6 +543,14 @@ jobs: name: shibuya-evm-tracing-runtime path: evm-tracing-artifacts + - name: Make evm tracing binary executable and rename + run: | + cd evm-tracing-artifacts + chmod +x astar-collator + mv astar_evm_tracing_runtime.compact.compressed.wasm ${{needs.upload-runtimes.outputs.astar_runtime_version}}_evm_tracing_runtime.compact.compressed.wasm + mv shiden_evm_tracing_runtime.compact.compressed.wasm ${{needs.upload-runtimes.outputs.shiden_runtime_version}}_evm_tracing_runtime.compact.compressed.wasm + mv shibuya_evm_tracing_runtime.compact.compressed.wasm ${{needs.upload-runtimes.outputs.shibuya_runtime_version}}_evm_tracing_runtime.compact.compressed.wasm + - name: Compress folder run: | tar zcvf evm-tracing-artifacts.tar.gz evm-tracing-artifacts From 855053ab412cd2fcf04d4af4eea61bed33fe08bc Mon Sep 17 00:00:00 2001 From: Shaun Wang Date: Fri, 11 Aug 2023 20:10:13 +1200 Subject: [PATCH 07/11] pallet-xvm refactor follow-up (#998) * pallet-xvm max weight limit config. * Fix runtime tests. * Forbid XVM re-entrancy. * XVM Re-entrance tests. * Naming consistency. * XVM precompile: respect caller's gas limit setting. * Remove max weight limit config from pallet-xvm. * Remove unused XVM call error. * Add tests for XVM precompile gas limit. --- Cargo.lock | 2 + chain-extensions/types/xvm/src/lib.rs | 5 +- pallets/xvm/Cargo.toml | 2 + pallets/xvm/src/lib.rs | 86 +++++++++++++++++---------- pallets/xvm/src/tests.rs | 4 +- precompiles/utils/src/testing.rs | 5 ++ precompiles/xvm/Cargo.toml | 1 + precompiles/xvm/src/lib.rs | 8 ++- precompiles/xvm/src/mock.rs | 29 ++++++++- precompiles/xvm/src/tests.rs | 51 ++++++++++++++++ primitives/src/xvm.rs | 4 +- runtime/local/src/lib.rs | 5 -- tests/integration/src/xvm.rs | 54 +++++++++++++++-- 13 files changed, 207 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03bb4473f6..cb075119f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7474,6 +7474,7 @@ dependencies = [ "fp-evm", "frame-support", "frame-system", + "hex", "hex-literal", "log", "num_enum 0.5.11", @@ -8241,6 +8242,7 @@ name = "pallet-xvm" version = "0.2.2" dependencies = [ "astar-primitives", + "environmental", "fp-evm", "frame-benchmarking", "frame-support", diff --git a/chain-extensions/types/xvm/src/lib.rs b/chain-extensions/types/xvm/src/lib.rs index c026fc52dc..c42b71c4d5 100644 --- a/chain-extensions/types/xvm/src/lib.rs +++ b/chain-extensions/types/xvm/src/lib.rs @@ -38,10 +38,11 @@ impl From for XvmExecutionResult { // `0` is reserved for `Ok` let error_code = match input { InvalidVmId => 1, - SameVmCallNotAllowed => 2, + SameVmCallDenied => 2, InvalidTarget => 3, InputTooLarge => 4, - ExecutionFailed(_) => 5, + ReentranceDenied => 5, + ExecutionFailed(_) => 6, }; Self::Err(error_code) } diff --git a/pallets/xvm/Cargo.toml b/pallets/xvm/Cargo.toml index 6b83517d55..d9f9bec944 100644 --- a/pallets/xvm/Cargo.toml +++ b/pallets/xvm/Cargo.toml @@ -7,6 +7,7 @@ homepage.workspace = true repository.workspace = true [dependencies] +environmental = { workspace = true } log = { workspace = true } serde = { workspace = true, optional = true } @@ -42,6 +43,7 @@ sp-io = { workspace = true } [features] default = ["std"] std = [ + "environmental/std", "log/std", "parity-scale-codec/std", "frame-support/std", diff --git a/pallets/xvm/src/lib.rs b/pallets/xvm/src/lib.rs index d10d141520..0f15c8b7d8 100644 --- a/pallets/xvm/src/lib.rs +++ b/pallets/xvm/src/lib.rs @@ -37,7 +37,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{ensure, traits::Currency}; +use frame_support::{ensure, traits::Currency, weights::Weight}; use pallet_contracts::{CollectEvents, DebugInfo, Determinism}; use pallet_evm::GasWeightMapping; use parity_scale_codec::Decode; @@ -66,6 +66,8 @@ pub use pallet::*; pub type WeightInfoOf = ::WeightInfo; +environmental::thread_local_impl!(static IN_XVM: environmental::RefCell = environmental::RefCell::new(false)); + #[frame_support::pallet] pub mod pallet { use super::*; @@ -120,25 +122,53 @@ where value: Balance, skip_execution: bool, ) -> CallResult { + let overheads = match vm_id { + VmId::Evm => WeightInfoOf::::evm_call_overheads(), + VmId::Wasm => WeightInfoOf::::wasm_call_overheads(), + }; + ensure!( context.source_vm_id != vm_id, CallErrorWithWeight { - error: CallError::SameVmCallNotAllowed, - used_weight: match vm_id { - VmId::Evm => WeightInfoOf::::evm_call_overheads(), - VmId::Wasm => WeightInfoOf::::wasm_call_overheads(), - }, + error: CallError::SameVmCallDenied, + used_weight: overheads, } ); - match vm_id { - VmId::Evm => { - Pallet::::evm_call(context, source, target, input, value, skip_execution) - } - VmId::Wasm => { - Pallet::::wasm_call(context, source, target, input, value, skip_execution) - } + // Set `IN_XVM` to true & check reentrance. + if IN_XVM.with(|in_xvm| in_xvm.replace(true)) { + return Err(CallErrorWithWeight { + error: CallError::ReentranceDenied, + used_weight: overheads, + }); } + + let res = match vm_id { + VmId::Evm => Pallet::::evm_call( + context, + source, + target, + input, + value, + overheads, + skip_execution, + ), + VmId::Wasm => Pallet::::wasm_call( + context, + source, + target, + input, + value, + overheads, + skip_execution, + ), + }; + + // Set `IN_XVM` to false. + // We should make sure that this line is executed whatever the execution path. + let _ = IN_XVM.with(|in_xvm| in_xvm.take()); + + res } fn evm_call( @@ -147,6 +177,7 @@ where target: Vec, input: Vec, value: Balance, + overheads: Weight, skip_execution: bool, ) -> CallResult { log::trace!( @@ -159,24 +190,22 @@ where target.len() == H160::len_bytes(), CallErrorWithWeight { error: CallError::InvalidTarget, - used_weight: WeightInfoOf::::evm_call_overheads(), + used_weight: overheads, } ); let target_decoded = Decode::decode(&mut target.as_ref()).map_err(|_| CallErrorWithWeight { error: CallError::InvalidTarget, - used_weight: WeightInfoOf::::evm_call_overheads(), + used_weight: overheads, })?; let bounded_input = EthereumTxInput::try_from(input).map_err(|_| CallErrorWithWeight { error: CallError::InputTooLarge, - used_weight: WeightInfoOf::::evm_call_overheads(), + used_weight: overheads, })?; let value_u256 = U256::from(value); // With overheads, less weight is available. - let weight_limit = context - .weight_limit - .saturating_sub(WeightInfoOf::::evm_call_overheads()); + let weight_limit = context.weight_limit.saturating_sub(overheads); let gas_limit = U256::from(T::GasWeightMapping::weight_to_gas(weight_limit)); let source = T::AccountMapping::into_h160(source); @@ -193,7 +222,7 @@ where if skip_execution { return Ok(CallInfo { output: vec![], - used_weight: WeightInfoOf::::evm_call_overheads(), + used_weight: overheads, }); } @@ -208,7 +237,7 @@ where let used_weight = post_dispatch_info .actual_weight .unwrap_or_default() - .saturating_add(WeightInfoOf::::evm_call_overheads()); + .saturating_add(overheads); CallInfo { output: call_info.value, used_weight, @@ -219,7 +248,7 @@ where .post_info .actual_weight .unwrap_or_default() - .saturating_add(WeightInfoOf::::evm_call_overheads()); + .saturating_add(overheads); CallErrorWithWeight { error: CallError::ExecutionFailed(Into::<&str>::into(e.error).into()), used_weight, @@ -233,6 +262,7 @@ where target: Vec, input: Vec, value: Balance, + overheads: Weight, skip_execution: bool, ) -> CallResult { log::trace!( @@ -244,23 +274,21 @@ where let dest = { let error = CallErrorWithWeight { error: CallError::InvalidTarget, - used_weight: WeightInfoOf::::wasm_call_overheads(), + used_weight: overheads, }; let decoded = Decode::decode(&mut target.as_ref()).map_err(|_| error.clone())?; T::Lookup::lookup(decoded).map_err(|_| error) }?; // With overheads, less weight is available. - let weight_limit = context - .weight_limit - .saturating_sub(WeightInfoOf::::wasm_call_overheads()); + let weight_limit = context.weight_limit.saturating_sub(overheads); // Note the skip execution check should be exactly before `pallet_contracts::bare_call` // to benchmark the correct overheads. if skip_execution { return Ok(CallInfo { output: vec![], - used_weight: WeightInfoOf::::wasm_call_overheads(), + used_weight: overheads, }); } @@ -277,9 +305,7 @@ where ); log::trace!(target: "xvm::wasm_call", "WASM call result: {:?}", call_result); - let used_weight = call_result - .gas_consumed - .saturating_add(WeightInfoOf::::wasm_call_overheads()); + let used_weight = call_result.gas_consumed.saturating_add(overheads); match call_result.result { Ok(success) => Ok(CallInfo { output: success.data, diff --git a/pallets/xvm/src/tests.rs b/pallets/xvm/src/tests.rs index aedb8b46d4..ba7b32b585 100644 --- a/pallets/xvm/src/tests.rs +++ b/pallets/xvm/src/tests.rs @@ -49,7 +49,7 @@ fn calling_into_same_vm_is_not_allowed() { value ), CallErrorWithWeight { - error: CallError::SameVmCallNotAllowed, + error: CallError::SameVmCallDenied, used_weight: evm_used_weight }, ); @@ -66,7 +66,7 @@ fn calling_into_same_vm_is_not_allowed() { assert_noop!( Xvm::call(wasm_context, wasm_vm_id, ALICE, wasm_target, input, value), CallErrorWithWeight { - error: CallError::SameVmCallNotAllowed, + error: CallError::SameVmCallDenied, used_weight: wasm_used_weight }, ); diff --git a/precompiles/utils/src/testing.rs b/precompiles/utils/src/testing.rs index 9eb1023a20..721681ec87 100644 --- a/precompiles/utils/src/testing.rs +++ b/precompiles/utils/src/testing.rs @@ -303,6 +303,11 @@ impl<'p, P: PrecompileSet> PrecompilesTester<'p, P> { self } + pub fn with_gas_limit(mut self, gas_limit: u64) -> Self { + self.handle.gas_limit = gas_limit; + self + } + pub fn expect_cost(mut self, cost: u64) -> Self { self.expected_cost = Some(cost); self diff --git a/precompiles/xvm/Cargo.toml b/precompiles/xvm/Cargo.toml index 397a412373..c64e26b3b2 100644 --- a/precompiles/xvm/Cargo.toml +++ b/precompiles/xvm/Cargo.toml @@ -8,6 +8,7 @@ homepage.workspace = true repository.workspace = true [dependencies] +hex = { workspace = true } log = { workspace = true } num_enum = { workspace = true } precompile-utils = { workspace = true } diff --git a/precompiles/xvm/src/lib.rs b/precompiles/xvm/src/lib.rs index 5ccdfcb342..6bb033dc1b 100644 --- a/precompiles/xvm/src/lib.rs +++ b/precompiles/xvm/src/lib.rs @@ -83,8 +83,12 @@ where id.try_into().map_err(|_| revert("invalid vm id")) }?; - let remaining_gas = handle.remaining_gas(); - let weight_limit = R::GasWeightMapping::gas_to_weight(remaining_gas, true); + let mut gas_limit = handle.remaining_gas(); + // If user specified a gas limit, make sure it's not exceeded. + if let Some(user_limit) = handle.gas_limit() { + gas_limit = gas_limit.min(user_limit); + } + let weight_limit = R::GasWeightMapping::gas_to_weight(gas_limit, true); let xvm_context = Context { source_vm_id: VmId::Evm, weight_limit, diff --git a/precompiles/xvm/src/mock.rs b/precompiles/xvm/src/mock.rs index 7115a43467..33fd5d1a78 100644 --- a/precompiles/xvm/src/mock.rs +++ b/precompiles/xvm/src/mock.rs @@ -38,6 +38,7 @@ use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; +use sp_std::cell::RefCell; use astar_primitives::xvm::{CallError::*, CallErrorWithWeight, CallInfo, CallResult}; @@ -236,10 +237,29 @@ impl pallet_evm::Config for Runtime { type GasLimitPovSizeRatio = ConstU64<4>; } +thread_local! { + static WEIGHT_LIMIT: RefCell = RefCell::new(Weight::zero()); +} + +pub(crate) struct WeightLimitCalledWith; +impl WeightLimitCalledWith { + pub(crate) fn get() -> Weight { + WEIGHT_LIMIT.with(|gas_limit| *gas_limit.borrow()) + } + + pub(crate) fn set(value: Weight) { + WEIGHT_LIMIT.with(|gas_limit| *gas_limit.borrow_mut() = value) + } + + pub(crate) fn reset() { + Self::set(Weight::zero()); + } +} + struct MockXvmWithArgsCheck; impl XvmCall for MockXvmWithArgsCheck { fn call( - _context: Context, + context: Context, vm_id: VmId, _source: AccountId, target: Vec, @@ -249,7 +269,7 @@ impl XvmCall for MockXvmWithArgsCheck { ensure!( vm_id != VmId::Evm, CallErrorWithWeight { - error: SameVmCallNotAllowed, + error: SameVmCallDenied, used_weight: Weight::zero() } ); @@ -267,6 +287,9 @@ impl XvmCall for MockXvmWithArgsCheck { used_weight: Weight::zero() } ); + + WeightLimitCalledWith::set(context.weight_limit); + Ok(CallInfo { output: vec![], used_weight: Weight::zero(), @@ -297,6 +320,8 @@ impl ExtBuilder { .build_storage::() .expect("Frame system builds valid default genesis config"); + WeightLimitCalledWith::reset(); + let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| System::set_block_number(1)); ext diff --git a/precompiles/xvm/src/tests.rs b/precompiles/xvm/src/tests.rs index dc09f55f99..37bbc56cf1 100644 --- a/precompiles/xvm/src/tests.rs +++ b/precompiles/xvm/src/tests.rs @@ -82,3 +82,54 @@ fn correct_arguments_works() { ); }) } + +#[test] +fn weight_limit_is_min_of_remaining_and_user_limit() { + ExtBuilder::default().build().execute_with(|| { + // The caller didn't set a limit. + precompiles() + .prepare_test( + TestAccount::Alice, + PRECOMPILE_ADDRESS, + EvmDataWriter::new_with_selector(Action::XvmCall) + .write(0x1Fu8) + .write(Bytes( + hex::decode("0000000000000000000000000000000000000000") + .expect("invalid hex"), + )) + .write(Bytes(b"".to_vec())) + .write(U256::one()) + .build(), + ) + .expect_no_logs() + .execute_some(); + assert_eq!( + WeightLimitCalledWith::get(), + ::GasWeightMapping::gas_to_weight(u64::MAX, true) + ); + + // The caller set a limit. + let gas_limit = 1_000; + precompiles() + .prepare_test( + TestAccount::Alice, + PRECOMPILE_ADDRESS, + EvmDataWriter::new_with_selector(Action::XvmCall) + .write(0x1Fu8) + .write(Bytes( + hex::decode("0000000000000000000000000000000000000000") + .expect("invalid hex"), + )) + .write(Bytes(b"".to_vec())) + .write(U256::one()) + .build(), + ) + .with_gas_limit(gas_limit) + .expect_no_logs() + .execute_some(); + assert_eq!( + WeightLimitCalledWith::get(), + ::GasWeightMapping::gas_to_weight(gas_limit, true) + ); + }); +} diff --git a/primitives/src/xvm.rs b/primitives/src/xvm.rs index 38de257e27..4b08aa6967 100644 --- a/primitives/src/xvm.rs +++ b/primitives/src/xvm.rs @@ -61,11 +61,13 @@ pub enum CallError { /// Invalid VM id. InvalidVmId, /// Calling the contracts in the same VM is not allowed. - SameVmCallNotAllowed, + SameVmCallDenied, /// Target contract address is invalid. InvalidTarget, /// Input is too large. InputTooLarge, + /// Reentrance is not allowed. + ReentranceDenied, /// The call failed on EVM or WASM execution. ExecutionFailed(Vec), } diff --git a/runtime/local/src/lib.rs b/runtime/local/src/lib.rs index 083839ba66..cb90692dab 100644 --- a/runtime/local/src/lib.rs +++ b/runtime/local/src/lib.rs @@ -467,11 +467,6 @@ impl pallet_ethereum_checked::Config for Runtime { type WeightInfo = pallet_ethereum_checked::weights::SubstrateWeight; } -parameter_types! { - pub EvmId: u8 = 0x0F; - pub WasmId: u8 = 0x1F; -} - impl pallet_xvm::Config for Runtime { type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type AccountMapping = HashedAccountMapping; diff --git a/tests/integration/src/xvm.rs b/tests/integration/src/xvm.rs index a92ff4c382..fb3ba9b72b 100644 --- a/tests/integration/src/xvm.rs +++ b/tests/integration/src/xvm.rs @@ -69,6 +69,7 @@ mod payable { } */ +const WASM_PAYABLE_NAME: &'static str = "payable"; /* Call WASM payable: @@ -151,6 +152,7 @@ impl Environment for CustomEnvironment { } */ +const CALL_EVM_PAYBLE_NAME: &'static str = "call_xvm_payable"; #[test] fn evm_payable_call_via_xvm_works() { @@ -197,7 +199,7 @@ fn evm_payable_call_via_xvm_works() { #[test] fn wasm_payable_call_via_xvm_works() { new_test_ext().execute_with(|| { - let contract_addr = deploy_wasm_contract("payable"); + let contract_addr = deploy_wasm_contract(WASM_PAYABLE_NAME); let prev_balance = Balances::free_balance(&contract_addr); let value = UNIT; @@ -223,7 +225,7 @@ fn wasm_payable_call_via_xvm_works() { #[test] fn calling_wasm_payable_from_evm_fails_if_caller_contract_balance_below_ed() { new_test_ext().execute_with(|| { - let wasm_payable_addr = deploy_wasm_contract("payable"); + let wasm_payable_addr = deploy_wasm_contract(WASM_PAYABLE_NAME); let call_wasm_payable_addr = deploy_evm_contract(CALL_WASM_PAYBLE); let value = 1_000_000_000; @@ -262,7 +264,7 @@ fn calling_wasm_payable_from_evm_fails_if_caller_contract_balance_below_ed() { #[test] fn calling_wasm_payable_from_evm_works() { new_test_ext().execute_with(|| { - let wasm_payable_addr = deploy_wasm_contract("payable"); + let wasm_payable_addr = deploy_wasm_contract(WASM_PAYABLE_NAME); let call_wasm_payable_addr = deploy_evm_contract(CALL_WASM_PAYBLE); let _ = Balances::deposit_creating(&account_id_from(call_wasm_payable_addr.clone()), ExistentialDeposit::get()); @@ -293,7 +295,7 @@ fn calling_wasm_payable_from_evm_works() { fn calling_evm_payable_from_wasm_works() { new_test_ext().execute_with(|| { let evm_payable_addr = deploy_evm_contract(EVM_PAYABLE); - let wasm_address = deploy_wasm_contract("call_xvm_payable"); + let wasm_address = deploy_wasm_contract(CALL_EVM_PAYBLE_NAME); let value = UNIT; @@ -316,7 +318,7 @@ fn calling_evm_payable_from_wasm_works() { RuntimeOrigin::signed(ALICE), MultiAddress::Id(wasm_address.clone()), value, - Weight::from_parts(10_000_000_000, 10 * 1024 * 1024), + Weight::from_parts(10_000_000_000, 1024 * 1024), None, input, )); @@ -332,3 +334,45 @@ fn calling_evm_payable_from_wasm_works() { assert_eq!(Balances::free_balance(&mock_unified_wasm_account), 0); }); } + +#[test] +fn reentrance_not_allowed() { + new_test_ext().execute_with(|| { + // Call path: WASM -> EVM -> WASM + let call_evm_payable_address = deploy_wasm_contract(CALL_EVM_PAYBLE_NAME); + let call_wasm_payable_addr = deploy_evm_contract(CALL_WASM_PAYBLE); + let _ = deploy_wasm_contract(WASM_PAYABLE_NAME); + + // to: 0x00a8f69d59df362b69a8d4acdb9001eb3e1b8d067b8fdaa70081aed945bde5c48c + // input: 0x0000002a (deposit) + // value: 1000000000 + let call_wasm_payable_input = hex::decode("4012b914000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000002100a8f69d59df362b69a8d4acdb9001eb3e1b8d067b8fdaa70081aed945bde5c48c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000002a00000000000000000000000000000000000000000000000000000000").expect("invalid call input hex"); + let input = hex::decode("0000002a") + .expect("invalid selector hex") + .iter() + .chain(call_wasm_payable_addr.as_ref().to_vec().encode().iter()) + .chain(call_wasm_payable_input.encode().iter()) + .cloned() + .collect::>(); + assert_ok!( + Contracts::call( + RuntimeOrigin::signed(ALICE), + MultiAddress::Id(call_evm_payable_address.clone()), + 0, + Weight::from_parts(10_000_000_000, 1024 * 1024), + None, + input, + ) + ); + + // TODO: after XVM error propagation finished, replace with assert `ReentranceDenied` + // error. + let wasm_entrance_count = System::events().iter().filter(|record| { + match record.event { + RuntimeEvent::Contracts(pallet_contracts::Event::Called { .. }) => true, + _ => false, + } + }).count(); + assert_eq!(wasm_entrance_count, 1); + }); +} From f577d2cda90fc05e986d8c983ab649604a0f2310 Mon Sep 17 00:00:00 2001 From: Deepanshu Hooda <43631678+gitofdeepanshu@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:07:40 +0530 Subject: [PATCH 08/11] feat: add batch precompile (#989) * feat: add batch precompile * typo * add tests * minor fixes and updates * refactor, add batch to local * fix expect_arguments * add logs * update license * taplo fix * more refactor, remote evm crate * remove evm crate, update utils version, add std in dappsstaking * update pallet_balances::config in mock * update pallet_evm config * update license * add to std-feature * utils: rectify patch to minor update * remove pub from logs function * fix typos * remove comments * refactor * fmt --- Cargo.lock | 36 +- Cargo.toml | 2 + precompiles/batch/Batch.sol | 77 +++ precompiles/batch/Cargo.toml | 57 ++ precompiles/batch/src/lib.rs | 337 +++++++++++ precompiles/batch/src/mock.rs | 225 ++++++++ precompiles/batch/src/tests.rs | 638 +++++++++++++++++++++ precompiles/dapps-staking/Cargo.toml | 2 +- precompiles/utils/Cargo.toml | 12 +- precompiles/utils/src/bytes.rs | 218 +++++++ precompiles/utils/src/data.rs | 129 ++++- precompiles/utils/src/lib.rs | 68 ++- precompiles/utils/src/testing.rs | 448 --------------- precompiles/utils/src/testing/account.rs | 186 ++++++ precompiles/utils/src/testing/execution.rs | 262 +++++++++ precompiles/utils/src/testing/handle.rs | 220 +++++++ precompiles/utils/src/testing/mod.rs | 98 ++++ runtime/local/Cargo.toml | 2 + runtime/local/src/precompiles.rs | 13 +- runtime/shibuya/Cargo.toml | 2 + runtime/shibuya/src/precompiles.rs | 7 +- 21 files changed, 2578 insertions(+), 461 deletions(-) create mode 100644 precompiles/batch/Batch.sol create mode 100644 precompiles/batch/Cargo.toml create mode 100644 precompiles/batch/src/lib.rs create mode 100644 precompiles/batch/src/mock.rs create mode 100644 precompiles/batch/src/tests.rs create mode 100644 precompiles/utils/src/bytes.rs delete mode 100644 precompiles/utils/src/testing.rs create mode 100644 precompiles/utils/src/testing/account.rs create mode 100644 precompiles/utils/src/testing/execution.rs create mode 100644 precompiles/utils/src/testing/handle.rs create mode 100644 precompiles/utils/src/testing/mod.rs diff --git a/Cargo.lock b/Cargo.lock index cb075119f4..9aa74cd3fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5517,6 +5517,7 @@ dependencies = [ "pallet-ethereum-checked", "pallet-evm", "pallet-evm-precompile-assets-erc20", + "pallet-evm-precompile-batch", "pallet-evm-precompile-blake2", "pallet-evm-precompile-bn128", "pallet-evm-precompile-dapps-staking", @@ -7295,6 +7296,34 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-evm-precompile-batch" +version = "0.1.0" +dependencies = [ + "derive_more", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "hex-literal", + "log", + "num_enum 0.5.11", + "pallet-balances", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "paste", + "precompile-utils", + "scale-info", + "serde", + "sha3", + "slices", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-evm-precompile-blake2" version = "2.0.0-dev" @@ -9815,9 +9844,11 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "precompile-utils" -version = "0.4.3" +version = "0.5.0" dependencies = [ "assert_matches", + "derive_more", + "environmental", "evm", "fp-evm", "frame-support", @@ -9829,6 +9860,8 @@ dependencies = [ "pallet-evm", "parity-scale-codec", "precompile-utils-macro", + "scale-info", + "serde", "sha3", "similar-asserts", "sp-core", @@ -12359,6 +12392,7 @@ dependencies = [ "pallet-evm", "pallet-evm-chain-id", "pallet-evm-precompile-assets-erc20", + "pallet-evm-precompile-batch", "pallet-evm-precompile-blake2", "pallet-evm-precompile-bn128", "pallet-evm-precompile-dapps-staking", diff --git a/Cargo.toml b/Cargo.toml index 566fadbe41..6e3948a47f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,7 @@ num-traits = { version = "0.2", default-features = false } rand = { version = "0.8.5", default-features = false } bounded-collections = { version = "0.1.5", default-features = false } hex = { version = "0.4.3", default-features = false } +paste = "1.0.6" # (native) array-bytes = "6.0.0" @@ -285,6 +286,7 @@ pallet-evm-precompile-substrate-ecdsa = { path = "./precompiles/substrate-ecdsa" pallet-evm-precompile-xcm = { path = "./precompiles/xcm", default-features = false } pallet-evm-precompile-xvm = { path = "./precompiles/xvm", default-features = false } pallet-evm-precompile-dapps-staking = { path = "./precompiles/dapps-staking", default-features = false } +pallet-evm-precompile-batch = { path = "./precompiles/batch", default-features = false } pallet-chain-extension-dapps-staking = { path = "./chain-extensions/dapps-staking", default-features = false } pallet-chain-extension-xvm = { path = "./chain-extensions/xvm", default-features = false } diff --git a/precompiles/batch/Batch.sol b/precompiles/batch/Batch.sol new file mode 100644 index 0000000000..b2884de6d3 --- /dev/null +++ b/precompiles/batch/Batch.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.3; + +/// Interface to the precompiled contract on Shibuya/Shiden/Astar +/// Predeployed at the address 0x0000000000000000000000000000000000005006 +/// For better understanding check the source code: +/// repo: https://github.com/AstarNetwork/astar + +/// @title Batch precompile +/// @dev Allows to perform multiple calls through one call to the precompile. +/// Can be used by EOA to do multiple calls in a single transaction. +interface Batch { + /// @dev Batch multiple calls into a single transaction. + /// All calls are performed from the address calling this precompile. + /// + /// In case of one subcall reverting following subcalls will still be attempted. + /// + /// @param to List of addresses to call. + /// @param value List of values for each subcall. If array is shorter than `to` then additional + /// calls will be performed with a value of 0. + /// @param callData Call data for each `to` address. If array is shorter than `to` then + /// additional calls will be performed with an empty call data. + /// @param gasLimit Gas limit for each `to` address. Use 0 to forward all the remaining gas. + /// If array is shorter than `to` then the remaining gas available will be used. + function batchSome( + address[] memory to, + uint256[] memory value, + bytes[] memory callData, + uint64[] memory gasLimit + ) external; + + /// @dev Batch multiple calls into a single transaction. + /// All calls are performed from the address calling this precompile. + /// + /// In case of one subcall reverting, no more subcalls will be executed but + /// the batch transaction will succeed. Use "batchAll" to revert on any subcall revert. + /// + /// @param to List of addresses to call. + /// @param value List of values for each subcall. If array is shorter than `to` then additional + /// calls will be performed with a value of 0. + /// @param callData Call data for each `to` address. If array is shorter than `to` then + /// additional calls will be performed with an empty call data. + /// @param gasLimit Gas limit for each `to` address. Use 0 to forward all the remaining gas. + /// If array is shorter than `to` then the remaining gas available will be used. + function batchSomeUntilFailure( + address[] memory to, + uint256[] memory value, + bytes[] memory callData, + uint64[] memory gasLimit + ) external; + + /// @dev Batch multiple calls into a single transaction. + /// All calls are performed from the address calling this precompile. + /// + /// In case of one subcall reverting, the entire batch will revert. + /// + /// @param to List of addresses to call. + /// @param value List of values for each subcall. If array is shorter than `to` then additional + /// calls will be performed with a value of 0. + /// @param callData Call data for each `to` address. If array is shorter than `to` then + /// additional calls will be performed with an empty call data. + /// @param gasLimit Gas limit for each `to` address. Use 0 to forward all the remaining gas. + /// If array is shorter than `to` then the remaining gas available will be used. + function batchAll( + address[] memory to, + uint256[] memory value, + bytes[] memory callData, + uint64[] memory gasLimit + ) external; + + /// Emitted when a subcall succeeds. + event SubcallSucceeded(uint256 index); + + /// Emitted when a subcall fails. + event SubcallFailed(uint256 index); + +} diff --git a/precompiles/batch/Cargo.toml b/precompiles/batch/Cargo.toml new file mode 100644 index 0000000000..56a366b43e --- /dev/null +++ b/precompiles/batch/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "pallet-evm-precompile-batch" +description = "A Precompile to batch multiple calls." +version = "0.1.0" +authors = ["StakeTechnologies", "PureStake"] +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +log = { workspace = true } +num_enum = { workspace = true } +paste = { workspace = true } +slices = { workspace = true } + +# Moonbeam +precompile-utils = { workspace = true } + +# Substrate +frame-support = { workspace = true } +frame-system = { workspace = true } +parity-scale-codec = { workspace = true, features = ["max-encoded-len"] } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-std = { workspace = true } + +# Frontier +evm = { workspace = true, features = ["with-codec"] } +fp-evm = { workspace = true } +pallet-evm = { workspace = true } + +[dev-dependencies] +derive_more = { workspace = true } +hex-literal = { workspace = true } +serde = { workspace = true } +sha3 = { workspace = true } + +pallet-balances = { workspace = true, features = ["std"] } +pallet-timestamp = { workspace = true, features = ["std"] } +parity-scale-codec = { workspace = true, features = ["max-encoded-len", "std"] } +precompile-utils = { workspace = true, features = ["std", "testing"] } +scale-info = { workspace = true, features = ["derive", "std"] } +sp-runtime = { workspace = true, features = ["std"] } + +[features] +default = ["std"] +std = [ + "fp-evm/std", + "frame-support/std", + "frame-system/std", + "pallet-evm/std", + "parity-scale-codec/std", + "precompile-utils/std", + "sp-core/std", + "sp-io/std", + "sp-std/std", +] diff --git a/precompiles/batch/src/lib.rs b/precompiles/batch/src/lib.rs new file mode 100644 index 0000000000..029c2e09a1 --- /dev/null +++ b/precompiles/batch/src/lib.rs @@ -0,0 +1,337 @@ +// 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 . + +// Copyright 2019-2022 PureStake Inc. +// Copyright 2022 Stake Technologies +// This file is part of pallet-evm-precompile-batch package, originally developed by Purestake Inc. +// pallet-evm-precompile-batch package used in Astar Network in terms of GPLv3. +// +// pallet-evm-precompile-batch 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. + +// pallet-evm-precompile-batch 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 pallet-evm-precompile-batch. If not, see . +#![cfg_attr(not(feature = "std"), no_std)] + +use ::evm::{ExitError, ExitReason}; +use fp_evm::{Context, Log, PrecompileFailure, PrecompileHandle, Transfer}; +use frame_support::{ + dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, + traits::ConstU32, +}; +use pallet_evm::{Precompile, PrecompileOutput}; +use precompile_utils::{bytes::BoundedBytes, data::BoundedVec, *}; +use sp_core::{H160, U256}; +use sp_std::{iter::repeat, marker::PhantomData, vec, vec::Vec}; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Mode { + BatchSome, // = "batchSome(address[],uint256[],bytes[],uint64[])", + BatchSomeUntilFailure, // = "batchSomeUntilFailure(address[],uint256[],bytes[],uint64[])", + BatchAll, // = "batchAll(address[],uint256[],bytes[],uint64[])", +} + +pub const LOG_SUBCALL_SUCCEEDED: [u8; 32] = keccak256!("SubcallSucceeded(uint256)"); +pub const LOG_SUBCALL_FAILED: [u8; 32] = keccak256!("SubcallFailed(uint256)"); +pub const CALL_DATA_LIMIT: u32 = 2u32.pow(16); +pub const ARRAY_LIMIT: u32 = 2u32.pow(9); + +type GetCallDataLimit = ConstU32; +type GetArrayLimit = ConstU32; + +fn log_subcall_succeeded(address: impl Into, index: usize) -> Log { + LogsBuilder::new(address.into()).log1( + LOG_SUBCALL_SUCCEEDED, + data::encode_event_data(U256::from(index)), + ) +} + +fn log_subcall_failed(address: impl Into, index: usize) -> Log { + LogsBuilder::new(address.into()).log1( + LOG_SUBCALL_FAILED, + data::encode_event_data(U256::from(index)), + ) +} + +#[precompile_utils::generate_function_selector] +#[derive(Debug, PartialEq)] +pub enum Action { + BatchSome = "batchSome(address[],uint256[],bytes[],uint64[])", + BatchSomeUntilFailure = "batchSomeUntilFailure(address[],uint256[],bytes[],uint64[])", + BatchAll = "batchAll(address[],uint256[],bytes[],uint64[])", +} + +/// Batch precompile. +#[derive(Debug, Clone)] +pub struct BatchPrecompile(PhantomData); + +impl Precompile for BatchPrecompile +where + Runtime: pallet_evm::Config, + Runtime::RuntimeCall: Dispatchable + GetDispatchInfo, +{ + fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { + let selector = handle.read_selector()?; + + handle.check_function_modifier(FunctionModifier::NonPayable)?; + match selector { + Action::BatchSome => Self::batch_some(handle), + Action::BatchAll => Self::batch_all(handle), + Action::BatchSomeUntilFailure => Self::batch_some_until_failure(handle), + } + } +} +// No funds are transfered to the precompile address. +// Transfers will directly be made on the behalf of the user by the precompile. +// #[precompile_utils::precompile] +impl BatchPrecompile +where + Runtime: pallet_evm::Config, + Runtime::RuntimeCall: Dispatchable + GetDispatchInfo, +{ + fn batch_some(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; + input.expect_arguments(4)?; + let to = input.read::>()?; + let value = input.read::>()?; + let call_data = + input.read::, GetArrayLimit>>()?; + let gas_limit = input.read::>()?; + log::trace!(target: "batch-precompile", "batch_some\n to address(s) {:?}, value(s) {:?} call_data(s) {:?}, gas_limit(s) {:?}", to, value,call_data, gas_limit); + Self::inner_batch(Mode::BatchSome, handle, to, value, call_data, gas_limit) + } + + fn batch_some_until_failure(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; + input.expect_arguments(4)?; + let to = input.read::>()?; + let value = input.read::>()?; + let call_data = + input.read::, GetArrayLimit>>()?; + let gas_limit = input.read::>()?; + log::trace!(target: "batch-precompile", "batch_some_until_failure\n to address(s) {:?}, value(s) {:?} call_data(s) {:?}, gas_limit(s) {:?}", to, value,call_data, gas_limit); + Self::inner_batch( + Mode::BatchSomeUntilFailure, + handle, + to, + value, + call_data, + gas_limit, + ) + } + + fn batch_all(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; + input.expect_arguments(4)?; + let to = input.read::>()?; + let value = input.read::>()?; + let call_data = + input.read::, GetArrayLimit>>()?; + let gas_limit = input.read::>()?; + log::trace!(target: "batch-precompile", "batch_all\n to address(s) {:?}, value(s) {:?} call_data(s) {:?}, gas_limit(s) {:?}", to, value,call_data, gas_limit); + Self::inner_batch(Mode::BatchAll, handle, to, value, call_data, gas_limit) + } + + fn inner_batch( + mode: Mode, + handle: &mut impl PrecompileHandle, + to: BoundedVec, + value: BoundedVec, + call_data: BoundedVec, GetArrayLimit>, + gas_limit: BoundedVec, + ) -> EvmResult { + let addresses = Vec::from(to).into_iter().enumerate(); + let values = Vec::from(value) + .into_iter() + .map(|x| Some(x)) + .chain(repeat(None)); + let calls_data = Vec::from(call_data) + .into_iter() + .map(|x| Some(x.into())) + .chain(repeat(None)); + let gas_limits = Vec::from(gas_limit).into_iter().map(|x| + // x = 0 => forward all remaining gas + if x == 0 { + None + } else { + Some(x) + } + ).chain(repeat(None)); + + // Cost of batch log. (doesn't change when index changes) + let log_cost = log_subcall_failed(handle.code_address(), 0) + .compute_cost() + .map_err(|_| revert("Failed to compute log cost"))?; + + for ((i, address), (value, (call_data, gas_limit))) in + addresses.zip(values.zip(calls_data.zip(gas_limits))) + { + let address = address.0; + let value = value.unwrap_or(U256::zero()); + let call_data = call_data.unwrap_or(vec![]); + + let sub_context = Context { + caller: handle.context().caller, + address: address.clone(), + apparent_value: value, + }; + + let transfer = if value.is_zero() { + None + } else { + Some(Transfer { + source: handle.context().caller, + target: address.clone(), + value, + }) + }; + + // We reserve enough gas to emit a final log and perform the subcall itself. + // If not enough gas we stop there according to Mode strategy. + let remaining_gas = handle.remaining_gas(); + + let forwarded_gas = match (remaining_gas.checked_sub(log_cost), mode) { + (Some(remaining), _) => remaining, + (None, Mode::BatchAll) => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }) + } + (None, _) => { + return Ok(succeed(EvmDataWriter::new().write(true).build())); + } + }; + + // Cost of the call itself that the batch precompile must pay. + let call_cost = call_cost(value, ::config()); + + let forwarded_gas = match forwarded_gas.checked_sub(call_cost) { + Some(remaining) => remaining, + None => { + let log = log_subcall_failed(handle.code_address(), i); + handle.record_log_costs(&[&log])?; + log.record(handle)?; + + match mode { + Mode::BatchAll => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }) + } + Mode::BatchSomeUntilFailure => { + return Ok(succeed(EvmDataWriter::new().write(true).build())) + } + Mode::BatchSome => continue, + } + } + }; + + // If there is a provided gas limit we ensure there is enough gas remaining. + let forwarded_gas = match gas_limit { + None => forwarded_gas, // provide all gas if no gas limit, + Some(limit) => { + if limit > forwarded_gas { + let log = log_subcall_failed(handle.code_address(), i); + handle.record_log_costs(&[&log])?; + log.record(handle)?; + + match mode { + Mode::BatchAll => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }) + } + Mode::BatchSomeUntilFailure => { + return Ok(succeed(EvmDataWriter::new().write(true).build())) + } + Mode::BatchSome => continue, + } + } + limit + } + }; + + let (reason, output) = handle.call( + address, + transfer, + call_data, + Some(forwarded_gas), + false, + &sub_context, + ); + + // Logs + // We reserved enough gas so this should not OOG. + match reason { + ExitReason::Revert(_) | ExitReason::Error(_) => { + let log = log_subcall_failed(handle.code_address(), i); + handle.record_log_costs(&[&log])?; + log.record(handle)? + } + ExitReason::Succeed(_) => { + let log = log_subcall_succeeded(handle.code_address(), i); + handle.record_log_costs(&[&log])?; + log.record(handle)? + } + _ => (), + } + + // How to proceed + match (mode, reason) { + // _: Fatal is always fatal + (_, ExitReason::Fatal(exit_status)) => { + return Err(PrecompileFailure::Fatal { exit_status }) + } + + // BatchAll : Reverts and errors are immediatly forwarded. + (Mode::BatchAll, ExitReason::Revert(exit_status)) => { + return Err(PrecompileFailure::Revert { + exit_status, + output, + }) + } + (Mode::BatchAll, ExitReason::Error(exit_status)) => { + return Err(PrecompileFailure::Error { exit_status }) + } + + // BatchSomeUntilFailure : Reverts and errors prevent subsequent subcalls to + // be executed but the precompile still succeed. + (Mode::BatchSomeUntilFailure, ExitReason::Revert(_) | ExitReason::Error(_)) => { + return Ok(succeed(EvmDataWriter::new().write(true).build())) + } + + // Success or ignored revert/error. + (_, _) => (), + } + } + + Ok(succeed(EvmDataWriter::new().write(true).build())) + } +} diff --git a/precompiles/batch/src/mock.rs b/precompiles/batch/src/mock.rs new file mode 100644 index 0000000000..4ac61de881 --- /dev/null +++ b/precompiles/batch/src/mock.rs @@ -0,0 +1,225 @@ +// 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 . + +// Copyright 2019-2022 PureStake Inc. +// Copyright 2022 Stake Technologies +// This file is part of pallet-evm-precompile-batch package, originally developed by Purestake Inc. +// pallet-evm-precompile-batch package used in Astar Network in terms of GPLv3. +// +// pallet-evm-precompile-batch 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. + +// pallet-evm-precompile-batch 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 pallet-evm-precompile-batch. If not, see . + +//! Test utilities +use super::*; + +use fp_evm::IsPrecompileResult; +use frame_support::traits::{ConstU64, Everything}; +use frame_support::{construct_runtime, parameter_types, weights::Weight}; +use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, PrecompileResult, PrecompileSet}; +use precompile_utils::{mock_account, testing::MockAccount}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + Perbill, +}; + +pub type AccountId = MockAccount; +pub type Balance = u128; +pub type BlockNumber = u32; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Evm: pallet_evm::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + } +); + +parameter_types! { + pub const BlockHashCount: u32 = 250; + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + pub const SS58Prefix: u8 = 42; +} + +impl frame_system::Config for Runtime { + type BaseCallFilter = Everything; + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = sp_runtime::generic::Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} +parameter_types! { + pub const ExistentialDeposit: u128 = 1; +} +impl pallet_balances::Config for Runtime { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = (); + type MaxFreezes = (); +} + +pub fn precompile_address() -> H160 { + H160::from_low_u64_be(0x5002) +} + +#[derive(Debug, Clone, Copy)] +pub struct BatchPrecompileMock(PhantomData); + +impl PrecompileSet for BatchPrecompileMock +where + R: pallet_evm::Config, + BatchPrecompile: Precompile, +{ + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + match handle.code_address() { + a if a == precompile_address() => Some(BatchPrecompile::::execute(handle)), + _ => None, + } + } + + fn is_precompile(&self, address: sp_core::H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: address == precompile_address(), + extra_cost: 0, + } + } +} + +mock_account!(Revert, |_| MockAccount::from_u64(2)); + +parameter_types! { + pub BlockGasLimit: U256 = U256::max_value(); + pub PrecompilesValue: BatchPrecompileMock = BatchPrecompileMock(Default::default()); + pub const WeightPerGas: Weight = Weight::from_parts(1, 0); +} + +impl pallet_evm::Config for Runtime { + type FeeCalculator = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = AccountId; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type Runner = pallet_evm::runner::stack::Runner; + type PrecompilesType = BatchPrecompileMock; + type PrecompilesValue = PrecompilesValue; + type Timestamp = Timestamp; + type ChainId = (); + type OnChargeTransaction = (); + type BlockGasLimit = BlockGasLimit; + type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; + type FindAuthor = (); + type OnCreate = (); + type WeightInfo = (); + type GasLimitPovSizeRatio = ConstU64<4>; +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +pub(crate) struct ExtBuilder { + // endowed accounts with balances + balances: Vec<(AccountId, Balance)>, +} + +impl Default for ExtBuilder { + fn default() -> ExtBuilder { + ExtBuilder { balances: vec![] } + } +} + +impl ExtBuilder { + pub(crate) fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .expect("Frame system builds valid default genesis config"); + + pallet_balances::GenesisConfig:: { + balances: self.balances, + } + .assimilate_storage(&mut t) + .expect("Pallet balances storage can be assimilated"); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + pallet_evm::Pallet::::create_account( + Revert.into(), + hex_literal::hex!("1460006000fd").to_vec(), + ); + }); + ext + } +} diff --git a/precompiles/batch/src/tests.rs b/precompiles/batch/src/tests.rs new file mode 100644 index 0000000000..55241fe08a --- /dev/null +++ b/precompiles/batch/src/tests.rs @@ -0,0 +1,638 @@ +// 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 . + +// Copyright 2019-2022 PureStake Inc. +// Copyright 2022 Stake Technologies +// This file is part of pallet-evm-precompile-batch package, originally developed by Purestake Inc. +// pallet-evm-precompile-batch package used in Astar Network in terms of GPLv3. +// +// pallet-evm-precompile-batch 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. + +// pallet-evm-precompile-batch 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 pallet-evm-precompile-batch. If not, see . + +use crate::mock::{precompile_address, BatchPrecompileMock, ExtBuilder, PrecompilesValue, Runtime}; +use crate::{log_subcall_failed, log_subcall_succeeded, Mode, *}; +use fp_evm::ExitError; +use precompile_utils::{call_cost, testing::*, LogsBuilder}; +use sp_core::{H256, U256}; + +fn precompiles() -> BatchPrecompileMock { + PrecompilesValue::get() +} + +fn costs() -> (u64, u64) { + let return_log_cost = log_subcall_failed(precompile_address(), 0) + .compute_cost() + .unwrap(); + let call_cost = + return_log_cost + call_cost(U256::one(), ::config()); + (return_log_cost, call_cost) +} + +#[test] +fn batch_some_empty() { + ExtBuilder::default().build().execute_with(|| { + precompiles() + .prepare_test( + Alice, + precompile_address(), + EvmDataWriter::new_with_selector(Action::BatchSome) + .write::>(vec![]) + .write::>(vec![]) + .write::>(vec![]) + .write::>(vec![]) + .build(), + ) + .execute_returns(EvmDataWriter::new().write(true).build()); + }) +} + +#[test] +fn batch_some_until_failure_empty() { + ExtBuilder::default().build().execute_with(|| { + precompiles() + .prepare_test( + Alice, + precompile_address(), + EvmDataWriter::new_with_selector(Action::BatchSomeUntilFailure) + .write::>(vec![]) + .write::>(vec![]) + .write::>(vec![]) + .write::>(vec![]) + .build(), + ) + .execute_returns(EvmDataWriter::new().write(true).build()); + }) +} + +#[test] +fn batch_all_empty() { + ExtBuilder::default().build().execute_with(|| { + precompiles() + .prepare_test( + Alice, + precompile_address(), + EvmDataWriter::new_with_selector(Action::BatchAll) + .write::>(vec![]) + .write::>(vec![]) + .write::>(vec![]) + .write::>(vec![]) + .build(), + ) + .execute_returns(EvmDataWriter::new().write(true).build()); + }) +} + +fn check_mode(mode: Mode) -> Action { + match mode { + Mode::BatchAll => Action::BatchAll, + Mode::BatchSome => Action::BatchSome, + Mode::BatchSomeUntilFailure => Action::BatchSomeUntilFailure, + } +} + +fn batch_returns( + precompiles: &BatchPrecompileMock, + mode: Mode, +) -> PrecompilesTester> { + let mut counter = 0; + let one = b"one"; + let two = b"two"; + let (_, total_call_cost) = costs(); + + precompiles + .prepare_test( + Alice, + precompile_address(), + EvmDataWriter::new_with_selector(check_mode(mode)) + .write(vec![Address(Bob.into()), Address(Charlie.into())]) + .write(vec![U256::from(1u8), U256::from(2u8)]) + .write(vec![Bytes::from(&one[..]), Bytes::from(&two[..])]) + .write::>(vec![]) + .build(), + ) + .with_target_gas(Some(100_000)) + .with_subcall_handle(move |subcall| { + let Subcall { + address, + transfer, + input, + target_gas, + is_static, + context, + } = subcall; + + // Called from the precompile caller. + assert_eq!(context.caller, Alice.into()); + assert_eq!(is_static, false); + + match address { + a if a == Bob.into() => { + assert_eq!(counter, 0, "this is the first call"); + counter += 1; + + assert_eq!( + target_gas, + Some(100_000 - total_call_cost), + "batch forward all gas" + ); + let transfer = transfer.expect("there is a transfer"); + assert_eq!(transfer.source, Alice.into()); + assert_eq!(transfer.target, Bob.into()); + assert_eq!(transfer.value, 1u8.into()); + + assert_eq!(context.address, Bob.into()); + assert_eq!(context.apparent_value, 1u8.into()); + + assert_eq!(&input, b"one"); + + SubcallOutput { + cost: 13, + logs: vec![ + LogsBuilder::new(Bob.into()).log1(H256::repeat_byte(0x11), vec![]) + ], + ..SubcallOutput::succeed() + } + } + a if a == Charlie.into() => { + assert_eq!(counter, 1, "this is the second call"); + counter += 1; + + assert_eq!( + target_gas, + Some(100_000 - 13 - total_call_cost * 2), + "batch forward all gas" + ); + let transfer = transfer.expect("there is a transfer"); + assert_eq!(transfer.source, Alice.into()); + assert_eq!(transfer.target, Charlie.into()); + assert_eq!(transfer.value, 2u8.into()); + + assert_eq!(context.address, Charlie.into()); + assert_eq!(context.apparent_value, 2u8.into()); + + assert_eq!(&input, b"two"); + + SubcallOutput { + cost: 17, + logs: vec![ + LogsBuilder::new(Charlie.into()).log1(H256::repeat_byte(0x22), vec![]) + ], + ..SubcallOutput::succeed() + } + } + _ => panic!("unexpected subcall"), + } + }) + .expect_cost(13 + 17 + total_call_cost * 2) +} + +#[test] +fn batch_some_returns() { + ExtBuilder::default().build().execute_with(|| { + batch_returns(&precompiles(), Mode::BatchSome) + .expect_log(LogsBuilder::new(Bob.into()).log1(H256::repeat_byte(0x11), vec![])) + .expect_log(log_subcall_succeeded(precompile_address(), 0)) + .expect_log(LogsBuilder::new(Charlie.into()).log1(H256::repeat_byte(0x22), vec![])) + .expect_log(log_subcall_succeeded(precompile_address(), 1)) + .execute_returns(EvmDataWriter::new().write(true).build()) + }) +} + +#[test] +fn batch_some_until_failure_returns() { + ExtBuilder::default().build().execute_with(|| { + batch_returns(&precompiles(), Mode::BatchSomeUntilFailure) + .expect_log(LogsBuilder::new(Bob.into()).log1(H256::repeat_byte(0x11), vec![])) + .expect_log(log_subcall_succeeded(precompile_address(), 0)) + .expect_log(LogsBuilder::new(Charlie.into()).log1(H256::repeat_byte(0x22), vec![])) + .expect_log(log_subcall_succeeded(precompile_address(), 1)) + .execute_returns(EvmDataWriter::new().write(true).build()) + }) +} + +#[test] +fn batch_all_returns() { + ExtBuilder::default().build().execute_with(|| { + batch_returns(&precompiles(), Mode::BatchAll) + .expect_log(LogsBuilder::new(Bob.into()).log1(H256::repeat_byte(0x11), vec![])) + .expect_log(log_subcall_succeeded(precompile_address(), 0)) + .expect_log(LogsBuilder::new(Charlie.into()).log1(H256::repeat_byte(0x22), vec![])) + .expect_log(log_subcall_succeeded(precompile_address(), 1)) + .execute_returns(EvmDataWriter::new().write(true).build()) + }) +} + +fn batch_out_of_gas( + precompiles: &BatchPrecompileMock, + mode: Mode, +) -> PrecompilesTester> { + let one = b"one"; + let (_, total_call_cost) = costs(); + precompiles + .prepare_test( + Alice, + precompile_address(), + EvmDataWriter::new_with_selector(check_mode(mode)) + .write(vec![Address(Bob.into())]) + .write(vec![U256::from(1u8)]) + .write(vec![Bytes::from(&one[..])]) + .write::>(vec![]) + .build(), + ) + .with_target_gas(Some(50_000)) + .with_subcall_handle(move |subcall| { + let Subcall { + address, + transfer, + input, + target_gas, + is_static, + context, + } = subcall; + + // Called from the precompile caller. + assert_eq!(context.caller, Alice.into()); + assert_eq!(is_static, false); + + match address { + a if a == Bob.into() => { + assert_eq!( + target_gas, + Some(50_000 - total_call_cost), + "batch forward all gas" + ); + let transfer = transfer.expect("there is a transfer"); + assert_eq!(transfer.source, Alice.into()); + assert_eq!(transfer.target, Bob.into()); + assert_eq!(transfer.value, 1u8.into()); + + assert_eq!(context.address, Bob.into()); + assert_eq!(context.apparent_value, 1u8.into()); + + assert_eq!(&input, b"one"); + + SubcallOutput { + cost: 11_000, + ..SubcallOutput::out_of_gas() + } + } + _ => panic!("unexpected subcall"), + } + }) +} + +#[test] +fn batch_some_out_of_gas() { + ExtBuilder::default().build().execute_with(|| { + batch_out_of_gas(&precompiles(), Mode::BatchSome) + .expect_log(log_subcall_failed(precompile_address(), 0)) + .execute_returns(EvmDataWriter::new().write(true).build()) + }) +} + +#[test] +fn batch_some_until_failure_out_of_gas() { + ExtBuilder::default().build().execute_with(|| { + batch_out_of_gas(&precompiles(), Mode::BatchSomeUntilFailure) + .expect_log(log_subcall_failed(precompile_address(), 0)) + .execute_returns(EvmDataWriter::new().write(true).build()) + }) +} + +#[test] +fn batch_all_out_of_gas() { + ExtBuilder::default().build().execute_with(|| { + batch_out_of_gas(&precompiles(), Mode::BatchAll).execute_error(ExitError::OutOfGas) + }) +} + +fn batch_incomplete( + precompiles: &BatchPrecompileMock, + mode: Mode, +) -> PrecompilesTester> { + let mut counter = 0; + let one = b"one"; + + let (_, total_call_cost) = costs(); + + precompiles + .prepare_test( + Alice, + precompile_address(), + EvmDataWriter::new_with_selector(check_mode(mode)) + .write(vec![ + Address(Bob.into()), + Address(Charlie.into()), + Address(Alice.into()), + ]) + .write(vec![U256::from(1u8), U256::from(2u8), U256::from(3u8)]) + .write(vec![Bytes::from(&one[..])]) + .write::>(vec![]) + .build(), + ) + .with_target_gas(Some(300_000)) + .with_subcall_handle(move |subcall| { + let Subcall { + address, + transfer, + input, + target_gas, + is_static, + context, + } = subcall; + + // Called from the precompile caller. + assert_eq!(context.caller, Alice.into()); + assert_eq!(is_static, false); + + match address { + a if a == Bob.into() => { + assert_eq!(counter, 0, "this is the first call"); + counter += 1; + + assert_eq!( + target_gas, + Some(300_000 - total_call_cost), + "batch forward all gas" + ); + let transfer = transfer.expect("there is a transfer"); + assert_eq!(transfer.source, Alice.into()); + assert_eq!(transfer.target, Bob.into()); + assert_eq!(transfer.value, 1u8.into()); + + assert_eq!(context.address, Bob.into()); + assert_eq!(context.apparent_value, 1u8.into()); + + assert_eq!(&input, b"one"); + + SubcallOutput { + cost: 13, + logs: vec![ + LogsBuilder::new(Bob.into()).log1(H256::repeat_byte(0x11), vec![]) + ], + ..SubcallOutput::succeed() + } + } + a if a == Charlie.into() => { + assert_eq!(counter, 1, "this is the second call"); + counter += 1; + + assert_eq!( + target_gas, + Some(300_000 - 13 - total_call_cost * 2), + "batch forward all gas" + ); + let transfer = transfer.expect("there is a transfer"); + assert_eq!(transfer.source, Alice.into()); + assert_eq!(transfer.target, Charlie.into()); + assert_eq!(transfer.value, 2u8.into()); + + assert_eq!(context.address, Charlie.into()); + assert_eq!(context.apparent_value, 2u8.into()); + + assert_eq!(&input, b""); + + SubcallOutput { + output: String::from("Revert message").as_bytes().to_vec(), + cost: 17, + ..SubcallOutput::revert() + } + } + a if a == Alice.into() => { + assert_eq!(counter, 2, "this is the third call"); + counter += 1; + + assert_eq!( + target_gas, + Some(300_000 - 13 - 17 - total_call_cost * 3), + "batch forward all gas" + ); + let transfer = transfer.expect("there is a transfer"); + assert_eq!(transfer.source, Alice.into()); + assert_eq!(transfer.target, Alice.into()); + assert_eq!(transfer.value, 3u8.into()); + + assert_eq!(context.address, Alice.into()); + assert_eq!(context.apparent_value, 3u8.into()); + + assert_eq!(&input, b""); + + SubcallOutput { + cost: 19, + logs: vec![ + LogsBuilder::new(Alice.into()).log1(H256::repeat_byte(0x33), vec![]) + ], + ..SubcallOutput::succeed() + } + } + _ => panic!("unexpected subcall"), + } + }) +} + +#[test] +fn batch_some_incomplete() { + ExtBuilder::default().build().execute_with(|| { + let (_, total_call_cost) = costs(); + + batch_incomplete(&precompiles(), Mode::BatchSome) + .expect_log(LogsBuilder::new(Bob.into()).log1(H256::repeat_byte(0x11), vec![])) + .expect_log(log_subcall_succeeded(precompile_address(), 0)) + .expect_log(log_subcall_failed(precompile_address(), 1)) + .expect_log(LogsBuilder::new(Alice.into()).log1(H256::repeat_byte(0x33), vec![])) + .expect_log(log_subcall_succeeded(precompile_address(), 2)) + .expect_cost(13 + 17 + 19 + total_call_cost * 3) + .execute_returns(EvmDataWriter::new().write(true).build()) + }) +} + +#[test] +fn batch_some_until_failure_incomplete() { + ExtBuilder::default().build().execute_with(|| { + let (_, total_call_cost) = costs(); + + batch_incomplete(&precompiles(), Mode::BatchSomeUntilFailure) + .expect_log(LogsBuilder::new(Bob.into()).log1(H256::repeat_byte(0x11), vec![])) + .expect_log(log_subcall_succeeded(precompile_address(), 0)) + .expect_log(log_subcall_failed(precompile_address(), 1)) + .expect_cost(13 + 17 + total_call_cost * 2) + .execute_returns(EvmDataWriter::new().write(true).build()) + }) +} + +#[test] +fn batch_all_incomplete() { + ExtBuilder::default().build().execute_with(|| { + batch_incomplete(&precompiles(), Mode::BatchAll) + .execute_reverts(|output| output == b"Revert message") + }) +} + +fn batch_log_out_of_gas( + precompiles: &BatchPrecompileMock, + mode: Mode, +) -> PrecompilesTester> { + let (log_cost, _) = costs(); + let one = b"one"; + + precompiles + .prepare_test( + Alice, + precompile_address(), + EvmDataWriter::new_with_selector(check_mode(mode)) + .write(vec![Address(Bob.into())]) + .write(vec![U256::from(1u8)]) + .write(vec![Bytes::from(&one[..])]) + .write::>(vec![]) + .build(), + ) + .with_target_gas(Some(log_cost - 1)) + .with_subcall_handle(move |_subcall| panic!("there shouldn't be any subcalls")) +} + +#[test] +fn batch_all_log_out_of_gas() { + ExtBuilder::default().build().execute_with(|| { + batch_log_out_of_gas(&precompiles(), Mode::BatchAll).execute_error(ExitError::OutOfGas); + }) +} + +#[test] +fn batch_some_log_out_of_gas() { + ExtBuilder::default().build().execute_with(|| { + batch_log_out_of_gas(&precompiles(), Mode::BatchSome) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + }) +} + +#[test] +fn batch_some_until_failure_log_out_of_gas() { + ExtBuilder::default().build().execute_with(|| { + batch_log_out_of_gas(&precompiles(), Mode::BatchSomeUntilFailure) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + }) +} + +fn batch_call_out_of_gas( + precompiles: &BatchPrecompileMock, + mode: Mode, +) -> PrecompilesTester> { + let (_, total_call_cost) = costs(); + let one = b"one"; + + precompiles + .prepare_test( + Alice, + precompile_address(), + EvmDataWriter::new_with_selector(check_mode(mode)) + .write(vec![Address(Bob.into())]) + .write(vec![U256::from(1u8)]) + .write(vec![Bytes::from(&one[..])]) + .write::>(vec![]) + .build(), + ) + .with_target_gas(Some(total_call_cost - 1)) + .with_subcall_handle(move |_subcall| panic!("there shouldn't be any subcalls")) +} + +#[test] +fn batch_all_call_out_of_gas() { + ExtBuilder::default().build().execute_with(|| { + batch_call_out_of_gas(&precompiles(), Mode::BatchAll).execute_error(ExitError::OutOfGas); + }) +} + +#[test] +fn batch_some_call_out_of_gas() { + ExtBuilder::default().build().execute_with(|| { + batch_call_out_of_gas(&precompiles(), Mode::BatchSome) + .expect_log(log_subcall_failed(precompile_address(), 0)) + .execute_returns(EvmDataWriter::new().write(true).build()); + }) +} + +#[test] +fn batch_some_until_failure_call_out_of_gas() { + ExtBuilder::default().build().execute_with(|| { + batch_call_out_of_gas(&precompiles(), Mode::BatchSomeUntilFailure) + .expect_log(log_subcall_failed(precompile_address(), 0)) + .execute_returns(EvmDataWriter::new().write(true).build()); + }) +} + +fn batch_gas_limit( + precompiles: &BatchPrecompileMock, + mode: Mode, +) -> PrecompilesTester> { + let (_, total_call_cost) = costs(); + let one = b"one"; + + precompiles + .prepare_test( + Alice, + precompile_address(), + EvmDataWriter::new_with_selector(check_mode(mode)) + .write(vec![Address(Bob.into())]) + .write(vec![U256::from(1u8)]) + .write(vec![Bytes::from(&one[..])]) + .write::>(vec![U256::from(50_000 - total_call_cost + 1)]) + .build(), + ) + .with_target_gas(Some(50_000)) + .with_subcall_handle(move |_subcall| panic!("there shouldn't be any subcalls")) +} + +#[test] +fn batch_all_gas_limit() { + ExtBuilder::default().build().execute_with(|| { + batch_gas_limit(&precompiles(), Mode::BatchAll).execute_error(ExitError::OutOfGas); + }) +} + +#[test] +fn batch_some_gas_limit() { + ExtBuilder::default().build().execute_with(|| { + let (return_log_cost, _) = costs(); + + batch_gas_limit(&precompiles(), Mode::BatchSome) + .expect_log(log_subcall_failed(precompile_address(), 0)) + .expect_cost(return_log_cost) + .execute_returns(EvmDataWriter::new().write(true).build()); + }) +} + +#[test] +fn batch_some_until_failure_gas_limit() { + ExtBuilder::default().build().execute_with(|| { + batch_gas_limit(&precompiles(), Mode::BatchSomeUntilFailure) + .expect_log(log_subcall_failed(precompile_address(), 0)) + .execute_returns(EvmDataWriter::new().write(true).build()); + }) +} diff --git a/precompiles/dapps-staking/Cargo.toml b/precompiles/dapps-staking/Cargo.toml index 232780acf6..aa5584170c 100644 --- a/precompiles/dapps-staking/Cargo.toml +++ b/precompiles/dapps-staking/Cargo.toml @@ -31,7 +31,7 @@ pallet-evm = { workspace = true } [dev-dependencies] derive_more = { workspace = true } -pallet-balances = { workspace = true } +pallet-balances = { workspace = true, features = ["std"] } pallet-timestamp = { workspace = true } precompile-utils = { workspace = true, features = ["testing"] } serde = { workspace = true } diff --git a/precompiles/utils/Cargo.toml b/precompiles/utils/Cargo.toml index 6be1724ce6..a97a1a81ff 100644 --- a/precompiles/utils/Cargo.toml +++ b/precompiles/utils/Cargo.toml @@ -2,17 +2,22 @@ name = "precompile-utils" authors = ["StakeTechnologies", "PureStake"] description = "Utils to write EVM precompiles." -version = "0.4.3" +version = "0.5.0" edition.workspace = true homepage.workspace = true repository.workspace = true [dependencies] # There's a problem with --all-features when this is moved under dev-deps -evm = { workspace = true, features = ["std"], optional = true } +derive_more = { workspace = true, optional = true } +environmental = { workspace = true } +evm = { workspace = true, features = ["with-codec"] } +hex-literal = { workspace = true, optional = true } impl-trait-for-tuples = { workspace = true } log = { workspace = true } num_enum = { workspace = true } +scale-info = { workspace = true, optional = true, features = ["derive"] } +serde = { workspace = true, optional = true } sha3 = { workspace = true } similar-asserts = { workspace = true, optional = true } @@ -53,5 +58,6 @@ std = [ "sp-std/std", "sp-runtime/std", "xcm/std", + "environmental/std", ] -testing = ["similar-asserts", "std"] +testing = ["similar-asserts", "std", "scale-info", "serde", "derive_more", "hex-literal"] diff --git a/precompiles/utils/src/bytes.rs b/precompiles/utils/src/bytes.rs new file mode 100644 index 0000000000..ba86df9297 --- /dev/null +++ b/precompiles/utils/src/bytes.rs @@ -0,0 +1,218 @@ +// This file is part of Astar. + +// Copyright 2019-2022 PureStake Inc. +// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Utils package, originally developed by Purestake Inc. +// Utils package used in Astar Network in terms of GPLv3. +// +// Utils 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. + +// Utils 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 Utils. If not, see . + +use super::*; +use alloc::borrow::ToOwned; +pub use alloc::string::String; +use sp_core::{ConstU32, Get}; + +type ConstU32Max = ConstU32<{ u32::MAX }>; + +pub type UnboundedBytes = BoundedBytesString; +pub type BoundedBytes = BoundedBytesString; + +pub type UnboundedString = BoundedBytesString; +pub type BoundedString = BoundedBytesString; + +trait Kind { + fn signature() -> String; +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BytesKind; + +impl Kind for BytesKind { + fn signature() -> String { + String::from("bytes") + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct StringKind; + +impl Kind for StringKind { + fn signature() -> String { + String::from("string") + } +} + +/// The `bytes/string` type of Solidity. +/// It is different from `Vec` which will be serialized with padding for each `u8` element +/// of the array, while `Bytes` is tightly packed. +#[derive(Debug)] +pub struct BoundedBytesString { + data: Vec, + _phantom: PhantomData<(K, S)>, +} + +impl> Clone for BoundedBytesString { + fn clone(&self) -> Self { + Self { + data: self.data.clone(), + _phantom: PhantomData, + } + } +} + +impl PartialEq> for BoundedBytesString { + fn eq(&self, other: &BoundedBytesString) -> bool { + self.data.eq(&other.data) + } +} + +impl Eq for BoundedBytesString {} + +impl> BoundedBytesString { + pub fn as_bytes(&self) -> &[u8] { + &self.data + } + + pub fn as_str(&self) -> Result<&str, sp_std::str::Utf8Error> { + sp_std::str::from_utf8(&self.data) + } +} + +impl> EvmData for BoundedBytesString { + fn read(reader: &mut EvmDataReader) -> EvmResult { + let mut inner_reader = reader.read_pointer()?; + + // Read bytes/string size. + let array_size: usize = inner_reader + .read::() + .map_err(|_| revert("length, out of bounds"))? + .try_into() + .map_err(|_| revert("length, value too large"))?; + + if array_size > S::get() as usize { + return Err(revert("length, value too large").into()); + } + + let data = inner_reader.read_raw_bytes(array_size)?; + + let bytes = Self { + data: data.to_owned(), + _phantom: PhantomData, + }; + + Ok(bytes) + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + let value: Vec<_> = value.into(); + let length = value.len(); + + // Pad the data. + // Leave it as is if a multiple of 32, otherwise pad to next + // multiple or 32. + let chunks = length / 32; + let padded_size = match length % 32 { + 0 => chunks * 32, + _ => (chunks + 1) * 32, + }; + + let mut value = value.to_vec(); + value.resize(padded_size, 0); + + writer.write_pointer( + EvmDataWriter::new() + .write(U256::from(length)) + .write(value) + .build(), + ); + } + + fn has_static_size() -> bool { + false + } +} + +// BytesString <=> Vec/&[u8] + +impl From> for Vec { + fn from(value: BoundedBytesString) -> Self { + value.data + } +} + +impl From> for BoundedBytesString { + fn from(value: Vec) -> Self { + Self { + data: value, + _phantom: PhantomData, + } + } +} + +impl From<&[u8]> for BoundedBytesString { + fn from(value: &[u8]) -> Self { + Self { + data: value.to_vec(), + _phantom: PhantomData, + } + } +} + +impl From<[u8; N]> for BoundedBytesString { + fn from(value: [u8; N]) -> Self { + Self { + data: value.to_vec(), + _phantom: PhantomData, + } + } +} + +impl From<&[u8; N]> for BoundedBytesString { + fn from(value: &[u8; N]) -> Self { + Self { + data: value.to_vec(), + _phantom: PhantomData, + } + } +} + +// BytesString <=> String/str + +impl TryFrom> for String { + type Error = alloc::string::FromUtf8Error; + + fn try_from(value: BoundedBytesString) -> Result { + alloc::string::String::from_utf8(value.data) + } +} + +impl From<&str> for BoundedBytesString { + fn from(value: &str) -> Self { + Self { + data: value.as_bytes().into(), + _phantom: PhantomData, + } + } +} + +impl From for BoundedBytesString { + fn from(value: String) -> Self { + Self { + data: value.as_bytes().into(), + _phantom: PhantomData, + } + } +} diff --git a/precompiles/utils/src/data.rs b/precompiles/utils/src/data.rs index 2359bcefd4..2504171b33 100644 --- a/precompiles/utils/src/data.rs +++ b/precompiles/utils/src/data.rs @@ -23,9 +23,9 @@ use crate::{revert, EvmResult}; use alloc::borrow::ToOwned; -use core::{any::type_name, ops::Range}; +use core::{any::type_name, marker::PhantomData, ops::Range}; use impl_trait_for_tuples::impl_for_tuples; -use sp_core::{H160, H256, U256}; +use sp_core::{Get, H160, H256, U256}; use sp_std::{convert::TryInto, vec, vec::Vec}; /// The `address` type of Solidity. @@ -325,8 +325,31 @@ pub trait EvmData: Sized { fn read(reader: &mut EvmDataReader) -> EvmResult; fn write(writer: &mut EvmDataWriter, value: Self); fn has_static_size() -> bool; + fn is_explicit_tuple() -> bool { + false + } +} +/// Encode the value into its Solidity ABI format. +/// If `T` is a tuple it is encoded as a Solidity tuple with dynamic-size offset. +fn encode(value: T) -> Vec { + EvmDataWriter::new().write(value).build() +} + +/// Encode the value into its Solidity ABI format. +/// If `T` is a tuple every element is encoded without a prefixed offset. +/// It matches the encoding of Solidity function arguments and return value, or event data. +pub fn encode_arguments(value: T) -> Vec { + let output = encode(value); + if T::is_explicit_tuple() && !T::has_static_size() { + output[32..].to_vec() + } else { + output + } } +pub use self::encode_arguments as encode_return_value; +pub use self::encode_arguments as encode_event_data; + #[impl_for_tuples(1, 18)] impl EvmData for Tuple { fn has_static_size() -> bool { @@ -604,3 +627,105 @@ impl EvmData for Bytes { false } } + +/// Wrapper around a Vec that provides a max length bound on read. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BoundedVec { + inner: Vec, + _phantom: PhantomData, +} + +impl> EvmData for BoundedVec { + fn read(reader: &mut EvmDataReader) -> EvmResult { + let mut inner_reader = reader.read_pointer()?; + + let array_size: usize = inner_reader + .read::() + .map_err(|_| revert("out of bounds: length of array"))? + .try_into() + .map_err(|_| revert("value too large : Array has more than max items allowed"))?; + + if array_size > S::get() as usize { + return Err(revert("value too large : Array has more than max items allowed").into()); + } + + let mut array = vec![]; + + let mut item_reader = EvmDataReader { + input: inner_reader + .input + .get(32..) + .ok_or_else(|| revert("read out of bounds: array content"))?, + cursor: 0, + }; + + for _ in 0..array_size { + array.push(item_reader.read()?); + } + + Ok(BoundedVec { + inner: array, + _phantom: PhantomData, + }) + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + let value: Vec<_> = value.into(); + let mut inner_writer = EvmDataWriter::new().write(U256::from(value.len())); + + for inner in value { + // Any offset in items are relative to the start of the item instead of the + // start of the array. However if there is offseted data it must but appended after + // all items (offsets) are written. We thus need to rely on `compute_offsets` to do + // that, and must store a "shift" to correct the offsets. + let shift = inner_writer.data.len(); + let item_writer = EvmDataWriter::new().write(inner); + + inner_writer = inner_writer.write_raw_bytes(&item_writer.data); + for mut offset_datum in item_writer.offset_data { + offset_datum.offset_shift += 32; + offset_datum.offset_position += shift; + inner_writer.offset_data.push(offset_datum); + } + } + + writer.write_pointer(inner_writer.build()); + } + + fn has_static_size() -> bool { + false + } +} + +impl From> for BoundedVec { + fn from(value: Vec) -> Self { + BoundedVec { + inner: value, + _phantom: PhantomData, + } + } +} + +impl From<&[T]> for BoundedVec { + fn from(value: &[T]) -> Self { + BoundedVec { + inner: value.to_vec(), + _phantom: PhantomData, + } + } +} + +impl From<[T; N]> for BoundedVec { + fn from(value: [T; N]) -> Self { + BoundedVec { + inner: value.to_vec(), + _phantom: PhantomData, + } + } +} + +impl From> for Vec { + fn from(value: BoundedVec) -> Self { + value.inner + } +} diff --git a/precompiles/utils/src/lib.rs b/precompiles/utils/src/lib.rs index 1b67c7ea24..ac7cfce9e3 100644 --- a/precompiles/utils/src/lib.rs +++ b/precompiles/utils/src/lib.rs @@ -25,6 +25,7 @@ extern crate alloc; use crate::alloc::borrow::ToOwned; +pub use alloc::string::String; use fp_evm::{ Context, ExitError, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, @@ -38,7 +39,8 @@ use pallet_evm::{GasWeightMapping, Log}; use sp_core::{H160, H256, U256}; use sp_std::{marker::PhantomData, vec, vec::Vec}; -mod data; +pub mod bytes; +pub mod data; pub use data::{Address, Bytes, EvmData, EvmDataReader, EvmDataWriter}; pub use precompile_utils_macro::{generate_function_selector, keccak256}; @@ -338,6 +340,68 @@ pub fn log_costs(topics: usize, data_len: usize) -> EvmResult { }) } +// Compute the cost of doing a subcall. +// Some parameters cannot be known in advance, so we estimate the worst possible cost. +pub fn call_cost(value: U256, config: &evm::Config) -> u64 { + // Copied from EVM code since not public. + pub const G_CALLVALUE: u64 = 9000; + pub const G_NEWACCOUNT: u64 = 25000; + + fn address_access_cost(is_cold: bool, regular_value: u64, config: &evm::Config) -> u64 { + if config.increase_state_access_gas { + if is_cold { + config.gas_account_access_cold + } else { + config.gas_storage_read_warm + } + } else { + regular_value + } + } + + fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { + if is_call_or_callcode && transfers_value { + G_CALLVALUE + } else { + 0 + } + } + + fn new_cost( + is_call_or_staticcall: bool, + new_account: bool, + transfers_value: bool, + config: &evm::Config, + ) -> u64 { + let eip161 = !config.empty_considered_exists; + if is_call_or_staticcall { + if eip161 { + if transfers_value && new_account { + G_NEWACCOUNT + } else { + 0 + } + } else if new_account { + G_NEWACCOUNT + } else { + 0 + } + } else { + 0 + } + } + + let transfers_value = value != U256::default(); + let is_cold = true; + let is_call_or_callcode = true; + let is_call_or_staticcall = true; + let new_account = true; + + address_access_cost(is_cold, config.gas_call, config) + + xfer_cost(is_call_or_callcode, transfers_value) + + new_cost(is_call_or_staticcall, new_account, transfers_value, config) +} + impl PrecompileHandleExt for T { #[must_use] /// Record cost of a log manualy. @@ -409,7 +473,7 @@ pub fn succeed(output: impl AsRef<[u8]>) -> PrecompileOutput { #[must_use] /// Check that a function call is compatible with the context it is /// called into. -fn check_function_modifier( +pub fn check_function_modifier( context: &Context, is_static: bool, modifier: FunctionModifier, diff --git a/precompiles/utils/src/testing.rs b/precompiles/utils/src/testing.rs deleted file mode 100644 index 721681ec87..0000000000 --- a/precompiles/utils/src/testing.rs +++ /dev/null @@ -1,448 +0,0 @@ -// This file is part of Astar. - -// Copyright 2019-2022 PureStake Inc. -// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later -// -// This file is part of Utils package, originally developed by Purestake Inc. -// Utils package used in Astar Network in terms of GPLv3. -// -// Utils 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. - -// Utils 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 Utils. If not, see . -use super::*; -use assert_matches::assert_matches; -use fp_evm::{ - ExitReason, ExitSucceed, PrecompileOutput, PrecompileResult, PrecompileSet, Transfer, -}; -use sp_std::boxed::Box; - -pub struct Subcall { - pub address: H160, - pub transfer: Option, - pub input: Vec, - pub target_gas: Option, - pub is_static: bool, - pub context: Context, -} - -pub struct SubcallOutput { - pub reason: ExitReason, - pub output: Vec, - pub cost: u64, - pub logs: Vec, -} - -pub trait SubcallTrait: FnMut(Subcall) -> SubcallOutput + 'static {} - -impl SubcallOutput + 'static> SubcallTrait for T {} - -pub type SubcallHandle = Box; - -/// Mock handle to write tests for precompiles. -pub struct MockHandle { - pub gas_limit: u64, - pub gas_used: u64, - pub logs: Vec, - pub subcall_handle: Option, - pub code_address: H160, - pub input: Vec, - pub context: Context, - pub is_static: bool, -} - -impl MockHandle { - pub fn new(code_address: H160, context: Context) -> Self { - Self { - gas_limit: u64::MAX, - gas_used: 0, - logs: vec![], - subcall_handle: None, - code_address, - input: Vec::new(), - context, - is_static: false, - } - } -} - -// Compute the cost of doing a subcall. -// Some parameters cannot be known in advance, so we estimate the worst possible cost. -pub fn call_cost(value: U256, config: &evm::Config) -> u64 { - // Copied from EVM code since not public. - pub const G_CALLVALUE: u64 = 9000; - pub const G_NEWACCOUNT: u64 = 25000; - - fn address_access_cost(is_cold: bool, regular_value: u64, config: &evm::Config) -> u64 { - if config.increase_state_access_gas { - if is_cold { - config.gas_account_access_cold - } else { - config.gas_storage_read_warm - } - } else { - regular_value - } - } - - fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { - if is_call_or_callcode && transfers_value { - G_CALLVALUE - } else { - 0 - } - } - - fn new_cost( - is_call_or_staticcall: bool, - new_account: bool, - transfers_value: bool, - config: &evm::Config, - ) -> u64 { - let eip161 = !config.empty_considered_exists; - if is_call_or_staticcall { - if eip161 { - if transfers_value && new_account { - G_NEWACCOUNT - } else { - 0 - } - } else if new_account { - G_NEWACCOUNT - } else { - 0 - } - } else { - 0 - } - } - - let transfers_value = value != U256::default(); - let is_cold = true; - let is_call_or_callcode = true; - let is_call_or_staticcall = true; - let new_account = true; - - address_access_cost(is_cold, config.gas_call, config) - + xfer_cost(is_call_or_callcode, transfers_value) - + new_cost(is_call_or_staticcall, new_account, transfers_value, config) -} - -impl PrecompileHandle for MockHandle { - /// Perform subcall in provided context. - /// Precompile specifies in which context the subcall is executed. - fn call( - &mut self, - address: H160, - transfer: Option, - input: Vec, - target_gas: Option, - is_static: bool, - context: &Context, - ) -> (ExitReason, Vec) { - if self - .record_cost(call_cost(context.apparent_value, &evm::Config::london())) - .is_err() - { - return (ExitReason::Error(ExitError::OutOfGas), vec![]); - } - - match &mut self.subcall_handle { - Some(handle) => { - let SubcallOutput { - reason, - output, - cost, - logs, - } = handle(Subcall { - address, - transfer, - input, - target_gas, - is_static, - context: context.clone(), - }); - - if self.record_cost(cost).is_err() { - return (ExitReason::Error(ExitError::OutOfGas), vec![]); - } - - for log in logs { - self.log(log.address, log.topics, log.data) - .expect("cannot fail"); - } - - (reason, output) - } - None => panic!("no subcall handle registered"), - } - } - - fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { - self.gas_used += cost; - - if self.gas_used > self.gas_limit { - Err(ExitError::OutOfGas) - } else { - Ok(()) - } - } - - fn remaining_gas(&self) -> u64 { - self.gas_limit - self.gas_used - } - - fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError> { - self.logs.push(PrettyLog(Log { - address, - topics, - data, - })); - Ok(()) - } - - /// Retreive the code address (what is the address of the precompile being called). - fn code_address(&self) -> H160 { - self.code_address - } - - /// Retreive the input data the precompile is called with. - fn input(&self) -> &[u8] { - &self.input - } - - /// Retreive the context in which the precompile is executed. - fn context(&self) -> &Context { - &self.context - } - - /// Is the precompile call is done statically. - fn is_static(&self) -> bool { - self.is_static - } - - /// Retreive the gas limit of this call. - fn gas_limit(&self) -> Option { - Some(self.gas_limit) - } - - fn record_external_cost( - &mut self, - _ref_time: Option, - _proof_size: Option, - ) -> Result<(), ExitError> { - Ok(()) - } - - fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} -} - -pub struct PrecompilesTester<'p, P> { - precompiles: &'p P, - handle: MockHandle, - - target_gas: Option, - subcall_handle: Option, - - expected_cost: Option, - expected_logs: Option>, -} - -impl<'p, P: PrecompileSet> PrecompilesTester<'p, P> { - pub fn new( - precompiles: &'p P, - from: impl Into, - to: impl Into, - data: Vec, - ) -> Self { - let to = to.into(); - let mut handle = MockHandle::new( - to, - Context { - address: to, - caller: from.into(), - apparent_value: U256::zero(), - }, - ); - - handle.input = data; - - Self { - precompiles, - handle, - - target_gas: None, - subcall_handle: None, - - expected_cost: None, - expected_logs: None, - } - } - - pub fn with_value(mut self, value: impl Into) -> Self { - self.handle.context.apparent_value = value.into(); - self - } - - pub fn with_subcall_handle(mut self, subcall_handle: impl SubcallTrait) -> Self { - self.subcall_handle = Some(Box::new(subcall_handle)); - self - } - - pub fn with_target_gas(mut self, target_gas: Option) -> Self { - self.target_gas = target_gas; - self - } - - pub fn with_gas_limit(mut self, gas_limit: u64) -> Self { - self.handle.gas_limit = gas_limit; - self - } - - pub fn expect_cost(mut self, cost: u64) -> Self { - self.expected_cost = Some(cost); - self - } - - pub fn expect_no_logs(mut self) -> Self { - self.expected_logs = Some(vec![]); - self - } - - pub fn expect_log(mut self, log: Log) -> Self { - self.expected_logs = Some({ - let mut logs = self.expected_logs.unwrap_or_default(); - logs.push(PrettyLog(log)); - logs - }); - self - } - - fn assert_optionals(&self) { - if let Some(cost) = &self.expected_cost { - assert_eq!(&self.handle.gas_used, cost); - } - - if let Some(logs) = &self.expected_logs { - similar_asserts::assert_eq!(&self.handle.logs, logs); - } - } - - fn execute(&mut self) -> Option { - let handle = &mut self.handle; - handle.subcall_handle = self.subcall_handle.take(); - - if let Some(gas_limit) = self.target_gas { - handle.gas_limit = gas_limit; - } - - let res = self.precompiles.execute(handle); - - self.subcall_handle = handle.subcall_handle.take(); - - res - } - - /// Execute the precompile set and expect some precompile to have been executed, regardless of the - /// result. - pub fn execute_some(mut self) { - let res = self.execute(); - assert!(res.is_some()); - self.assert_optionals(); - } - - /// Execute the precompile set and expect no precompile to have been executed. - pub fn execute_none(mut self) { - let res = self.execute(); - assert!(res.is_none()); - self.assert_optionals(); - } - - /// Execute the precompile set and check it returns provided output. - pub fn execute_returns(mut self, output: Vec) { - let res = self.execute(); - assert_eq!( - res, - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output - })) - ); - self.assert_optionals(); - } - - /// Execute the precompile set and check if it reverts. - /// Take a closure allowing to perform custom matching on the output. - pub fn execute_reverts(mut self, check: impl Fn(&[u8]) -> bool) { - let res = self.execute(); - assert_matches!( - res, - Some(Err(PrecompileFailure::Revert { output, ..})) - if check(&output) - ); - self.assert_optionals(); - } - - /// Execute the precompile set and check it returns provided output. - pub fn execute_error(mut self, error: ExitError) { - let res = self.execute(); - assert_eq!( - res, - Some(Err(PrecompileFailure::Error { exit_status: error })) - ); - self.assert_optionals(); - } -} - -pub trait PrecompileTesterExt: PrecompileSet + Sized { - fn prepare_test( - &self, - from: impl Into, - to: impl Into, - data: Vec, - ) -> PrecompilesTester; -} - -impl PrecompileTesterExt for T { - fn prepare_test( - &self, - from: impl Into, - to: impl Into, - data: Vec, - ) -> PrecompilesTester { - PrecompilesTester::new(self, from, to, data) - } -} - -#[derive(Clone, PartialEq, Eq)] -pub struct PrettyLog(Log); - -impl core::fmt::Debug for PrettyLog { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { - let bytes = self - .0 - .data - .iter() - .map(|b| format!("{:02X}", b)) - .collect::>() - .join(""); - - let message = String::from_utf8(self.0.data.clone()).ok(); - - f.debug_struct("Log") - .field("address", &self.0.address) - .field("topics", &self.0.topics) - .field("data", &bytes) - .field("data_utf8", &message) - .finish() - } -} diff --git a/precompiles/utils/src/testing/account.rs b/precompiles/utils/src/testing/account.rs new file mode 100644 index 0000000000..66c5a13714 --- /dev/null +++ b/precompiles/utils/src/testing/account.rs @@ -0,0 +1,186 @@ +// This file is part of Astar. + +// Copyright 2019-2022 PureStake Inc. +// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Utils package, originally developed by Purestake Inc. +// Utils package used in Astar Network in terms of GPLv3. +// +// Utils 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. + +// Utils 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 Utils. If not, see . +use { + pallet_evm::AddressMapping, + scale_info::TypeInfo, + serde::{Deserialize, Serialize}, + sp_core::{Decode, Encode, MaxEncodedLen, H160, H256}, +}; + +#[derive( + Eq, + PartialEq, + Ord, + PartialOrd, + Clone, + Encode, + Decode, + Debug, + MaxEncodedLen, + TypeInfo, + Serialize, + Deserialize, + derive_more::Display, +)] +pub struct MockAccount(pub H160); + +impl MockAccount { + pub fn from_u64(v: u64) -> Self { + H160::from_low_u64_be(v).into() + } + + pub fn zero() -> Self { + H160::zero().into() + } + + pub fn has_prefix(&self, prefix: &[u8]) -> bool { + &self.0[0..4] == prefix + } + + pub fn has_prefix_u32(&self, prefix: u32) -> bool { + self.0[0..4] == prefix.to_be_bytes() + } + + pub fn without_prefix(&self) -> u128 { + u128::from_be_bytes(<[u8; 16]>::try_from(&self.0[4..20]).expect("slice have len 16")) + } +} + +impl From for H160 { + fn from(account: MockAccount) -> H160 { + account.0 + } +} + +impl From for [u8; 20] { + fn from(account: MockAccount) -> [u8; 20] { + let x: H160 = account.into(); + x.into() + } +} + +impl From for H256 { + fn from(x: MockAccount) -> H256 { + let x: H160 = x.into(); + x.into() + } +} + +impl From for MockAccount { + fn from(address: H160) -> MockAccount { + MockAccount(address) + } +} + +impl From<[u8; 20]> for MockAccount { + fn from(address: [u8; 20]) -> MockAccount { + let x: H160 = address.into(); + MockAccount(x) + } +} + +impl AddressMapping for MockAccount { + fn into_account_id(address: H160) -> MockAccount { + address.into() + } +} + +#[macro_export] +macro_rules! mock_account { + ($name:ident, $convert:expr) => { + pub struct $name; + mock_account!(# $name, $convert); + }; + ($name:ident ( $($field:ty),* ), $convert:expr) => { + pub struct $name($(pub $field),*); + mock_account!(# $name, $convert); + }; + (# $name:ident, $convert:expr) => { + impl From<$name> for MockAccount { + fn from(value: $name) -> MockAccount { + $convert(value) + } + } + + impl From<$name> for sp_core::H160 { + fn from(value: $name) -> sp_core::H160 { + MockAccount::from(value).into() + } + } + + impl From<$name> for sp_core::H256 { + fn from(value: $name) -> sp_core::H256 { + MockAccount::from(value).into() + } + } + }; +} + +mock_account!(Zero, |_| MockAccount::zero()); +mock_account!(Alice, |_| H160::repeat_byte(0xAA).into()); +mock_account!(Bob, |_| H160::repeat_byte(0xBB).into()); +mock_account!(Charlie, |_| H160::repeat_byte(0xCC).into()); +mock_account!(David, |_| H160::repeat_byte(0xDD).into()); + +mock_account!(Precompile1, |_| MockAccount::from_u64(1)); + +mock_account!(CryptoAlith, |_| H160::from(hex_literal::hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac" +)) +.into()); +mock_account!(CryptoBaltathar, |_| H160::from(hex_literal::hex!( + "3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0" +)) +.into()); +mock_account!(CryptoCarleth, |_| H160::from(hex_literal::hex!( + "798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc" +)) +.into()); + +mock_account!( + AddressInPrefixedSet(u32, u128), + |value: AddressInPrefixedSet| { + let prefix: u32 = value.0; + let index: u128 = value.1; + + let mut buffer = Vec::with_capacity(20); // 160 bits + + buffer.extend_from_slice(&prefix.to_be_bytes()); + buffer.extend_from_slice(&index.to_be_bytes()); + + assert_eq!(buffer.len(), 20, "address buffer should have len of 20"); + + H160::from_slice(&buffer).into() + } +); + +pub fn alith_secret_key() -> [u8; 32] { + hex_literal::hex!("5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133") +} + +pub fn baltathar_secret_key() -> [u8; 32] { + hex_literal::hex!("8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b") +} + +pub fn charleth_secret_key() -> [u8; 32] { + hex_literal::hex!("0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b") +} diff --git a/precompiles/utils/src/testing/execution.rs b/precompiles/utils/src/testing/execution.rs new file mode 100644 index 0000000000..c7a52a9261 --- /dev/null +++ b/precompiles/utils/src/testing/execution.rs @@ -0,0 +1,262 @@ +// This file is part of Astar. + +// Copyright 2019-2022 PureStake Inc. +// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Utils package, originally developed by Purestake Inc. +// Utils package used in Astar Network in terms of GPLv3. +// +// Utils 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. + +// Utils 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 Utils. If not, see . +// This file is part of Astar. + +// Copyright 2019-2022 PureStake Inc. +// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Utils package, originally developed by Purestake Inc. +// Utils package used in Astar Network in terms of GPLv3. +// +// Utils 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. + +// Utils 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 Utils. If not, see . + +use { + crate::testing::{decode_revert_message, MockHandle, PrettyLog, SubcallHandle, SubcallTrait}, + assert_matches::assert_matches, + fp_evm::{ + Context, ExitError, ExitSucceed, Log, PrecompileFailure, PrecompileOutput, + PrecompileResult, PrecompileSet, + }, + sp_core::{H160, U256}, + sp_std::boxed::Box, +}; + +#[must_use] +pub struct PrecompilesTester<'p, P> { + precompiles: &'p P, + handle: MockHandle, + + target_gas: Option, + subcall_handle: Option, + + expected_cost: Option, + expected_logs: Option>, +} + +impl<'p, P: PrecompileSet> PrecompilesTester<'p, P> { + pub fn new( + precompiles: &'p P, + from: impl Into, + to: impl Into, + data: Vec, + ) -> Self { + let to = to.into(); + let mut handle = MockHandle::new( + to.clone(), + Context { + address: to, + caller: from.into(), + apparent_value: U256::zero(), + }, + ); + + handle.input = data; + + Self { + precompiles, + handle, + + target_gas: None, + subcall_handle: None, + + expected_cost: None, + expected_logs: None, + } + } + + pub fn with_value(mut self, value: impl Into) -> Self { + self.handle.context.apparent_value = value.into(); + self + } + + pub fn with_subcall_handle(mut self, subcall_handle: impl SubcallTrait) -> Self { + self.subcall_handle = Some(Box::new(subcall_handle)); + self + } + + pub fn with_target_gas(mut self, target_gas: Option) -> Self { + self.target_gas = target_gas; + self + } + + pub fn with_gas_limit(mut self, gas_limit: u64) -> Self { + self.handle.gas_limit = gas_limit; + self + } + + pub fn expect_cost(mut self, cost: u64) -> Self { + self.expected_cost = Some(cost); + self + } + + pub fn expect_no_logs(mut self) -> Self { + self.expected_logs = Some(vec![]); + self + } + + pub fn expect_log(mut self, log: Log) -> Self { + self.expected_logs = Some({ + let mut logs = self.expected_logs.unwrap_or_else(Vec::new); + logs.push(PrettyLog(log)); + logs + }); + self + } + + fn assert_optionals(&self) { + if let Some(cost) = &self.expected_cost { + assert_eq!(&self.handle.gas_used, cost); + } + + if let Some(logs) = &self.expected_logs { + similar_asserts::assert_eq!(&self.handle.logs, logs); + } + } + + fn execute(&mut self) -> Option { + let handle = &mut self.handle; + handle.subcall_handle = self.subcall_handle.take(); + + if let Some(gas_limit) = self.target_gas { + handle.gas_limit = gas_limit; + } + + let res = self.precompiles.execute(handle); + + self.subcall_handle = handle.subcall_handle.take(); + + res + } + + /// Execute the precompile set and expect some precompile to have been executed, regardless of the + /// result. + pub fn execute_some(mut self) { + let res = self.execute(); + assert!(res.is_some()); + self.assert_optionals(); + } + + /// Execute the precompile set and expect no precompile to have been executed. + pub fn execute_none(mut self) { + let res = self.execute(); + assert!(res.is_some()); + self.assert_optionals(); + } + + /// Execute the precompile set and check it returns provided output. + pub fn execute_returns_raw(mut self, output: Vec) { + let res = self.execute(); + + match res { + Some(Err(PrecompileFailure::Revert { output, .. })) => { + let decoded = decode_revert_message(&output); + eprintln!( + "Revert message (bytes): {:?}", + sp_core::hexdisplay::HexDisplay::from(&decoded) + ); + eprintln!( + "Revert message (string): {:?}", + core::str::from_utf8(decoded).ok() + ); + panic!("Shouldn't have reverted"); + } + Some(Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: execution_output, + })) => { + if execution_output != output { + eprintln!( + "Output (bytes): {:?}", + sp_core::hexdisplay::HexDisplay::from(&execution_output) + ); + eprintln!( + "Output (string): {:?}", + core::str::from_utf8(&execution_output).ok() + ); + panic!("Output doesn't match"); + } + } + other => panic!("Unexpected result: {:?}", other), + } + + self.assert_optionals(); + } + + /// Execute the precompile set and check it returns provided Solidity encoded output. + pub fn execute_returns(self, output: Vec) { + self.execute_returns_raw(output) + } + + /// Execute the precompile set and check if it reverts. + /// Take a closure allowing to perform custom matching on the output. + pub fn execute_reverts(mut self, check: impl Fn(&[u8]) -> bool) { + let res = self.execute(); + assert_matches!( + res, + Some(Err(PrecompileFailure::Revert { output, ..})) + if check(&output) + ); + self.assert_optionals(); + } + + /// Execute the precompile set and check it returns provided output. + pub fn execute_error(mut self, error: ExitError) { + let res = self.execute(); + assert_eq!( + res, + Some(Err(PrecompileFailure::Error { exit_status: error })) + ); + self.assert_optionals(); + } +} + +pub trait PrecompileTesterExt: PrecompileSet + Sized { + fn prepare_test( + &self, + from: impl Into, + to: impl Into, + data: impl Into>, + ) -> PrecompilesTester; +} + +impl PrecompileTesterExt for T { + fn prepare_test( + &self, + from: impl Into, + to: impl Into, + data: impl Into>, + ) -> PrecompilesTester { + PrecompilesTester::new(self, from, to, data.into()) + } +} diff --git a/precompiles/utils/src/testing/handle.rs b/precompiles/utils/src/testing/handle.rs new file mode 100644 index 0000000000..b88714e7eb --- /dev/null +++ b/precompiles/utils/src/testing/handle.rs @@ -0,0 +1,220 @@ +// This file is part of Astar. + +// Copyright 2019-2022 PureStake Inc. +// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Utils package, originally developed by Purestake Inc. +// Utils package used in Astar Network in terms of GPLv3. +// +// Utils 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. + +// Utils 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 Utils. If not, see . + +use { + crate::testing::PrettyLog, + evm::{ExitRevert, ExitSucceed}, + fp_evm::{Context, ExitError, ExitReason, Log, PrecompileHandle, Transfer}, + sp_core::{H160, H256}, + sp_std::boxed::Box, +}; + +#[derive(Debug, Clone)] +pub struct Subcall { + pub address: H160, + pub transfer: Option, + pub input: Vec, + pub target_gas: Option, + pub is_static: bool, + pub context: Context, +} + +#[derive(Debug, Clone)] +pub struct SubcallOutput { + pub reason: ExitReason, + pub output: Vec, + pub cost: u64, + pub logs: Vec, +} + +impl SubcallOutput { + pub fn revert() -> Self { + Self { + reason: ExitReason::Revert(ExitRevert::Reverted), + output: Vec::new(), + cost: 0, + logs: Vec::new(), + } + } + + pub fn succeed() -> Self { + Self { + reason: ExitReason::Succeed(ExitSucceed::Returned), + output: Vec::new(), + cost: 0, + logs: Vec::new(), + } + } + + pub fn out_of_gas() -> Self { + Self { + reason: ExitReason::Error(ExitError::OutOfGas), + output: Vec::new(), + cost: 0, + logs: Vec::new(), + } + } +} + +pub trait SubcallTrait: FnMut(Subcall) -> SubcallOutput + 'static {} + +impl SubcallOutput + 'static> SubcallTrait for T {} + +pub type SubcallHandle = Box; + +/// Mock handle to write tests for precompiles. +pub struct MockHandle { + pub gas_limit: u64, + pub gas_used: u64, + pub logs: Vec, + pub subcall_handle: Option, + pub code_address: H160, + pub input: Vec, + pub context: Context, + pub is_static: bool, +} + +impl MockHandle { + pub fn new(code_address: H160, context: Context) -> Self { + Self { + gas_limit: u64::MAX, + gas_used: 0, + logs: vec![], + subcall_handle: None, + code_address, + input: Vec::new(), + context, + is_static: false, + } + } +} + +impl PrecompileHandle for MockHandle { + /// Perform subcall in provided context. + /// Precompile specifies in which context the subcall is executed. + fn call( + &mut self, + address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + context: &Context, + ) -> (ExitReason, Vec) { + if self + .record_cost(crate::call_cost( + context.apparent_value, + &evm::Config::london(), + )) + .is_err() + { + return (ExitReason::Error(ExitError::OutOfGas), vec![]); + } + + match &mut self.subcall_handle { + Some(handle) => { + let SubcallOutput { + reason, + output, + cost, + logs, + } = handle(Subcall { + address, + transfer, + input, + target_gas, + is_static, + context: context.clone(), + }); + + if self.record_cost(cost).is_err() { + return (ExitReason::Error(ExitError::OutOfGas), vec![]); + } + + for log in logs { + self.log(log.address, log.topics, log.data) + .expect("cannot fail"); + } + + (reason, output) + } + None => panic!("no subcall handle registered"), + } + } + + fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { + self.gas_used += cost; + + if self.gas_used > self.gas_limit { + Err(ExitError::OutOfGas) + } else { + Ok(()) + } + } + + fn record_external_cost( + &mut self, + _ref_time: Option, + _proof_size: Option, + ) -> Result<(), ExitError> { + Ok(()) + } + fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} + + fn remaining_gas(&self) -> u64 { + self.gas_limit - self.gas_used + } + + fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError> { + self.logs.push(PrettyLog(Log { + address, + topics, + data, + })); + Ok(()) + } + + /// Retreive the code address (what is the address of the precompile being called). + fn code_address(&self) -> H160 { + self.code_address + } + + /// Retreive the input data the precompile is called with. + fn input(&self) -> &[u8] { + &self.input + } + + /// Retreive the context in which the precompile is executed. + fn context(&self) -> &Context { + &self.context + } + + /// Is the precompile call is done statically. + fn is_static(&self) -> bool { + self.is_static + } + + /// Retreive the gas limit of this call. + fn gas_limit(&self) -> Option { + Some(self.gas_limit) + } +} diff --git a/precompiles/utils/src/testing/mod.rs b/precompiles/utils/src/testing/mod.rs new file mode 100644 index 0000000000..be6c5a881c --- /dev/null +++ b/precompiles/utils/src/testing/mod.rs @@ -0,0 +1,98 @@ +// This file is part of Astar. + +// Copyright 2019-2022 PureStake Inc. +// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Utils package, originally developed by Purestake Inc. +// Utils package used in Astar Network in terms of GPLv3. +// +// Utils 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. + +// Utils 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 Utils. If not, see . + +pub mod account; +pub mod execution; +pub mod handle; + +pub use {account::*, execution::*, handle::*}; + +use fp_evm::Log; + +pub fn decode_revert_message(encoded: &[u8]) -> &[u8] { + let encoded_len = encoded.len(); + // selector 4 + offset 32 + string length 32 + if encoded_len > 68 { + let message_len = encoded[36..68].iter().sum::(); + if encoded_len >= 68 + message_len as usize { + return &encoded[68..68 + message_len as usize]; + } + } + b"decode_revert_message: error" +} + +#[derive(Clone, PartialEq, Eq)] +pub struct PrettyLog(Log); + +impl core::fmt::Debug for PrettyLog { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + let bytes = self + .0 + .data + .iter() + .map(|b| format!("{:02X}", b)) + .collect::>() + .join(""); + + let message = String::from_utf8(self.0.data.clone()).ok(); + + f.debug_struct("Log") + .field("address", &self.0.address) + .field("topics", &self.0.topics) + .field("data", &bytes) + .field("data_utf8", &message) + .finish() + } +} +/// Panics if an event is not found in the system log of events +#[macro_export] +macro_rules! assert_event_emitted { + ($event:expr) => { + match &$event { + e => { + assert!( + crate::mock::events().iter().find(|x| *x == e).is_some(), + "Event {:?} was not found in events: \n {:?}", + e, + crate::mock::events() + ); + } + } + }; +} + +// Panics if an event is found in the system log of events +#[macro_export] +macro_rules! assert_event_not_emitted { + ($event:expr) => { + match &$event { + e => { + assert!( + crate::mock::events().iter().find(|x| *x == e).is_none(), + "Event {:?} was found in events: \n {:?}", + e, + crate::mock::events() + ); + } + } + }; +} diff --git a/runtime/local/Cargo.toml b/runtime/local/Cargo.toml index b49f360404..77a300febf 100644 --- a/runtime/local/Cargo.toml +++ b/runtime/local/Cargo.toml @@ -28,6 +28,7 @@ pallet-democracy = { workspace = true } pallet-ethereum = { workspace = true } pallet-ethereum-checked = { workspace = true } pallet-evm = { workspace = true } +pallet-evm-precompile-batch = { workspace = true } pallet-evm-precompile-blake2 = { workspace = true } pallet-evm-precompile-bn128 = { workspace = true } pallet-evm-precompile-dispatch = { workspace = true } @@ -127,6 +128,7 @@ std = [ "pallet-evm-precompile-modexp/std", "pallet-evm-precompile-sha3fips/std", "pallet-evm-precompile-dapps-staking/std", + "pallet-evm-precompile-batch/std", "pallet-evm-precompile-sr25519/std", "pallet-evm-precompile-substrate-ecdsa/std", "pallet-evm-precompile-xvm/std", diff --git a/runtime/local/src/precompiles.rs b/runtime/local/src/precompiles.rs index 9e0bf989d9..8520a7522a 100644 --- a/runtime/local/src/precompiles.rs +++ b/runtime/local/src/precompiles.rs @@ -23,6 +23,7 @@ use pallet_evm::{ PrecompileResult, PrecompileSet, }; use pallet_evm_precompile_assets_erc20::{AddressToAssetId, Erc20AssetsPrecompileSet}; +use pallet_evm_precompile_batch::BatchPrecompile; use pallet_evm_precompile_blake2::Blake2F; use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; use pallet_evm_precompile_dapps_staking::DappsStakingWrapper; @@ -54,9 +55,11 @@ impl LocalNetworkPrecompiles { /// Return all addresses that contain precompiles. This can be used to populate dummy code /// under the precompile. pub fn used_addresses() -> impl Iterator { - sp_std::vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1025, 1026, 1027, 20481, 20482, 20483, 20485] - .into_iter() - .map(hash) + sp_std::vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1025, 1026, 1027, 20481, 20482, 20483, 20485, 20846 + ] + .into_iter() + .map(hash) } } @@ -67,6 +70,7 @@ impl PrecompileSet for LocalNetworkPrecompiles where Erc20AssetsPrecompileSet: PrecompileSet, DappsStakingWrapper: Precompile, + BatchPrecompile: Precompile, XvmPrecompile>: Precompile, Dispatch: Precompile, R: pallet_evm::Config @@ -113,6 +117,9 @@ where a if a == hash(20485) => { Some(XvmPrecompile::>::execute(handle)) } + // Batch 0x5006 + a if a == hash(20486) => Some(BatchPrecompile::::execute(handle)), + // If the address matches asset prefix, the we route through the asset precompile set a if &a.to_fixed_bytes()[0..4] == ASSET_PRECOMPILE_ADDRESS_PREFIX => { Erc20AssetsPrecompileSet::::new().execute(handle) diff --git a/runtime/shibuya/Cargo.toml b/runtime/shibuya/Cargo.toml index f02da1a5fb..5658d2e833 100644 --- a/runtime/shibuya/Cargo.toml +++ b/runtime/shibuya/Cargo.toml @@ -105,6 +105,7 @@ pallet-custom-signatures = { workspace = true } pallet-dapps-staking = { workspace = true } pallet-ethereum-checked = { workspace = true } pallet-evm-precompile-assets-erc20 = { workspace = true } +pallet-evm-precompile-batch = { workspace = true } pallet-evm-precompile-dapps-staking = { workspace = true } pallet-evm-precompile-sr25519 = { workspace = true } pallet-evm-precompile-substrate-ecdsa = { workspace = true } @@ -180,6 +181,7 @@ std = [ "pallet-evm-precompile-modexp/std", "pallet-evm-precompile-sha3fips/std", "pallet-evm-precompile-dapps-staking/std", + "pallet-evm-precompile-batch/std", "pallet-evm-precompile-sr25519/std", "pallet-evm-precompile-substrate-ecdsa/std", "pallet-evm-precompile-assets-erc20/std", diff --git a/runtime/shibuya/src/precompiles.rs b/runtime/shibuya/src/precompiles.rs index d84839aa4b..3761e33ff4 100644 --- a/runtime/shibuya/src/precompiles.rs +++ b/runtime/shibuya/src/precompiles.rs @@ -23,6 +23,7 @@ use pallet_evm::{ PrecompileResult, PrecompileSet, }; use pallet_evm_precompile_assets_erc20::{AddressToAssetId, Erc20AssetsPrecompileSet}; +use pallet_evm_precompile_batch::BatchPrecompile; use pallet_evm_precompile_blake2::Blake2F; use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; use pallet_evm_precompile_dapps_staking::DappsStakingWrapper; @@ -58,7 +59,8 @@ impl ShibuyaNetworkPrecompiles { /// under the precompile. pub fn used_addresses() -> impl Iterator { sp_std::vec![ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1025, 1026, 1027, 20481, 20482, 20483, 20484, 20485 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1025, 1026, 1027, 20481, 20482, 20483, 20484, 20485, + 20486 ] .into_iter() .map(hash) @@ -73,6 +75,7 @@ where Erc20AssetsPrecompileSet: PrecompileSet, DappsStakingWrapper: Precompile, XcmPrecompile: Precompile, + BatchPrecompile: Precompile, XvmPrecompile>: Precompile, Dispatch: Precompile, R: pallet_evm::Config @@ -123,6 +126,8 @@ where a if a == hash(20485) => { Some(XvmPrecompile::>::execute(handle)) } + // Batch 0x5006 + a if a == hash(20486) => Some(BatchPrecompile::::execute(handle)), // If the address matches asset prefix, the we route through the asset precompile set a if &a.to_fixed_bytes()[0..4] == ASSET_PRECOMPILE_ADDRESS_PREFIX => { Erc20AssetsPrecompileSet::::new().execute(handle) From d9b5c65033eba2127db9a31cc78cbbc71826115c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Pa=C4=8Dandi?= <3002868+Dinonard@users.noreply.github.com> Date: Fri, 11 Aug 2023 15:14:12 +0200 Subject: [PATCH 09/11] Automatic EVM revert code registration for XC20 (#1001) * 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 --- Cargo.lock | 5 + pallets/xc-asset-config/src/lib.rs | 21 - pallets/xc-asset-config/src/mock.rs | 1 - precompiles/assets-erc20/Cargo.toml | 1 + precompiles/assets-erc20/src/mock.rs | 2 + precompiles/xcm/src/mock.rs | 2 + primitives/Cargo.toml | 12 +- primitives/src/benchmarks.rs | 30 ++ primitives/src/evm.rs | 56 +++ primitives/src/lib.rs | 10 +- primitives/src/xcm/mod.rs | 51 +- primitives/src/xcm/tests.rs | 155 +----- runtime/astar/Cargo.toml | 1 + runtime/astar/src/lib.rs | 26 +- runtime/astar/src/weights/mod.rs | 19 + runtime/astar/src/weights/pallet_assets.rs | 496 +++++++++++++++++++ runtime/local/Cargo.toml | 1 + runtime/local/src/lib.rs | 12 +- runtime/local/src/weights/mod.rs | 19 + runtime/local/src/weights/pallet_assets.rs | 496 +++++++++++++++++++ runtime/shibuya/Cargo.toml | 1 + runtime/shibuya/src/lib.rs | 30 +- runtime/shibuya/src/weights/mod.rs | 19 + runtime/shibuya/src/weights/pallet_assets.rs | 496 +++++++++++++++++++ runtime/shiden/Cargo.toml | 1 + runtime/shiden/src/lib.rs | 26 +- runtime/shiden/src/weights/mod.rs | 19 + runtime/shiden/src/weights/pallet_assets.rs | 496 +++++++++++++++++++ scripts/templates/weight-template.hbs | 18 + tests/integration/Cargo.toml | 2 + tests/integration/src/assets.rs | 81 +++ tests/integration/src/lib.rs | 3 + tests/integration/src/setup.rs | 2 +- tests/xcm-simulator/src/mocks/parachain.rs | 3 +- 34 files changed, 2323 insertions(+), 290 deletions(-) create mode 100644 primitives/src/benchmarks.rs create mode 100644 primitives/src/evm.rs create mode 100644 runtime/astar/src/weights/mod.rs create mode 100644 runtime/astar/src/weights/pallet_assets.rs create mode 100644 runtime/local/src/weights/mod.rs create mode 100644 runtime/local/src/weights/pallet_assets.rs create mode 100644 runtime/shibuya/src/weights/mod.rs create mode 100644 runtime/shibuya/src/weights/pallet_assets.rs create mode 100644 runtime/shiden/src/weights/mod.rs create mode 100644 runtime/shiden/src/weights/pallet_assets.rs create mode 100644 tests/integration/src/assets.rs diff --git a/Cargo.lock b/Cargo.lock index 9aa74cd3fe..b1dd542a8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -515,6 +515,9 @@ dependencies = [ "frame-support", "impl-trait-for-tuples", "log", + "pallet-assets", + "pallet-evm", + "pallet-evm-precompile-assets-erc20", "pallet-xc-asset-config", "parity-scale-codec", "scale-info", @@ -4461,12 +4464,14 @@ dependencies = [ "frame-support", "frame-system", "hex", + "pallet-assets", "pallet-balances", "pallet-contracts", "pallet-contracts-primitives", "pallet-dapps-staking", "pallet-ethereum-checked", "pallet-evm", + "pallet-evm-precompile-assets-erc20", "pallet-proxy", "pallet-utility", "parity-scale-codec", diff --git a/pallets/xc-asset-config/src/lib.rs b/pallets/xc-asset-config/src/lib.rs index 4310198def..193cdf961f 100644 --- a/pallets/xc-asset-config/src/lib.rs +++ b/pallets/xc-asset-config/src/lib.rs @@ -85,21 +85,6 @@ pub mod pallet { #[pallet::without_storage_info] pub struct Pallet(PhantomData); - /// Callback definition trait for cross-chain asset registration/deregistration notifications. - pub trait XcAssetChanged { - /// 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 XcAssetChanged 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 { /// Get asset type from assetId @@ -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; - /// The required origin for managing cross-chain asset configuration /// /// Should most likely be root. @@ -242,8 +224,6 @@ pub mod pallet { AssetIdToLocation::::insert(&asset_id, asset_location.clone()); AssetLocationToId::::insert(&asset_location, asset_id); - T::XcAssetChanged::xc_asset_registered(asset_id); - Self::deposit_event(Event::AssetRegistered { asset_location, asset_id, @@ -354,7 +334,6 @@ pub mod pallet { AssetIdToLocation::::remove(&asset_id); AssetLocationToId::::remove(&asset_location); AssetLocationUnitsPerSecond::::remove(&asset_location); - T::XcAssetChanged::xc_asset_unregistered(asset_id); Self::deposit_event(Event::AssetRemoved { asset_id, diff --git a/pallets/xc-asset-config/src/mock.rs b/pallets/xc-asset-config/src/mock.rs index aa00246b1f..f15736b8b8 100644 --- a/pallets/xc-asset-config/src/mock.rs +++ b/pallets/xc-asset-config/src/mock.rs @@ -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; type WeightInfo = (); } diff --git a/precompiles/assets-erc20/Cargo.toml b/precompiles/assets-erc20/Cargo.toml index cafa20e6b9..f70e155468 100644 --- a/precompiles/assets-erc20/Cargo.toml +++ b/precompiles/assets-erc20/Cargo.toml @@ -55,3 +55,4 @@ std = [ "sp-runtime/std", "sp-std/std", ] +runtime-benchmarks = [] diff --git a/precompiles/assets-erc20/src/mock.rs b/precompiles/assets-erc20/src/mock.rs index 3d7ec86fca..47c4ba498b 100644 --- a/precompiles/assets-erc20/src/mock.rs +++ b/precompiles/assets-erc20/src/mock.rs @@ -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. diff --git a/precompiles/xcm/src/mock.rs b/precompiles/xcm/src/mock.rs index 85f1ddf094..b2d2ace443 100644 --- a/precompiles/xcm/src/mock.rs +++ b/precompiles/xcm/src/mock.rs @@ -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(PhantomData); diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 4c8eef897c..6a4306fe00 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -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 } @@ -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] @@ -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"] diff --git a/primitives/src/benchmarks.rs b/primitives/src/benchmarks.rs new file mode 100644 index 0000000000..f0cfa392df --- /dev/null +++ b/primitives/src/benchmarks.rs @@ -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 . + +use crate::AssetId; + +#[cfg(feature = "runtime-benchmarks")] +/// Benchmark helper for `pallet-assets`. +pub struct AssetsBenchmarkHelper; +impl> pallet_assets::BenchmarkHelper + for AssetsBenchmarkHelper +{ + fn create_asset_id_parameter(id: u32) -> AssetIdParameter { + AssetId::from(id).into() + } +} diff --git a/primitives/src/evm.rs b/primitives/src/evm.rs new file mode 100644 index 0000000000..25d1599219 --- /dev/null +++ b/primitives/src/evm.rs @@ -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 . + +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(PhantomData<(A, R)>); +impl AssetsCallback for EvmRevertCodeHandler +where + A: AddressToAssetId, + 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::::contains_key(&address), ()); + pallet_evm::AccountCodes::::insert(address, EVM_REVERT_CODE.to_vec()); + Ok(()) + } + + fn destroyed(id: &AssetId) -> Result<(), ()> { + let address = A::asset_id_to_address(*id); + pallet_evm::AccountCodes::::remove(address); + Ok(()) + } +} diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 777ba49fd9..9cf71cb373 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -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. diff --git a/primitives/src/xcm/mod.rs b/primitives/src/xcm/mod.rs index f8ce3e4eb0..66210bf95f 100644 --- a/primitives/src/xcm/mod.rs +++ b/primitives/src/xcm/mod.rs @@ -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}; @@ -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}; @@ -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(PhantomData); -impl> ShouldExecute for AllowPaidExecWithDescendOriginFrom { - fn should_execute( - origin: &MultiLocation, - message: &mut [Instruction], - 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::>() - .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}; diff --git a/primitives/src/xcm/tests.rs b/primitives/src/xcm/tests.rs index faef681aaf..21dcfbeaef 100644 --- a/primitives/src/xcm/tests.rs +++ b/primitives/src/xcm/tests.rs @@ -17,10 +17,7 @@ // along with Astar. If not, see . use super::*; -use frame_support::{ - assert_ok, - traits::{Everything, Nothing}, -}; +use frame_support::assert_ok; use sp_runtime::traits::Zero; use xcm_executor::traits::Convert; @@ -411,156 +408,6 @@ fn reserve_asset_filter_for_unsupported_asset_multi_location() { assert!(!ReserveAssetFilter::contains(&multi_asset, &origin)); } -/// Returns valid XCM sequence for bypassing `AllowPaidExecWithDescendOriginFrom` -fn desc_origin_barrier_valid_sequence() -> Vec> { - vec![ - DescendOrigin(X1(Junction::Parachain(1234))), - WithdrawAsset((Here, 100).into()), - BuyExecution { - fees: (Here, 100).into(), - weight_limit: WeightLimit::Unlimited, - }, - ] -} - -#[test] -fn allow_paid_exec_with_descend_origin_works() { - let mut valid_message = desc_origin_barrier_valid_sequence(); - - let res = AllowPaidExecWithDescendOriginFrom::::should_execute( - &Here.into(), - &mut valid_message, - Weight::from_parts(150, 0), - &mut Weight::zero(), - ); - assert_eq!(res, Ok(())); - - // Still works even if there are follow-up instructions - valid_message = desc_origin_barrier_valid_sequence(); - valid_message.push(SetErrorHandler(Default::default())); - let res = AllowPaidExecWithDescendOriginFrom::::should_execute( - &Here.into(), - &mut valid_message, - Weight::from_parts(100, 0), - &mut Weight::zero(), - ); - assert_eq!(res, Ok(())); -} - -#[test] -fn allow_paid_exec_with_descend_origin_with_weight_correction_works() { - let mut valid_message = desc_origin_barrier_valid_sequence(); - - // Ensure that `Limited` gets adjusted to the provided enforced_weight_limit - let enforced_weight_limit = Weight::from_parts(3, 0); - let res = AllowPaidExecWithDescendOriginFrom::::should_execute( - &Here.into(), - &mut valid_message, - enforced_weight_limit, - &mut Weight::zero(), - ); - assert_eq!(res, Ok(())); - - if let BuyExecution { - weight_limit, - fees: _, - } = valid_message[2].clone() - { - assert_eq!(weight_limit, WeightLimit::Limited(enforced_weight_limit)) - } else { - panic!("3rd instruction should be BuyExecution!"); - } - - // Ensure that we use `BuyExecution` with `Unlimited` weight limit - let _ = std::mem::replace( - &mut valid_message[2], - BuyExecution { - fees: (Here, 100).into(), - weight_limit: WeightLimit::Limited(enforced_weight_limit.add_ref_time(7)), - }, - ); - - // Ensure that `Unlimited` gets adjusted to the provided max weight limit - let res = AllowPaidExecWithDescendOriginFrom::::should_execute( - &Here.into(), - &mut valid_message, - enforced_weight_limit, - &mut Weight::zero(), - ); - assert_eq!(res, Ok(())); - - if let BuyExecution { - weight_limit, - fees: _, - } = valid_message[2].clone() - { - assert_eq!(weight_limit, WeightLimit::Limited(enforced_weight_limit)) - } else { - panic!("3rd instruction should be BuyExecution!"); - } -} - -#[test] -fn allow_paid_exec_with_descend_origin_with_unsupported_origin_fails() { - let mut valid_message = desc_origin_barrier_valid_sequence(); - - let res = AllowPaidExecWithDescendOriginFrom::::should_execute( - &Here.into(), - &mut valid_message, - Weight::from_parts(100, 0), - &mut Weight::zero(), - ); - assert_eq!(res, Err(ProcessMessageError::Unsupported)); -} - -#[test] -fn allow_paid_exec_with_descend_origin_with_invalid_message_fails() { - let mut invalid_message = vec![WithdrawAsset((Here, 100).into())]; - - let res = AllowPaidExecWithDescendOriginFrom::::should_execute( - &Here.into(), - &mut invalid_message, - Weight::from_parts(100, 0), - &mut Weight::zero(), - ); - assert_eq!(res, Err(ProcessMessageError::Unsupported)); - - // Should still fail, even if correct sequence follows next - invalid_message.append(&mut desc_origin_barrier_valid_sequence()); - let res = AllowPaidExecWithDescendOriginFrom::::should_execute( - &Here.into(), - &mut invalid_message, - Weight::from_parts(100, 0), - &mut Weight::zero(), - ); - assert_eq!(res, Err(ProcessMessageError::Unsupported)); -} - -#[test] -fn allow_paid_exec_with_descend_origin_too_small_weight_fails() { - let mut valid_message = desc_origin_barrier_valid_sequence(); - let enforced_weight_limit = Weight::from_parts(29, 0); - - // Ensure that we use `BuyExecution` with `Limited` weight but with insufficient weight. - // This means that not enough execution time (weight) is being bought compared to the - // weight of whole sequence. - let _ = std::mem::replace( - &mut valid_message[2], - BuyExecution { - fees: (Here, 100).into(), - weight_limit: WeightLimit::Limited(enforced_weight_limit.sub_ref_time(7)), - }, - ); - - let res = AllowPaidExecWithDescendOriginFrom::::should_execute( - &Here.into(), - &mut valid_message, - enforced_weight_limit, - &mut Weight::zero(), - ); - assert_eq!(res, Err(ProcessMessageError::Unsupported)); -} - // TODO: can be deleted after uplift to `polkadot-v0.9.44` or beyond. #[test] fn hashed_description_sanity_check() { diff --git a/runtime/astar/Cargo.toml b/runtime/astar/Cargo.toml index 07f8fec6f7..2dcb04a7b5 100644 --- a/runtime/astar/Cargo.toml +++ b/runtime/astar/Cargo.toml @@ -219,6 +219,7 @@ runtime-benchmarks = [ "pallet-collator-selection/runtime-benchmarks", "polkadot-runtime/runtime-benchmarks", "astar-primitives/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", ] try-runtime = [ "fp-self-contained/try-runtime", diff --git a/runtime/astar/src/lib.rs b/runtime/astar/src/lib.rs index 242a4dfd9a..1b8e7e4424 100644 --- a/runtime/astar/src/lib.rs +++ b/runtime/astar/src/lib.rs @@ -23,7 +23,8 @@ #![recursion_limit = "256"] pub use astar_primitives::{ - AccountId, Address, AssetId, Balance, BlockNumber, Hash, Header, Index, Signature, + evm::EvmRevertCodeHandler, xcm::AssetLocationIdConverter, AccountId, Address, AssetId, Balance, + BlockNumber, Hash, Header, Index, Signature, }; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use frame_support::{ @@ -71,7 +72,6 @@ use sp_runtime::{ }; use sp_std::prelude::*; -use astar_primitives::xcm::AssetLocationIdConverter; use pallet_evm_precompile_assets_erc20::AddressToAssetId; #[cfg(any(feature = "std", test))] @@ -85,6 +85,7 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::BuildStorage; mod precompiles; +mod weights; mod xcm_config; pub type AstarAssetLocationIdConverter = AssetLocationIdConverter; @@ -580,10 +581,12 @@ impl pallet_assets::Config for Runtime { type StringLimit = AssetsStringLimit; type Freezer = (); type Extra = (); - type WeightInfo = pallet_assets::weights::SubstrateWeight; + type WeightInfo = weights::pallet_assets::SubstrateWeight; type RemoveItemsLimit = ConstU32<1000>; type AssetIdParameter = Compact; - type CallbackHandle = (); + type CallbackHandle = EvmRevertCodeHandler; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = astar_primitives::benchmarks::AssetsBenchmarkHelper; } parameter_types! { @@ -834,23 +837,9 @@ impl pallet_sudo::Config for Runtime { type WeightInfo = pallet_sudo::weights::SubstrateWeight; } -pub struct EvmRevertCodeHandler; -impl pallet_xc_asset_config::XcAssetChanged for EvmRevertCodeHandler { - fn xc_asset_registered(asset_id: AssetId) { - let address = Runtime::asset_id_to_address(asset_id); - pallet_evm::AccountCodes::::insert(address, vec![0x60, 0x00, 0x60, 0x00, 0xfd]); - } - - fn xc_asset_unregistered(asset_id: AssetId) { - let address = Runtime::asset_id_to_address(asset_id); - pallet_evm::AccountCodes::::remove(address); - } -} - impl pallet_xc_asset_config::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssetId = AssetId; - type XcAssetChanged = EvmRevertCodeHandler; type ManagerOrigin = EnsureRoot; type WeightInfo = pallet_xc_asset_config::weights::SubstrateWeight; } @@ -1163,6 +1152,7 @@ mod benches { define_benchmarks!( [frame_benchmarking, BaselineBench::] [frame_system, SystemBench::] + [pallet_assets, Assets] [pallet_balances, Balances] [pallet_timestamp, Timestamp] [pallet_dapps_staking, DappsStaking] diff --git a/runtime/astar/src/weights/mod.rs b/runtime/astar/src/weights/mod.rs new file mode 100644 index 0000000000..2db3ac12ab --- /dev/null +++ b/runtime/astar/src/weights/mod.rs @@ -0,0 +1,19 @@ +// 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 . + +pub mod pallet_assets; diff --git a/runtime/astar/src/weights/pallet_assets.rs b/runtime/astar/src/weights/pallet_assets.rs new file mode 100644 index 0000000000..676729637e --- /dev/null +++ b/runtime/astar/src/weights/pallet_assets.rs @@ -0,0 +1,496 @@ +// 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 . + +//! Autogenerated weights for pallet_assets +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `devserver-01`, CPU: `Intel(R) Xeon(R) E-2236 CPU @ 3.40GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("astar-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/astar-collator +// benchmark +// pallet +// --chain=astar-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_assets +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./benchmark-results/assets_weights.rs +// --template=./scripts/templates/weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weights for pallet_assets using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_assets::WeightInfo for SubstrateWeight { + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:1 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `659` + // Estimated: `4124` + // Minimum execution time: 35_155_000 picoseconds. + Weight::from_parts(35_760_000, 4124) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:1 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn force_create() -> Weight { + // Proof Size summary in bytes: + // Measured: `519` + // Estimated: `3984` + // Minimum execution time: 20_004_000 picoseconds. + Weight::from_parts(20_348_000, 3984) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn start_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_682_000 picoseconds. + Weight::from_parts(14_059_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1001 w:1000) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1000 w:1000) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `c` is `[0, 1000]`. + fn destroy_accounts(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255 + c * (208 ±0)` + // Estimated: `3687 + c * (2621 ±0)` + // Minimum execution time: 17_910_000 picoseconds. + Weight::from_parts(18_110_000, 3687) + // Standard Error: 6_525 + .saturating_add(Weight::from_parts(11_623_553, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2621).saturating_mul(c.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1001 w:1000) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + /// The range of component `a` is `[0, 1000]`. + fn destroy_approvals(a: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `438 + a * (86 ±0)` + // Estimated: `3687 + a * (2635 ±0)` + // Minimum execution time: 18_065_000 picoseconds. + Weight::from_parts(18_372_000, 3687) + // Standard Error: 4_985 + .saturating_add(Weight::from_parts(13_662_720, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_parts(0, 2635).saturating_mul(a.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:0 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn finish_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 16_700_000 picoseconds. + Weight::from_parts(17_418_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn mint() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 26_305_000 picoseconds. + Weight::from_parts(27_014_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn burn() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 30_525_000 picoseconds. + Weight::from_parts(31_113_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 42_955_000 picoseconds. + Weight::from_parts(43_309_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 37_951_000 picoseconds. + Weight::from_parts(38_330_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 43_061_000 picoseconds. + Weight::from_parts(43_561_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn freeze() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_259_000 picoseconds. + Weight::from_parts(17_516_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn thaw() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_165_000 picoseconds. + Weight::from_parts(17_547_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn freeze_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_634_000 picoseconds. + Weight::from_parts(14_069_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn thaw_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_456_000 picoseconds. + Weight::from_parts(13_770_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn transfer_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 16_083_000 picoseconds. + Weight::from_parts(16_343_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn set_team() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 14_443_000 picoseconds. + Weight::from_parts(14_818_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn set_metadata(_n: u32, _s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 28_260_000 picoseconds. + Weight::from_parts(29_181_263, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `431` + // Estimated: `3687` + // Minimum execution time: 28_580_000 picoseconds. + Weight::from_parts(28_826_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn force_set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `94` + // Estimated: `3687` + // Minimum execution time: 14_605_000 picoseconds. + Weight::from_parts(15_107_642, 3687) + // Standard Error: 450 + .saturating_add(Weight::from_parts(6_608, 0).saturating_mul(n.into())) + // Standard Error: 450 + .saturating_add(Weight::from_parts(2_783, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn force_clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `431` + // Estimated: `3687` + // Minimum execution time: 27_925_000 picoseconds. + Weight::from_parts(28_323_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn force_asset_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 13_904_000 picoseconds. + Weight::from_parts(14_431_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn approve_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 31_152_000 picoseconds. + Weight::from_parts(31_715_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer_approved() -> Weight { + // Proof Size summary in bytes: + // Measured: `680` + // Estimated: `6232` + // Minimum execution time: 63_252_000 picoseconds. + Weight::from_parts(63_718_000, 6232) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `471` + // Estimated: `3687` + // Minimum execution time: 34_375_000 picoseconds. + Weight::from_parts(34_640_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn force_cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `471` + // Estimated: `3687` + // Minimum execution time: 34_159_000 picoseconds. + Weight::from_parts(34_693_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn set_min_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 15_114_000 picoseconds. + Weight::from_parts(15_271_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn touch() -> Weight { + // Proof Size summary in bytes: + // Measured: `429` + // Estimated: `3687` + // Minimum execution time: 34_356_000 picoseconds. + Weight::from_parts(34_856_000, 3687) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn touch_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 31_063_000 picoseconds. + Weight::from_parts(31_331_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn refund() -> Weight { + // Proof Size summary in bytes: + // Measured: `567` + // Estimated: `3687` + // Minimum execution time: 31_678_000 picoseconds. + Weight::from_parts(31_941_000, 3687) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn refund_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `426` + // Estimated: `3687` + // Minimum execution time: 28_601_000 picoseconds. + Weight::from_parts(28_966_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn block() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_545_000 picoseconds. + Weight::from_parts(17_959_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/runtime/local/Cargo.toml b/runtime/local/Cargo.toml index 77a300febf..6226f6fe8a 100644 --- a/runtime/local/Cargo.toml +++ b/runtime/local/Cargo.toml @@ -184,6 +184,7 @@ runtime-benchmarks = [ "pallet-preimage/runtime-benchmarks", "pallet-ethereum-checked/runtime-benchmarks", "astar-primitives/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", ] try-runtime = [ "fp-self-contained/try-runtime", diff --git a/runtime/local/src/lib.rs b/runtime/local/src/lib.rs index cb90692dab..49815eddfe 100644 --- a/runtime/local/src/lib.rs +++ b/runtime/local/src/lib.rs @@ -62,7 +62,8 @@ use sp_runtime::{ use sp_std::prelude::*; pub use astar_primitives::{ - AccountId, Address, AssetId, Balance, BlockNumber, Hash, Header, Index, Signature, + evm::EvmRevertCodeHandler, AccountId, Address, AssetId, Balance, BlockNumber, Hash, Header, + Index, Signature, }; #[cfg(feature = "std")] @@ -115,6 +116,8 @@ pub type Precompiles = LocalNetworkPrecompiles; mod chain_extensions; pub use chain_extensions::*; +mod weights; + /// Constant values used within the runtime. pub const MICROAST: Balance = 1_000_000_000_000; pub const MILLIAST: Balance = 1_000 * MICROAST; @@ -322,10 +325,12 @@ impl pallet_assets::Config for Runtime { type StringLimit = AssetsStringLimit; type Freezer = (); type Extra = (); - type WeightInfo = pallet_assets::weights::SubstrateWeight; + type WeightInfo = weights::pallet_assets::SubstrateWeight; type RemoveItemsLimit = ConstU32<1000>; type AssetIdParameter = Compact; - type CallbackHandle = (); + type CallbackHandle = EvmRevertCodeHandler; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = astar_primitives::benchmarks::AssetsBenchmarkHelper; } parameter_types! { @@ -1137,6 +1142,7 @@ extern crate frame_benchmarking; mod benches { define_benchmarks!( [frame_benchmarking, BaselineBench::] + [pallet_assets, Assets] [frame_system, SystemBench::] [pallet_balances, Balances] [pallet_timestamp, Timestamp] diff --git a/runtime/local/src/weights/mod.rs b/runtime/local/src/weights/mod.rs new file mode 100644 index 0000000000..2db3ac12ab --- /dev/null +++ b/runtime/local/src/weights/mod.rs @@ -0,0 +1,19 @@ +// 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 . + +pub mod pallet_assets; diff --git a/runtime/local/src/weights/pallet_assets.rs b/runtime/local/src/weights/pallet_assets.rs new file mode 100644 index 0000000000..676729637e --- /dev/null +++ b/runtime/local/src/weights/pallet_assets.rs @@ -0,0 +1,496 @@ +// 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 . + +//! Autogenerated weights for pallet_assets +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `devserver-01`, CPU: `Intel(R) Xeon(R) E-2236 CPU @ 3.40GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("astar-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/astar-collator +// benchmark +// pallet +// --chain=astar-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_assets +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./benchmark-results/assets_weights.rs +// --template=./scripts/templates/weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weights for pallet_assets using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_assets::WeightInfo for SubstrateWeight { + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:1 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `659` + // Estimated: `4124` + // Minimum execution time: 35_155_000 picoseconds. + Weight::from_parts(35_760_000, 4124) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:1 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn force_create() -> Weight { + // Proof Size summary in bytes: + // Measured: `519` + // Estimated: `3984` + // Minimum execution time: 20_004_000 picoseconds. + Weight::from_parts(20_348_000, 3984) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn start_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_682_000 picoseconds. + Weight::from_parts(14_059_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1001 w:1000) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1000 w:1000) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `c` is `[0, 1000]`. + fn destroy_accounts(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255 + c * (208 ±0)` + // Estimated: `3687 + c * (2621 ±0)` + // Minimum execution time: 17_910_000 picoseconds. + Weight::from_parts(18_110_000, 3687) + // Standard Error: 6_525 + .saturating_add(Weight::from_parts(11_623_553, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2621).saturating_mul(c.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1001 w:1000) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + /// The range of component `a` is `[0, 1000]`. + fn destroy_approvals(a: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `438 + a * (86 ±0)` + // Estimated: `3687 + a * (2635 ±0)` + // Minimum execution time: 18_065_000 picoseconds. + Weight::from_parts(18_372_000, 3687) + // Standard Error: 4_985 + .saturating_add(Weight::from_parts(13_662_720, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_parts(0, 2635).saturating_mul(a.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:0 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn finish_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 16_700_000 picoseconds. + Weight::from_parts(17_418_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn mint() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 26_305_000 picoseconds. + Weight::from_parts(27_014_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn burn() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 30_525_000 picoseconds. + Weight::from_parts(31_113_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 42_955_000 picoseconds. + Weight::from_parts(43_309_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 37_951_000 picoseconds. + Weight::from_parts(38_330_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 43_061_000 picoseconds. + Weight::from_parts(43_561_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn freeze() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_259_000 picoseconds. + Weight::from_parts(17_516_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn thaw() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_165_000 picoseconds. + Weight::from_parts(17_547_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn freeze_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_634_000 picoseconds. + Weight::from_parts(14_069_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn thaw_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_456_000 picoseconds. + Weight::from_parts(13_770_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn transfer_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 16_083_000 picoseconds. + Weight::from_parts(16_343_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn set_team() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 14_443_000 picoseconds. + Weight::from_parts(14_818_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn set_metadata(_n: u32, _s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 28_260_000 picoseconds. + Weight::from_parts(29_181_263, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `431` + // Estimated: `3687` + // Minimum execution time: 28_580_000 picoseconds. + Weight::from_parts(28_826_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn force_set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `94` + // Estimated: `3687` + // Minimum execution time: 14_605_000 picoseconds. + Weight::from_parts(15_107_642, 3687) + // Standard Error: 450 + .saturating_add(Weight::from_parts(6_608, 0).saturating_mul(n.into())) + // Standard Error: 450 + .saturating_add(Weight::from_parts(2_783, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn force_clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `431` + // Estimated: `3687` + // Minimum execution time: 27_925_000 picoseconds. + Weight::from_parts(28_323_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn force_asset_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 13_904_000 picoseconds. + Weight::from_parts(14_431_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn approve_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 31_152_000 picoseconds. + Weight::from_parts(31_715_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer_approved() -> Weight { + // Proof Size summary in bytes: + // Measured: `680` + // Estimated: `6232` + // Minimum execution time: 63_252_000 picoseconds. + Weight::from_parts(63_718_000, 6232) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `471` + // Estimated: `3687` + // Minimum execution time: 34_375_000 picoseconds. + Weight::from_parts(34_640_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn force_cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `471` + // Estimated: `3687` + // Minimum execution time: 34_159_000 picoseconds. + Weight::from_parts(34_693_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn set_min_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 15_114_000 picoseconds. + Weight::from_parts(15_271_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn touch() -> Weight { + // Proof Size summary in bytes: + // Measured: `429` + // Estimated: `3687` + // Minimum execution time: 34_356_000 picoseconds. + Weight::from_parts(34_856_000, 3687) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn touch_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 31_063_000 picoseconds. + Weight::from_parts(31_331_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn refund() -> Weight { + // Proof Size summary in bytes: + // Measured: `567` + // Estimated: `3687` + // Minimum execution time: 31_678_000 picoseconds. + Weight::from_parts(31_941_000, 3687) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn refund_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `426` + // Estimated: `3687` + // Minimum execution time: 28_601_000 picoseconds. + Weight::from_parts(28_966_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn block() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_545_000 picoseconds. + Weight::from_parts(17_959_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/runtime/shibuya/Cargo.toml b/runtime/shibuya/Cargo.toml index 5658d2e833..ad17c0f917 100644 --- a/runtime/shibuya/Cargo.toml +++ b/runtime/shibuya/Cargo.toml @@ -259,6 +259,7 @@ runtime-benchmarks = [ "polkadot-runtime/runtime-benchmarks", "orml-xtokens/runtime-benchmarks", "astar-primitives/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", ] try-runtime = [ "fp-self-contained/try-runtime", diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index cfacb68e70..5891d44e95 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -68,11 +68,11 @@ use sp_runtime::{ use sp_std::prelude::*; pub use astar_primitives::{ - ethereum_checked::CheckedEthereumTransact, AccountId, Address, AssetId, Balance, BlockNumber, - Hash, Header, Index, Signature, + ethereum_checked::CheckedEthereumTransact, evm::EvmRevertCodeHandler, + xcm::AssetLocationIdConverter, AccountId, Address, AssetId, Balance, BlockNumber, Hash, Header, + Index, Signature, }; -use astar_primitives::xcm::AssetLocationIdConverter; use pallet_evm_precompile_assets_erc20::AddressToAssetId; #[cfg(any(feature = "std", test))] @@ -87,6 +87,7 @@ pub use sp_runtime::BuildStorage; mod chain_extensions; mod precompiles; +mod weights; mod xcm_config; pub type ShibuyaAssetLocationIdConverter = AssetLocationIdConverter; @@ -628,10 +629,12 @@ impl pallet_assets::Config for Runtime { type StringLimit = AssetsStringLimit; type Freezer = (); type Extra = (); - type WeightInfo = pallet_assets::weights::SubstrateWeight; + type WeightInfo = weights::pallet_assets::SubstrateWeight; type RemoveItemsLimit = ConstU32<1000>; type AssetIdParameter = Compact; - type CallbackHandle = (); + type CallbackHandle = EvmRevertCodeHandler; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = astar_primitives::benchmarks::AssetsBenchmarkHelper; } parameter_types! { @@ -1231,25 +1234,11 @@ impl pallet_proxy::Config for Runtime { type AnnouncementDepositFactor = AnnouncementDepositFactor; } -pub struct EvmRevertCodeHandler; -impl pallet_xc_asset_config::XcAssetChanged for EvmRevertCodeHandler { - fn xc_asset_registered(asset_id: AssetId) { - let address = Runtime::asset_id_to_address(asset_id); - pallet_evm::AccountCodes::::insert(address, vec![0x60, 0x00, 0x60, 0x00, 0xfd]); - } - - fn xc_asset_unregistered(asset_id: AssetId) { - let address = Runtime::asset_id_to_address(asset_id); - pallet_evm::AccountCodes::::remove(address); - } -} - impl pallet_xc_asset_config::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssetId = AssetId; - type XcAssetChanged = EvmRevertCodeHandler; // Good enough for testnet since we lack pallet-assets hooks for now - type ManagerOrigin = EitherOfDiverse, EnsureSigned>; + type ManagerOrigin = EnsureRoot; type WeightInfo = pallet_xc_asset_config::weights::SubstrateWeight; } @@ -1431,6 +1420,7 @@ mod benches { define_benchmarks!( [frame_benchmarking, BaselineBench::] [frame_system, SystemBench::] + [pallet_assets, Assets] [pallet_balances, Balances] [pallet_timestamp, Timestamp] [pallet_dapps_staking, DappsStaking] diff --git a/runtime/shibuya/src/weights/mod.rs b/runtime/shibuya/src/weights/mod.rs new file mode 100644 index 0000000000..2db3ac12ab --- /dev/null +++ b/runtime/shibuya/src/weights/mod.rs @@ -0,0 +1,19 @@ +// 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 . + +pub mod pallet_assets; diff --git a/runtime/shibuya/src/weights/pallet_assets.rs b/runtime/shibuya/src/weights/pallet_assets.rs new file mode 100644 index 0000000000..676729637e --- /dev/null +++ b/runtime/shibuya/src/weights/pallet_assets.rs @@ -0,0 +1,496 @@ +// 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 . + +//! Autogenerated weights for pallet_assets +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `devserver-01`, CPU: `Intel(R) Xeon(R) E-2236 CPU @ 3.40GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("astar-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/astar-collator +// benchmark +// pallet +// --chain=astar-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_assets +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./benchmark-results/assets_weights.rs +// --template=./scripts/templates/weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weights for pallet_assets using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_assets::WeightInfo for SubstrateWeight { + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:1 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `659` + // Estimated: `4124` + // Minimum execution time: 35_155_000 picoseconds. + Weight::from_parts(35_760_000, 4124) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:1 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn force_create() -> Weight { + // Proof Size summary in bytes: + // Measured: `519` + // Estimated: `3984` + // Minimum execution time: 20_004_000 picoseconds. + Weight::from_parts(20_348_000, 3984) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn start_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_682_000 picoseconds. + Weight::from_parts(14_059_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1001 w:1000) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1000 w:1000) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `c` is `[0, 1000]`. + fn destroy_accounts(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255 + c * (208 ±0)` + // Estimated: `3687 + c * (2621 ±0)` + // Minimum execution time: 17_910_000 picoseconds. + Weight::from_parts(18_110_000, 3687) + // Standard Error: 6_525 + .saturating_add(Weight::from_parts(11_623_553, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2621).saturating_mul(c.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1001 w:1000) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + /// The range of component `a` is `[0, 1000]`. + fn destroy_approvals(a: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `438 + a * (86 ±0)` + // Estimated: `3687 + a * (2635 ±0)` + // Minimum execution time: 18_065_000 picoseconds. + Weight::from_parts(18_372_000, 3687) + // Standard Error: 4_985 + .saturating_add(Weight::from_parts(13_662_720, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_parts(0, 2635).saturating_mul(a.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:0 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn finish_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 16_700_000 picoseconds. + Weight::from_parts(17_418_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn mint() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 26_305_000 picoseconds. + Weight::from_parts(27_014_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn burn() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 30_525_000 picoseconds. + Weight::from_parts(31_113_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 42_955_000 picoseconds. + Weight::from_parts(43_309_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 37_951_000 picoseconds. + Weight::from_parts(38_330_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 43_061_000 picoseconds. + Weight::from_parts(43_561_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn freeze() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_259_000 picoseconds. + Weight::from_parts(17_516_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn thaw() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_165_000 picoseconds. + Weight::from_parts(17_547_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn freeze_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_634_000 picoseconds. + Weight::from_parts(14_069_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn thaw_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_456_000 picoseconds. + Weight::from_parts(13_770_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn transfer_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 16_083_000 picoseconds. + Weight::from_parts(16_343_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn set_team() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 14_443_000 picoseconds. + Weight::from_parts(14_818_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn set_metadata(_n: u32, _s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 28_260_000 picoseconds. + Weight::from_parts(29_181_263, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `431` + // Estimated: `3687` + // Minimum execution time: 28_580_000 picoseconds. + Weight::from_parts(28_826_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn force_set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `94` + // Estimated: `3687` + // Minimum execution time: 14_605_000 picoseconds. + Weight::from_parts(15_107_642, 3687) + // Standard Error: 450 + .saturating_add(Weight::from_parts(6_608, 0).saturating_mul(n.into())) + // Standard Error: 450 + .saturating_add(Weight::from_parts(2_783, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn force_clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `431` + // Estimated: `3687` + // Minimum execution time: 27_925_000 picoseconds. + Weight::from_parts(28_323_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn force_asset_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 13_904_000 picoseconds. + Weight::from_parts(14_431_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn approve_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 31_152_000 picoseconds. + Weight::from_parts(31_715_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer_approved() -> Weight { + // Proof Size summary in bytes: + // Measured: `680` + // Estimated: `6232` + // Minimum execution time: 63_252_000 picoseconds. + Weight::from_parts(63_718_000, 6232) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `471` + // Estimated: `3687` + // Minimum execution time: 34_375_000 picoseconds. + Weight::from_parts(34_640_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn force_cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `471` + // Estimated: `3687` + // Minimum execution time: 34_159_000 picoseconds. + Weight::from_parts(34_693_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn set_min_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 15_114_000 picoseconds. + Weight::from_parts(15_271_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn touch() -> Weight { + // Proof Size summary in bytes: + // Measured: `429` + // Estimated: `3687` + // Minimum execution time: 34_356_000 picoseconds. + Weight::from_parts(34_856_000, 3687) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn touch_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 31_063_000 picoseconds. + Weight::from_parts(31_331_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn refund() -> Weight { + // Proof Size summary in bytes: + // Measured: `567` + // Estimated: `3687` + // Minimum execution time: 31_678_000 picoseconds. + Weight::from_parts(31_941_000, 3687) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn refund_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `426` + // Estimated: `3687` + // Minimum execution time: 28_601_000 picoseconds. + Weight::from_parts(28_966_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn block() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_545_000 picoseconds. + Weight::from_parts(17_959_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/runtime/shiden/Cargo.toml b/runtime/shiden/Cargo.toml index 2d3c6e0e4c..b9861b7f2e 100644 --- a/runtime/shiden/Cargo.toml +++ b/runtime/shiden/Cargo.toml @@ -229,6 +229,7 @@ runtime-benchmarks = [ "pallet-collator-selection/runtime-benchmarks", "polkadot-runtime/runtime-benchmarks", "astar-primitives/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", ] try-runtime = [ "fp-self-contained/try-runtime", diff --git a/runtime/shiden/src/lib.rs b/runtime/shiden/src/lib.rs index 6a87d6f7e6..220f1e4bc4 100644 --- a/runtime/shiden/src/lib.rs +++ b/runtime/shiden/src/lib.rs @@ -68,10 +68,10 @@ use sp_runtime::{ use sp_std::prelude::*; pub use astar_primitives::{ - AccountId, Address, AssetId, Balance, BlockNumber, Hash, Header, Index, Signature, + evm::EvmRevertCodeHandler, xcm::AssetLocationIdConverter, AccountId, Address, AssetId, Balance, + BlockNumber, Hash, Header, Index, Signature, }; -use astar_primitives::xcm::AssetLocationIdConverter; use pallet_evm_precompile_assets_erc20::AddressToAssetId; #[cfg(any(feature = "std", test))] @@ -85,6 +85,7 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::BuildStorage; mod precompiles; +mod weights; mod xcm_config; pub type ShidenAssetLocationIdConverter = AssetLocationIdConverter; @@ -583,10 +584,12 @@ impl pallet_assets::Config for Runtime { type StringLimit = AssetsStringLimit; type Freezer = (); type Extra = (); - type WeightInfo = pallet_assets::weights::SubstrateWeight; + type WeightInfo = weights::pallet_assets::SubstrateWeight; type RemoveItemsLimit = ConstU32<1000>; type AssetIdParameter = Compact; - type CallbackHandle = (); + type CallbackHandle = EvmRevertCodeHandler; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = astar_primitives::benchmarks::AssetsBenchmarkHelper; } parameter_types! { @@ -822,23 +825,9 @@ impl pallet_sudo::Config for Runtime { type WeightInfo = pallet_sudo::weights::SubstrateWeight; } -pub struct EvmRevertCodeHandler; -impl pallet_xc_asset_config::XcAssetChanged for EvmRevertCodeHandler { - fn xc_asset_registered(asset_id: AssetId) { - let address = Runtime::asset_id_to_address(asset_id); - pallet_evm::AccountCodes::::insert(address, vec![0x60, 0x00, 0x60, 0x00, 0xfd]); - } - - fn xc_asset_unregistered(asset_id: AssetId) { - let address = Runtime::asset_id_to_address(asset_id); - pallet_evm::AccountCodes::::remove(address); - } -} - impl pallet_xc_asset_config::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssetId = AssetId; - type XcAssetChanged = EvmRevertCodeHandler; type ManagerOrigin = EnsureRoot; type WeightInfo = pallet_xc_asset_config::weights::SubstrateWeight; } @@ -1153,6 +1142,7 @@ mod benches { define_benchmarks!( [frame_benchmarking, BaselineBench::] [frame_system, SystemBench::] + [pallet_assets, Assets] [pallet_balances, Balances] [pallet_timestamp, Timestamp] [pallet_dapps_staking, DappsStaking] diff --git a/runtime/shiden/src/weights/mod.rs b/runtime/shiden/src/weights/mod.rs new file mode 100644 index 0000000000..2db3ac12ab --- /dev/null +++ b/runtime/shiden/src/weights/mod.rs @@ -0,0 +1,19 @@ +// 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 . + +pub mod pallet_assets; diff --git a/runtime/shiden/src/weights/pallet_assets.rs b/runtime/shiden/src/weights/pallet_assets.rs new file mode 100644 index 0000000000..676729637e --- /dev/null +++ b/runtime/shiden/src/weights/pallet_assets.rs @@ -0,0 +1,496 @@ +// 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 . + +//! Autogenerated weights for pallet_assets +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `devserver-01`, CPU: `Intel(R) Xeon(R) E-2236 CPU @ 3.40GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("astar-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/astar-collator +// benchmark +// pallet +// --chain=astar-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_assets +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./benchmark-results/assets_weights.rs +// --template=./scripts/templates/weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weights for pallet_assets using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_assets::WeightInfo for SubstrateWeight { + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:1 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn create() -> Weight { + // Proof Size summary in bytes: + // Measured: `659` + // Estimated: `4124` + // Minimum execution time: 35_155_000 picoseconds. + Weight::from_parts(35_760_000, 4124) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:1 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn force_create() -> Weight { + // Proof Size summary in bytes: + // Measured: `519` + // Estimated: `3984` + // Minimum execution time: 20_004_000 picoseconds. + Weight::from_parts(20_348_000, 3984) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn start_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_682_000 picoseconds. + Weight::from_parts(14_059_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1001 w:1000) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1000 w:1000) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `c` is `[0, 1000]`. + fn destroy_accounts(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255 + c * (208 ±0)` + // Estimated: `3687 + c * (2621 ±0)` + // Minimum execution time: 17_910_000 picoseconds. + Weight::from_parts(18_110_000, 3687) + // Standard Error: 6_525 + .saturating_add(Weight::from_parts(11_623_553, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2621).saturating_mul(c.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1001 w:1000) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + /// The range of component `a` is `[0, 1000]`. + fn destroy_approvals(a: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `438 + a * (86 ±0)` + // Estimated: `3687 + a * (2635 ±0)` + // Minimum execution time: 18_065_000 picoseconds. + Weight::from_parts(18_372_000, 3687) + // Standard Error: 4_985 + .saturating_add(Weight::from_parts(13_662_720, 0).saturating_mul(a.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) + .saturating_add(Weight::from_parts(0, 2635).saturating_mul(a.into())) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// Storage: EVM AccountCodes (r:0 w:1) + /// Proof Skipped: EVM AccountCodes (max_values: None, max_size: None, mode: Measured) + fn finish_destroy() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 16_700_000 picoseconds. + Weight::from_parts(17_418_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn mint() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 26_305_000 picoseconds. + Weight::from_parts(27_014_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn burn() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 30_525_000 picoseconds. + Weight::from_parts(31_113_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 42_955_000 picoseconds. + Weight::from_parts(43_309_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 37_951_000 picoseconds. + Weight::from_parts(38_330_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6232` + // Minimum execution time: 43_061_000 picoseconds. + Weight::from_parts(43_561_000, 6232) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn freeze() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_259_000 picoseconds. + Weight::from_parts(17_516_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn thaw() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_165_000 picoseconds. + Weight::from_parts(17_547_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn freeze_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_634_000 picoseconds. + Weight::from_parts(14_069_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn thaw_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 13_456_000 picoseconds. + Weight::from_parts(13_770_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:0) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn transfer_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 16_083_000 picoseconds. + Weight::from_parts(16_343_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn set_team() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 14_443_000 picoseconds. + Weight::from_parts(14_818_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn set_metadata(_n: u32, _s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 28_260_000 picoseconds. + Weight::from_parts(29_181_263, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `431` + // Estimated: `3687` + // Minimum execution time: 28_580_000 picoseconds. + Weight::from_parts(28_826_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + /// The range of component `n` is `[0, 50]`. + /// The range of component `s` is `[0, 50]`. + fn force_set_metadata(n: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `94` + // Estimated: `3687` + // Minimum execution time: 14_605_000 picoseconds. + Weight::from_parts(15_107_642, 3687) + // Standard Error: 450 + .saturating_add(Weight::from_parts(6_608, 0).saturating_mul(n.into())) + // Standard Error: 450 + .saturating_add(Weight::from_parts(2_783, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Metadata (r:1 w:1) + /// Proof: Assets Metadata (max_values: None, max_size: Some(152), added: 2627, mode: MaxEncodedLen) + fn force_clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `431` + // Estimated: `3687` + // Minimum execution time: 27_925_000 picoseconds. + Weight::from_parts(28_323_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn force_asset_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 13_904_000 picoseconds. + Weight::from_parts(14_431_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn approve_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `289` + // Estimated: `3687` + // Minimum execution time: 31_152_000 picoseconds. + Weight::from_parts(31_715_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + /// Storage: Assets Account (r:2 w:2) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn transfer_approved() -> Weight { + // Proof Size summary in bytes: + // Measured: `680` + // Estimated: `6232` + // Minimum execution time: 63_252_000 picoseconds. + Weight::from_parts(63_718_000, 6232) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `471` + // Estimated: `3687` + // Minimum execution time: 34_375_000 picoseconds. + Weight::from_parts(34_640_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Approvals (r:1 w:1) + /// Proof: Assets Approvals (max_values: None, max_size: Some(160), added: 2635, mode: MaxEncodedLen) + fn force_cancel_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `471` + // Estimated: `3687` + // Minimum execution time: 34_159_000 picoseconds. + Weight::from_parts(34_693_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn set_min_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 15_114_000 picoseconds. + Weight::from_parts(15_271_000, 3687) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn touch() -> Weight { + // Proof Size summary in bytes: + // Measured: `429` + // Estimated: `3687` + // Minimum execution time: 34_356_000 picoseconds. + Weight::from_parts(34_856_000, 3687) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn touch_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `255` + // Estimated: `3687` + // Minimum execution time: 31_063_000 picoseconds. + Weight::from_parts(31_331_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn refund() -> Weight { + // Proof Size summary in bytes: + // Measured: `567` + // Estimated: `3687` + // Minimum execution time: 31_678_000 picoseconds. + Weight::from_parts(31_941_000, 3687) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + /// Storage: Assets Asset (r:1 w:1) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + fn refund_other() -> Weight { + // Proof Size summary in bytes: + // Measured: `426` + // Estimated: `3687` + // Minimum execution time: 28_601_000 picoseconds. + Weight::from_parts(28_966_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Assets Asset (r:1 w:0) + /// Proof: Assets Asset (max_values: None, max_size: Some(222), added: 2697, mode: MaxEncodedLen) + /// Storage: Assets Account (r:1 w:1) + /// Proof: Assets Account (max_values: None, max_size: Some(146), added: 2621, mode: MaxEncodedLen) + fn block() -> Weight { + // Proof Size summary in bytes: + // Measured: `375` + // Estimated: `3687` + // Minimum execution time: 17_545_000 picoseconds. + Weight::from_parts(17_959_000, 3687) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} diff --git a/scripts/templates/weight-template.hbs b/scripts/templates/weight-template.hbs index 0df6bef5d3..1bc7767bd6 100644 --- a/scripts/templates/weight-template.hbs +++ b/scripts/templates/weight-template.hbs @@ -1,4 +1,22 @@ {{header}} +// 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 . + //! Autogenerated weights for {{pallet}} //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} diff --git a/tests/integration/Cargo.toml b/tests/integration/Cargo.toml index 26ca026e01..834f6dd902 100644 --- a/tests/integration/Cargo.toml +++ b/tests/integration/Cargo.toml @@ -17,6 +17,7 @@ pallet-evm = { workspace = true } # frame dependencies frame-support = { workspace = true } frame-system = { workspace = true } +pallet-assets = { workspace = true } pallet-balances = { workspace = true } pallet-contracts = { workspace = true } pallet-contracts-primitives = { workspace = true } @@ -29,6 +30,7 @@ sp-runtime = { workspace = true } # astar dependencies pallet-ethereum-checked = { workspace = true } +pallet-evm-precompile-assets-erc20 = { workspace = true } astar-primitives = { workspace = true } astar-runtime = { workspace = true, features = ["std"], optional = true } diff --git a/tests/integration/src/assets.rs b/tests/integration/src/assets.rs new file mode 100644 index 0000000000..7deb49e18d --- /dev/null +++ b/tests/integration/src/assets.rs @@ -0,0 +1,81 @@ +// 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 . + +use crate::setup::*; + +use astar_primitives::evm::EVM_REVERT_CODE; +use pallet_evm_precompile_assets_erc20::AddressToAssetId; + +#[test] +fn asset_create_and_destroy_work_for_evm_revert_code() { + new_test_ext().execute_with(|| { + let asset_id = 19; + let precompile_address = Runtime::asset_id_to_address(asset_id); + + // Asset creation results in insertion of the revert opt code at the precompile address + assert!( + !pallet_evm::AccountCodes::::contains_key(&precompile_address), + "Precompile address should be empty." + ); + assert_ok!(Assets::create( + RuntimeOrigin::signed(ALICE), + asset_id.into(), + ALICE.into(), + 1, + )); + assert_eq!( + pallet_evm::AccountCodes::::get(&precompile_address), + EVM_REVERT_CODE.to_vec(), + "Precompile address should contain the revert code." + ); + + // Asset destroy results in removal of the revert opt code from the precompile address + assert_ok!(Assets::start_destroy( + RuntimeOrigin::signed(ALICE), + asset_id.into(), + )); + assert_ok!(Assets::finish_destroy( + RuntimeOrigin::signed(ALICE), + asset_id.into(), + )); + assert!( + !pallet_evm::AccountCodes::::contains_key(&precompile_address), + "After asset is destroyed, precompile address should be empty." + ); + }); +} + +#[test] +fn asset_create_fails_if_account_code_is_non_empty() { + new_test_ext().execute_with(|| { + let asset_id = 19; + let precompile_address = Runtime::asset_id_to_address(asset_id); + + // Asset registration must fail if the precompile address is not empty + pallet_evm::AccountCodes::::insert(&precompile_address, EVM_REVERT_CODE.to_vec()); + assert_noop!( + Assets::create( + RuntimeOrigin::signed(ALICE), + asset_id.into(), + ALICE.into(), + 1, + ), + pallet_assets::Error::::CallbackFailed + ); + }); +} diff --git a/tests/integration/src/lib.rs b/tests/integration/src/lib.rs index 6b01e7cf77..c36fc93edc 100644 --- a/tests/integration/src/lib.rs +++ b/tests/integration/src/lib.rs @@ -26,5 +26,8 @@ mod setup; #[cfg(any(feature = "shibuya", feature = "shiden", feature = "astar"))] mod proxy; +#[cfg(any(feature = "shibuya", feature = "shiden", feature = "astar"))] +mod assets; + #[cfg(feature = "shibuya")] mod xvm; diff --git a/tests/integration/src/setup.rs b/tests/integration/src/setup.rs index 3407614818..594ca745b0 100644 --- a/tests/integration/src/setup.rs +++ b/tests/integration/src/setup.rs @@ -19,7 +19,7 @@ //! Runtime integration tests setup & imports. pub use frame_support::{ - assert_ok, + assert_noop, assert_ok, traits::{OnFinalize, OnIdle, OnInitialize}, weights::Weight, }; diff --git a/tests/xcm-simulator/src/mocks/parachain.rs b/tests/xcm-simulator/src/mocks/parachain.rs index 8be1841ff6..0c00aa5d48 100644 --- a/tests/xcm-simulator/src/mocks/parachain.rs +++ b/tests/xcm-simulator/src/mocks/parachain.rs @@ -156,6 +156,8 @@ impl pallet_assets::Config for Runtime { type RemoveItemsLimit = ConstU32<100>; type CallbackHandle = (); type WeightInfo = pallet_assets::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } impl pallet_timestamp::Config for Runtime { @@ -391,7 +393,6 @@ parameter_types! { impl pallet_xc_asset_config::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssetId = AssetId; - type XcAssetChanged = (); type ManagerOrigin = EnsureRoot; type WeightInfo = pallet_xc_asset_config::weights::SubstrateWeight; } From 288f9971f3e1f42e795a70823b198188a9f42795 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Mon, 14 Aug 2023 19:47:26 +0530 Subject: [PATCH 10/11] chore: bump semver and spec versions (#1002) --- Cargo.lock | 10 +++++----- bin/collator/Cargo.toml | 2 +- runtime/astar/Cargo.toml | 2 +- runtime/astar/src/lib.rs | 2 +- runtime/local/Cargo.toml | 2 +- runtime/shibuya/Cargo.toml | 2 +- runtime/shiden/Cargo.toml | 2 +- runtime/shiden/src/lib.rs | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1dd542a8a..94e894bc5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -404,7 +404,7 @@ dependencies = [ [[package]] name = "astar-collator" -version = "5.16.1" +version = "5.17.0" dependencies = [ "astar-primitives", "astar-runtime", @@ -532,7 +532,7 @@ dependencies = [ [[package]] name = "astar-runtime" -version = "5.16.1" +version = "5.17.0" dependencies = [ "array-bytes 6.1.0", "astar-primitives", @@ -5487,7 +5487,7 @@ checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "local-runtime" -version = "5.16.1" +version = "5.17.0" dependencies = [ "array-bytes 6.1.0", "astar-primitives", @@ -12347,7 +12347,7 @@ dependencies = [ [[package]] name = "shibuya-runtime" -version = "5.16.1" +version = "5.17.0" dependencies = [ "array-bytes 6.1.0", "astar-primitives", @@ -12456,7 +12456,7 @@ dependencies = [ [[package]] name = "shiden-runtime" -version = "5.16.1" +version = "5.17.0" dependencies = [ "array-bytes 6.1.0", "astar-primitives", diff --git a/bin/collator/Cargo.toml b/bin/collator/Cargo.toml index b6c7a10b58..46e4e590af 100644 --- a/bin/collator/Cargo.toml +++ b/bin/collator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "astar-collator" -version = "5.16.1" +version = "5.17.0" description = "Astar collator implementation in Rust." build = "build.rs" default-run = "astar-collator" diff --git a/runtime/astar/Cargo.toml b/runtime/astar/Cargo.toml index 2dcb04a7b5..beff8e5c6c 100644 --- a/runtime/astar/Cargo.toml +++ b/runtime/astar/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "astar-runtime" -version = "5.16.1" +version = "5.17.0" build = "build.rs" authors.workspace = true edition.workspace = true diff --git a/runtime/astar/src/lib.rs b/runtime/astar/src/lib.rs index 1b8e7e4424..d832a98a2a 100644 --- a/runtime/astar/src/lib.rs +++ b/runtime/astar/src/lib.rs @@ -142,7 +142,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("astar"), impl_name: create_runtime_str!("astar"), authoring_version: 1, - spec_version: 64, + spec_version: 65, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, diff --git a/runtime/local/Cargo.toml b/runtime/local/Cargo.toml index 6226f6fe8a..e7733bebd4 100644 --- a/runtime/local/Cargo.toml +++ b/runtime/local/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "local-runtime" -version = "5.16.1" +version = "5.17.0" build = "build.rs" authors.workspace = true edition.workspace = true diff --git a/runtime/shibuya/Cargo.toml b/runtime/shibuya/Cargo.toml index ad17c0f917..29e5cb658a 100644 --- a/runtime/shibuya/Cargo.toml +++ b/runtime/shibuya/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shibuya-runtime" -version = "5.16.1" +version = "5.17.0" build = "build.rs" authors.workspace = true edition.workspace = true diff --git a/runtime/shiden/Cargo.toml b/runtime/shiden/Cargo.toml index b9861b7f2e..d020642239 100644 --- a/runtime/shiden/Cargo.toml +++ b/runtime/shiden/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shiden-runtime" -version = "5.16.1" +version = "5.17.0" build = "build.rs" authors.workspace = true edition.workspace = true diff --git a/runtime/shiden/src/lib.rs b/runtime/shiden/src/lib.rs index 220f1e4bc4..7bb2b1cf82 100644 --- a/runtime/shiden/src/lib.rs +++ b/runtime/shiden/src/lib.rs @@ -144,7 +144,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("shiden"), impl_name: create_runtime_str!("shiden"), authoring_version: 1, - spec_version: 104, + spec_version: 105, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, From ff3011c199f9059d020550c1559248a8598d744e Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Tue, 15 Aug 2023 10:20:14 +0530 Subject: [PATCH 11/11] chore: bump shibuya spec (#1003) --- runtime/shibuya/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index 5891d44e95..8f70eee505 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -166,7 +166,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("shibuya"), impl_name: create_runtime_str!("shibuya"), authoring_version: 1, - spec_version: 106, + spec_version: 107, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2,