From 7213e363c042cf80ecb2503d1bbdef56d06f63a5 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 8 May 2024 09:29:40 +0200 Subject: [PATCH 01/10] XcmDryRunApi - Dry-running extrinsics to get their XCM effects (#3872) # Context Estimating fees for XCM execution and sending has been an area with bad UX. The addition of the [XcmPaymentApi](https://github.com/paritytech/polkadot-sdk/pull/3607) exposed the necessary components to be able to estimate XCM fees correctly, however, that was not the full story. The `XcmPaymentApi` works for estimating fees only if you know the specific XCM you want to execute or send. This is necessary but most UIs want to estimate the fees for extrinsics, they don't necessarily know the XCM program that's executed by them. # Main addition A new runtime API is introduced, the `XcmDryRunApi`, that given an extrinsic, or an XCM program, returns its effects: - Execution result - Local XCM (in the case of an extrinsic) - Forwarded XCMs - List of events This API can be used on its own for dry-running purposes, for double-checking or testing, but it mainly shines when used in conjunction with the `XcmPaymentApi`. UIs can use these two APIs to estimate transfers. # How it works New tests are added to exemplify how to incorporate both APIs. There's a mock test just to make sure everything works under `xcm-fee-payment-runtime-api`. There's a real-world test using Westend and AssetHubWestend under `cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs`. Added both a test for a simple teleport between chains and a reserve transfer asset between two parachains going through a reserve. The steps to follow: - Use `XcmDryRunApi::dry_run_extrinsic` to get local XCM program and forwarded messages - For each forwarded message - Use `XcmPaymentApi::query_delivery_fee` LOCALLY to get the delivery fees - Use `XcmPaymentApi::query_xcm_weight` ON THE DESTINATION to get the remote execution weight - (optional) Use `XcmPaymentApi::query_acceptable_payment_assets` ON THE DESTINATION to know on which assets the execution fees can be paid - Use `XcmPaymentApi::query_weight_to_asset_fee` ON THE DESTINATION to convert weight to the actual remote execution fees - Use `XcmDryRunApi::dry_run_xcm` ON THE DESTINATION to know if a new message will be forwarded, if so, continue # Dear reviewer The changes in this PR are grouped as follows, and in order of importance: - Addition of new runtime API - Definition, mock and simple tests: polkadot/xcm/xcm-fee-payment-runtime-api/* - Implemented on Westend, Asset Hub Westend and Penpal, will implement on every runtime in a following PR - Addition of a new config item to the XCM executor for recording xcms about to be executed - Definition: polkadot/xcm/xcm-executor/* - Implementation: polkadot/xcm/pallet-xcm/* - had to update all runtime xcm_config.rs files with `type XcmRecorder = XcmPallet;` - Addition of a new trait for inspecting the messages in queues - Definition: polkadot/xcm/xcm-builder/src/routing.rs - Implemented it on all routers: - ChildParachainRouter: polkadot/runtime/common/src/xcm_sender.rs - ParentAsUmp: cumulus/primitives/utility/src/lib.rs (piggybacked on implementation in cumulus/pallets/parachain-system/src/lib.rs) - XcmpQueue: cumulus/pallets/xcmp-queue/src/lib.rs - Bridge: bridges/modules/xcm-bridge-hub-router/src/lib.rs - More complicated and useful tests: - cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs ## Next steps With this PR, Westend, AssetHubWestend, Rococo and AssetHubRococo have the new API. UIs can test on these runtimes to create better experiences around cross-chain operations. Next: - Add XcmDryRunApi to all system parachains - Integrate xcm fee estimation in all emulated tests - Get this on the fellowship runtimes --------- Co-authored-by: Adrian Catangiu --- Cargo.lock | 33 ++ .../modules/xcm-bridge-hub-router/src/lib.rs | 43 +- .../modules/xcm-bridge-hub-router/src/mock.rs | 47 +- cumulus/pallets/parachain-system/Cargo.toml | 3 + cumulus/pallets/parachain-system/src/lib.rs | 16 +- cumulus/pallets/xcmp-queue/Cargo.toml | 5 +- cumulus/pallets/xcmp-queue/src/lib.rs | 35 +- cumulus/pallets/xcmp-queue/src/mock.rs | 1 + cumulus/pallets/xcmp-queue/src/tests.rs | 40 ++ .../emulated/chains/relays/westend/Cargo.toml | 2 + .../tests/assets/asset-hub-westend/Cargo.toml | 6 + .../assets/asset-hub-westend/src/tests/mod.rs | 1 + .../src/tests/xcm_fee_estimation.rs | 370 ++++++++++++ .../assets/asset-hub-rococo/Cargo.toml | 1 + .../assets/asset-hub-rococo/src/lib.rs | 71 ++- .../assets/asset-hub-rococo/src/xcm_config.rs | 1 + .../assets/asset-hub-westend/Cargo.toml | 1 + .../assets/asset-hub-westend/src/lib.rs | 157 ++++-- .../asset-hub-westend/src/xcm_config.rs | 1 + .../bridge-hub-rococo/src/xcm_config.rs | 1 + .../bridge-hub-westend/src/xcm_config.rs | 1 + .../collectives-westend/src/xcm_config.rs | 1 + .../contracts-rococo/src/xcm_config.rs | 1 + .../coretime-rococo/src/xcm_config.rs | 1 + .../coretime-westend/src/xcm_config.rs | 1 + .../glutton/glutton-westend/src/xcm_config.rs | 1 + .../people/people-rococo/src/xcm_config.rs | 1 + .../people/people-westend/src/xcm_config.rs | 1 + .../runtimes/starters/shell/src/xcm_config.rs | 1 + .../runtimes/testing/penpal/Cargo.toml | 3 + .../runtimes/testing/penpal/src/lib.rs | 107 +++- .../runtimes/testing/penpal/src/xcm_config.rs | 1 + .../testing/rococo-parachain/src/lib.rs | 1 + cumulus/primitives/utility/src/lib.rs | 12 +- polkadot/node/service/Cargo.toml | 1 + polkadot/node/service/src/fake_runtime_api.rs | 20 +- polkadot/runtime/common/src/xcm_sender.rs | 21 +- polkadot/runtime/parachains/src/dmp.rs | 2 +- polkadot/runtime/rococo/Cargo.toml | 1 + polkadot/runtime/rococo/src/lib.rs | 67 ++- polkadot/runtime/rococo/src/xcm_config.rs | 1 + .../runtime/test-runtime/src/xcm_config.rs | 1 + polkadot/runtime/westend/Cargo.toml | 1 + polkadot/runtime/westend/src/lib.rs | 67 ++- polkadot/runtime/westend/src/xcm_config.rs | 1 + .../src/fungible/mock.rs | 1 + .../pallet-xcm-benchmarks/src/generic/mock.rs | 1 + polkadot/xcm/pallet-xcm/Cargo.toml | 1 + polkadot/xcm/pallet-xcm/src/lib.rs | 61 +- polkadot/xcm/pallet-xcm/src/mock.rs | 1 + polkadot/xcm/pallet-xcm/src/tests/mod.rs | 36 +- polkadot/xcm/xcm-builder/src/lib.rs | 4 +- polkadot/xcm/xcm-builder/src/routing.rs | 27 +- polkadot/xcm/xcm-builder/src/tests/mock.rs | 1 + .../xcm/xcm-builder/src/tests/pay/mock.rs | 1 + .../xcm/xcm-builder/src/universal_exports.rs | 9 + polkadot/xcm/xcm-builder/tests/mock/mod.rs | 1 + polkadot/xcm/xcm-executor/src/config.rs | 6 +- polkadot/xcm/xcm-executor/src/lib.rs | 9 + polkadot/xcm/xcm-executor/src/traits/mod.rs | 2 + .../xcm/xcm-executor/src/traits/record_xcm.rs | 46 ++ .../xcm-fee-payment-runtime-api/Cargo.toml | 31 ++ .../src/dry_run.rs | 83 +++ .../xcm-fee-payment-runtime-api/src/fees.rs | 97 ++++ .../xcm-fee-payment-runtime-api/src/lib.rs | 93 +--- .../tests/fee_estimation.rs | 370 ++++++++++++ .../xcm-fee-payment-runtime-api/tests/mock.rs | 525 ++++++++++++++++++ .../example/src/parachain/xcm_config/mod.rs | 1 + .../example/src/relay_chain/xcm_config/mod.rs | 1 + .../xcm/xcm-simulator/fuzzer/src/parachain.rs | 1 + .../xcm-simulator/fuzzer/src/relay_chain.rs | 1 + prdoc/pr_3872.prdoc | 86 +++ .../contracts/mock-network/src/parachain.rs | 1 + .../contracts/mock-network/src/relay_chain.rs | 1 + .../runtime/src/configs/xcm_config.rs | 1 + 75 files changed, 2477 insertions(+), 173 deletions(-) create mode 100644 cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs create mode 100644 polkadot/xcm/xcm-executor/src/traits/record_xcm.rs create mode 100644 polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs create mode 100644 polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs create mode 100644 polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs create mode 100644 polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs create mode 100644 prdoc/pr_3872.prdoc diff --git a/Cargo.lock b/Cargo.lock index 8a7ba656af12..0dda623c14ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -896,21 +896,27 @@ dependencies = [ "cumulus-pallet-xcmp-queue", "emulated-integration-tests-common", "frame-support", + "frame-system", "pallet-asset-conversion", + "pallet-asset-tx-payment", "pallet-assets", "pallet-balances", "pallet-message-queue", + "pallet-transaction-payment", "pallet-treasury", "pallet-xcm", "parachains-common", "parity-scale-codec", "penpal-runtime", "polkadot-runtime-common", + "sp-core", + "sp-keyring", "sp-runtime", "staging-xcm", "staging-xcm-executor", "westend-runtime", "westend-system-emulated-network", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -3930,6 +3936,7 @@ dependencies = [ "sp-trie", "sp-version", "staging-xcm", + "staging-xcm-builder", "trie-db", "trie-standardmap", ] @@ -5122,6 +5129,19 @@ dependencies = [ "regex", ] +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.10.1" @@ -12145,6 +12165,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -22651,8 +22672,10 @@ dependencies = [ "sp-consensus-beefy", "sp-core", "sp-runtime", + "staging-xcm", "westend-runtime", "westend-runtime-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -23292,14 +23315,24 @@ dependencies = [ name = "xcm-fee-payment-runtime-api" version = "0.1.0" dependencies = [ + "env_logger 0.9.3", + "frame-executive", "frame-support", + "frame-system", + "log", + "pallet-assets", + "pallet-balances", + "pallet-xcm", "parity-scale-codec", "scale-info", "sp-api", + "sp-io", "sp-runtime", "sp-std 14.0.0", "sp-weights", "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", ] [[package]] diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs index ece72ac8494b..607394603466 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -37,8 +37,9 @@ use codec::Encode; use frame_support::traits::Get; use sp_core::H256; use sp_runtime::{FixedPointNumber, FixedU128, Saturating}; +use sp_std::vec::Vec; use xcm::prelude::*; -use xcm_builder::{ExporterFor, SovereignPaidRemoteExporter}; +use xcm_builder::{ExporterFor, InspectMessageQueues, SovereignPaidRemoteExporter}; pub use pallet::*; pub use weights::WeightInfo; @@ -95,7 +96,7 @@ pub mod pallet { /// Origin of the sibling bridge hub that is allowed to report bridge status. type BridgeHubOrigin: EnsureOrigin; /// Actual message sender (`HRMP` or `DMP`) to the sibling bridge hub location. - type ToBridgeHubSender: SendXcm; + type ToBridgeHubSender: SendXcm + InspectMessageQueues; /// Underlying channel with the sibling bridge hub. It must match the channel, used /// by the `Self::ToBridgeHubSender`. type WithBridgeHubChannel: XcmChannelStatusProvider; @@ -396,6 +397,12 @@ impl, I: 'static> SendXcm for Pallet { } } +impl, I: 'static> InspectMessageQueues for Pallet { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + ViaBridgeHubExporter::::get_messages() + } +} + #[cfg(test)] mod tests { use super::*; @@ -635,4 +642,36 @@ mod tests { ); }); } + + #[test] + fn get_messages_works() { + run_test(|| { + assert_ok!(send_xcm::( + (Parent, Parent, GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)).into(), + vec![ClearOrigin].into() + )); + assert_eq!( + XcmBridgeHubRouter::get_messages(), + vec![( + VersionedLocation::V4((Parent, Parachain(1002)).into()), + vec![VersionedXcm::V4( + Xcm::builder() + .withdraw_asset((Parent, 1_002_000)) + .buy_execution((Parent, 1_002_000), Unlimited) + .set_appendix( + Xcm::builder_unsafe() + .deposit_asset(AllCounted(1), (Parent, Parachain(1000))) + .build() + ) + .export_message( + Kusama, + Parachain(1000), + Xcm::builder_unsafe().clear_origin().build() + ) + .build() + )], + ),], + ); + }); + } } diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs index 20c86d1da9a2..3e2c1bb369cb 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/mock.rs @@ -19,14 +19,16 @@ use crate as pallet_xcm_bridge_hub_router; use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; +use codec::Encode; use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{Contains, Equals}, }; use frame_system::EnsureRoot; use sp_runtime::{traits::ConstU128, BuildStorage}; +use sp_std::cell::RefCell; use xcm::prelude::*; -use xcm_builder::{NetworkExportTable, NetworkExportTableItem}; +use xcm_builder::{InspectMessageQueues, NetworkExportTable, NetworkExportTableItem}; pub type AccountId = u64; type Block = frame_system::mocking::MockBlock; @@ -102,23 +104,46 @@ pub struct TestToBridgeHubSender; impl TestToBridgeHubSender { pub fn is_message_sent() -> bool { - frame_support::storage::unhashed::get_or_default(b"TestToBridgeHubSender.Sent") + !Self::get_messages().is_empty() } } +thread_local! { + pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); +} + impl SendXcm for TestToBridgeHubSender { - type Ticket = (); + type Ticket = (Location, Xcm<()>); fn validate( - _destination: &mut Option, - _message: &mut Option>, + destination: &mut Option, + message: &mut Option>, ) -> SendResult { - Ok(((), (BridgeFeeAsset::get(), HRMP_FEE).into())) + let pair = (destination.take().unwrap(), message.take().unwrap()); + Ok((pair, (BridgeFeeAsset::get(), HRMP_FEE).into())) } - fn deliver(_ticket: Self::Ticket) -> Result { - frame_support::storage::unhashed::put(b"TestToBridgeHubSender.Sent", &true); - Ok([0u8; 32]) + fn deliver(pair: Self::Ticket) -> Result { + let hash = fake_message_hash(&pair.1); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); + Ok(hash) + } +} + +impl InspectMessageQueues for TestToBridgeHubSender { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + SENT_XCM.with(|q| { + (*q.borrow()) + .clone() + .iter() + .map(|(location, message)| { + ( + VersionedLocation::V4(location.clone()), + vec![VersionedXcm::V4(message.clone())], + ) + }) + .collect() + }) } } @@ -146,3 +171,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { pub fn run_test(test: impl FnOnce() -> T) -> T { new_test_ext().execute_with(test) } + +pub(crate) fn fake_message_hash(message: &Xcm) -> XcmHash { + message.using_encoded(sp_io::hashing::blake2_256) +} diff --git a/cumulus/pallets/parachain-system/Cargo.toml b/cumulus/pallets/parachain-system/Cargo.toml index cc2e8943caad..57e274db361d 100644 --- a/cumulus/pallets/parachain-system/Cargo.toml +++ b/cumulus/pallets/parachain-system/Cargo.toml @@ -38,6 +38,7 @@ polkadot-parachain-primitives = { path = "../../../polkadot/parachain", default- polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", default-features = false } polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-features = false, optional = true } xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false } # Cumulus cumulus-pallet-parachain-system-proc-macro = { path = "proc-macro", default-features = false } @@ -95,6 +96,7 @@ std = [ "sp-tracing/std", "sp-trie/std", "trie-db/std", + "xcm-builder/std", "xcm/std", ] @@ -109,6 +111,7 @@ runtime-benchmarks = [ "polkadot-runtime-common/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", ] try-runtime = [ diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index 54a1def59600..c8e7d1bb30f7 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -55,7 +55,8 @@ use sp_runtime::{ BoundedSlice, FixedU128, RuntimeDebug, Saturating, }; use sp_std::{cmp, collections::btree_map::BTreeMap, prelude::*}; -use xcm::latest::XcmHash; +use xcm::{latest::XcmHash, VersionedLocation, VersionedXcm}; +use xcm_builder::InspectMessageQueues; mod benchmarking; pub mod migration; @@ -1608,6 +1609,19 @@ impl UpwardMessageSender for Pallet { } } +impl InspectMessageQueues for Pallet { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + use xcm::prelude::*; + + let messages: Vec> = PendingUpwardMessages::::get() + .iter() + .map(|encoded_message| VersionedXcm::<()>::decode(&mut &encoded_message[..]).unwrap()) + .collect(); + + vec![(VersionedLocation::V4(Parent.into()), messages)] + } +} + #[cfg(feature = "runtime-benchmarks")] impl polkadot_runtime_common::xcm_sender::EnsureForParachain for Pallet { fn ensure(para_id: ParaId) { diff --git a/cumulus/pallets/xcmp-queue/Cargo.toml b/cumulus/pallets/xcmp-queue/Cargo.toml index ab196c6d3ec6..e3530ef7bf0e 100644 --- a/cumulus/pallets/xcmp-queue/Cargo.toml +++ b/cumulus/pallets/xcmp-queue/Cargo.toml @@ -28,6 +28,7 @@ polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-f polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", default-features = false } xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false } # Cumulus cumulus-primitives-core = { path = "../../primitives/core", default-features = false } @@ -46,9 +47,6 @@ sp-core = { path = "../../../substrate/primitives/core" } pallet-balances = { path = "../../../substrate/frame/balances" } frame-support = { path = "../../../substrate/frame/support", features = ["experimental"] } -# Polkadot -xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder" } - # Cumulus cumulus-pallet-parachain-system = { path = "../parachain-system", features = ["parameterized-consensus-hook"] } @@ -71,6 +69,7 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", + "xcm-builder/std", "xcm-executor/std", "xcm/std", ] diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 7de2fd809421..cc785b66150e 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -70,7 +70,8 @@ use scale_info::TypeInfo; use sp_core::MAX_POSSIBLE_ALLOCATION; use sp_runtime::{FixedU128, RuntimeDebug, Saturating}; use sp_std::prelude::*; -use xcm::{latest::prelude::*, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; +use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; +use xcm_builder::InspectMessageQueues; use xcm_executor::traits::ConvertOrigin; pub use pallet::*; @@ -947,6 +948,38 @@ impl SendXcm for Pallet { } } +impl InspectMessageQueues for Pallet { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + use xcm::prelude::*; + + OutboundXcmpMessages::::iter() + .map(|(para_id, _, messages)| { + let mut data = &messages[..]; + let decoded_format = + XcmpMessageFormat::decode_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut data) + .unwrap(); + if decoded_format != XcmpMessageFormat::ConcatenatedVersionedXcm { + panic!("Unexpected format.") + } + let mut decoded_messages = Vec::new(); + while !data.is_empty() { + let decoded_message = VersionedXcm::<()>::decode_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut data, + ) + .unwrap(); + decoded_messages.push(decoded_message); + } + + ( + VersionedLocation::V4((Parent, Parachain(para_id.into())).into()), + decoded_messages, + ) + }) + .collect() + } +} + impl FeeTracker for Pallet { type Id = ParaId; diff --git a/cumulus/pallets/xcmp-queue/src/mock.rs b/cumulus/pallets/xcmp-queue/src/mock.rs index 9d9a723cf8b5..e258576aa3f6 100644 --- a/cumulus/pallets/xcmp-queue/src/mock.rs +++ b/cumulus/pallets/xcmp-queue/src/mock.rs @@ -178,6 +178,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } pub type XcmRouter = ( diff --git a/cumulus/pallets/xcmp-queue/src/tests.rs b/cumulus/pallets/xcmp-queue/src/tests.rs index 0b41095828f2..f48e9eec3ac0 100644 --- a/cumulus/pallets/xcmp-queue/src/tests.rs +++ b/cumulus/pallets/xcmp-queue/src/tests.rs @@ -844,3 +844,43 @@ fn verify_fee_factor_increase_and_decrease() { assert!(DeliveryFeeFactor::::get(sibling_para_id) < FixedU128::from_float(1.63)); }); } + +#[test] +fn get_messages_works() { + new_test_ext().execute_with(|| { + use xcm_builder::InspectMessageQueues; + let sibling_para_id = ParaId::from(2001); + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(sibling_para_id); + let destination: Location = (Parent, Parachain(sibling_para_id.into())).into(); + let other_sibling_para_id = ParaId::from(2002); + let other_destination: Location = (Parent, Parachain(other_sibling_para_id.into())).into(); + let message = Xcm(vec![ClearOrigin]); + assert_ok!(send_xcm::(destination.clone(), message.clone())); + assert_ok!(send_xcm::(destination.clone(), message.clone())); + assert_ok!(send_xcm::(destination.clone(), message.clone())); + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(other_sibling_para_id); + assert_ok!(send_xcm::(other_destination.clone(), message.clone())); + assert_ok!(send_xcm::(other_destination.clone(), message)); + let queued_messages = XcmpQueue::get_messages(); + assert_eq!( + queued_messages, + vec![ + ( + VersionedLocation::V4(other_destination), + vec![ + VersionedXcm::V4(Xcm(vec![ClearOrigin])), + VersionedXcm::V4(Xcm(vec![ClearOrigin])), + ], + ), + ( + VersionedLocation::V4(destination), + vec![ + VersionedXcm::V4(Xcm(vec![ClearOrigin])), + VersionedXcm::V4(Xcm(vec![ClearOrigin])), + VersionedXcm::V4(Xcm(vec![ClearOrigin])), + ], + ), + ], + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml index 20aedb50e6a1..e4688a1c9f02 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml @@ -25,6 +25,8 @@ pallet-staking = { path = "../../../../../../../substrate/frame/staking", defaul polkadot-primitives = { path = "../../../../../../../polkadot/primitives", default-features = false } westend-runtime-constants = { path = "../../../../../../../polkadot/runtime/westend/constants", default-features = false } westend-runtime = { path = "../../../../../../../polkadot/runtime/westend" } +xcm = { package = "staging-xcm", path = "../../../../../../../polkadot/xcm", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../../../../../../polkadot/xcm/xcm-fee-payment-runtime-api", default-features = false } # Cumulus parachains-common = { path = "../../../../../common" } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml index 00f4308324a9..0a2b0f6d45ee 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml @@ -16,18 +16,24 @@ assert_matches = "1.5.0" # Substrate sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } +sp-keyring = { path = "../../../../../../../substrate/primitives/keyring", default-features = false } +sp-core = { path = "../../../../../../../substrate/primitives/core", default-features = false } frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../../../../substrate/frame/system", default-features = false } pallet-balances = { path = "../../../../../../../substrate/frame/balances", default-features = false } pallet-assets = { path = "../../../../../../../substrate/frame/assets", default-features = false } pallet-asset-conversion = { path = "../../../../../../../substrate/frame/asset-conversion", default-features = false } pallet-treasury = { path = "../../../../../../../substrate/frame/treasury", default-features = false } pallet-message-queue = { path = "../../../../../../../substrate/frame/message-queue", default-features = false } +pallet-transaction-payment = { path = "../../../../../../../substrate/frame/transaction-payment", default-features = false } +pallet-asset-tx-payment = { path = "../../../../../../../substrate/frame/transaction-payment/asset-tx-payment", default-features = false } # Polkadot polkadot-runtime-common = { path = "../../../../../../../polkadot/runtime/common" } xcm = { package = "staging-xcm", path = "../../../../../../../polkadot/xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../../polkadot/xcm/xcm-executor", default-features = false } pallet-xcm = { path = "../../../../../../../polkadot/xcm/pallet-xcm", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../../../../../../polkadot/xcm/xcm-fee-payment-runtime-api", default-features = false } westend-runtime = { path = "../../../../../../../polkadot/runtime/westend" } # Cumulus diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs index bf013697b4c7..61eb70524fc9 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs @@ -21,3 +21,4 @@ mod set_xcm_versions; mod swap; mod teleport; mod treasury; +mod xcm_fee_estimation; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs new file mode 100644 index 000000000000..aeec9b44dab4 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -0,0 +1,370 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests to ensure correct XCM fee estimation for cross-chain asset transfers. + +use crate::imports::*; + +use sp_keyring::AccountKeyring::Alice; +use sp_runtime::{generic, MultiSignature}; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_xcm_dry_run_api::XcmDryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; + +/// We are able to dry-run and estimate the fees for a teleport between relay and system para. +/// Scenario: Alice on Westend relay chain wants to teleport WND to Asset Hub. +/// We want to know the fees using the `XcmDryRunApi` and `XcmPaymentApi`. +#[test] +fn teleport_relay_system_para_works() { + let destination: Location = Parachain(1000).into(); // Asset Hub. + let beneficiary_id = AssetHubWestendReceiver::get(); + let beneficiary: Location = AccountId32 { id: beneficiary_id.clone().into(), network: None } // Test doesn't allow specifying a network here. + .into(); // Beneficiary in Asset Hub. + let teleport_amount = 1_000_000_000_000; // One WND (12 decimals). + let assets: Assets = vec![(Here, teleport_amount).into()].into(); + + // We get them from the Westend closure. + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); + ::new_ext().execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; + + let call = RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::V4(destination.clone())), + beneficiary: Box::new(VersionedLocation::V4(beneficiary)), + assets: Box::new(VersionedAssets::V4(assets)), + fee_asset_item: 0, + weight_limit: Unlimited, + }); + let sender = Alice; // Is the same as `WestendSender`. + let extrinsic = construct_extrinsic_westend(sender, call); + let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); + assert_eq!(result.forwarded_xcms.len(), 1); + let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; + assert_eq!(messages_to_query.len(), 1); + remote_message = messages_to_query[0].clone(); + let delivery_fees = + Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) + .unwrap(); + delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + }); + + // This is set in the AssetHubWestend closure. + let mut remote_execution_fees = 0; + ::execute_with(|| { + type Runtime = ::Runtime; + + let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); + remote_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) + .unwrap(); + }); + + let test_args = TestContext { + sender: WestendSender::get(), // Alice. + receiver: AssetHubWestendReceiver::get(), // Bob in Asset Hub. + args: TestArgs::new_relay(destination, beneficiary_id, teleport_amount), + }; + let mut test = RelayToSystemParaTest::new(test_args); + + let sender_balance_before = test.sender.balance; + let receiver_balance_before = test.receiver.balance; + assert_eq!(sender_balance_before, 1_000_000_000_000_000_000); + assert_eq!(receiver_balance_before, 4_096_000_000_000); + + test.set_dispatchable::(transfer_assets); + test.assert(); + + let sender_balance_after = test.sender.balance; + let receiver_balance_after = test.receiver.balance; + + // We now know the exact fees. + assert_eq!( + sender_balance_after, + sender_balance_before - delivery_fees_amount - teleport_amount + ); + assert_eq!( + receiver_balance_after, + receiver_balance_before + teleport_amount - remote_execution_fees + ); +} + +/// We are able to dry-run and estimate the fees for a multi-hop XCM journey. +/// Scenario: Alice on PenpalA has some WND and wants to send them to PenpalB. +/// We want to know the fees using the `XcmDryRunApi` and `XcmPaymentApi`. +#[test] +fn multi_hop_works() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let amount_to_send = 1_000_000_000_000; // One WND (12 decimals). + let asset_owner = PenpalAssetOwner::get(); + let assets: Assets = (Parent, amount_to_send).into(); + let relay_native_asset_location = RelayLocation::get(); + let sender_as_seen_by_relay = Westend::child_location_of(PenpalA::para_id()); + let sov_of_sender_on_relay = Westend::sovereign_account_id_of(sender_as_seen_by_relay.clone()); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner.clone()), + relay_native_asset_location.clone(), + sender.clone(), + amount_to_send * 2, + ); + + // fund the Parachain Origin's SA on Relay Chain with the native tokens held in reserve + Westend::fund_accounts(vec![(sov_of_sender_on_relay.clone().into(), amount_to_send * 2)]); + + // Init values for Parachain Destination + let beneficiary_id = PenpalBReceiver::get(); + let beneficiary: Location = AccountId32 { + id: beneficiary_id.clone().into(), + network: None, // Test doesn't allow specifying a network here. + } + .into(); + + // We get them from the PenpalA closure. + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; + + let call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::V4(destination.clone())), + beneficiary: Box::new(VersionedLocation::V4(beneficiary)), + assets: Box::new(VersionedAssets::V4(assets.clone())), + fee_asset_item: 0, + weight_limit: Unlimited, + }); + let sender = Alice; // Same as `PenpalASender`. + let extrinsic = construct_extrinsic_penpal(sender, call); + let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); + assert_eq!(result.forwarded_xcms.len(), 1); + let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; + assert_eq!(messages_to_query.len(), 1); + remote_message = messages_to_query[0].clone(); + let delivery_fees = + Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) + .unwrap(); + delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + }); + + // This is set in the Westend closure. + let mut intermediate_execution_fees = 0; + let mut intermediate_delivery_fees_amount = 0; + let mut intermediate_remote_message = VersionedXcm::V4(Xcm::<()>(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; + + // First we get the execution fees. + let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); + intermediate_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Here.into())).unwrap(); + + // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. + let xcm_program = + VersionedXcm::V4(Xcm::::from(remote_message.clone().try_into().unwrap())); + + // Now we get the delivery fees to the final destination. + let result = + Runtime::dry_run_xcm(sender_as_seen_by_relay.clone().into(), xcm_program).unwrap(); + let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; + // There's actually two messages here. + // One created when the message we sent from PenpalA arrived and was executed. + // The second one when we dry-run the xcm. + // We could've gotten the message from the queue without having to dry-run, but + // offchain applications would have to dry-run, so we do it here as well. + intermediate_remote_message = messages_to_query[0].clone(); + let delivery_fees = Runtime::query_delivery_fees( + destination_to_query.clone(), + intermediate_remote_message.clone(), + ) + .unwrap(); + intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + }); + + // Get the final execution fees in the destination. + let mut final_execution_fees = 0; + ::execute_with(|| { + type Runtime = ::Runtime; + + let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap(); + final_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) + .unwrap(); + }); + + // Dry-running is done. + PenpalA::reset_ext(); + Westend::reset_ext(); + PenpalB::reset_ext(); + + // Fund accounts again. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + relay_native_asset_location.clone(), + sender.clone(), + amount_to_send * 2, + ); + Westend::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]); + + // Actually run the extrinsic. + let test_args = TestContext { + sender: PenpalASender::get(), // Alice. + receiver: PenpalBReceiver::get(), // Bob in PenpalB. + args: TestArgs::new_para( + destination, + beneficiary_id.clone(), + amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = ParaToParaThroughRelayTest::new(test_args); + + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &beneficiary_id) + }); + + test.set_dispatchable::(transfer_assets_para_to_para); + test.assert(); + + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &beneficiary_id) + }); + + // We know the exact fees on every hop. + assert_eq!( + sender_assets_after, + sender_assets_before - amount_to_send - delivery_fees_amount /* This is charged directly + * from the sender's + * account. */ + ); + assert_eq!( + receiver_assets_after, + receiver_assets_before + amount_to_send - + intermediate_execution_fees - + intermediate_delivery_fees_amount - + final_execution_fees + ); +} + +fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 { + let latest_assets: Assets = assets.try_into().unwrap(); + let Fungible(amount) = latest_assets.inner()[0].fun else { + unreachable!("asset is fungible"); + }; + amount +} + +fn transfer_assets(test: RelayToSystemParaTest) -> DispatchResult { + ::XcmPallet::transfer_assets( + test.signed_origin, + bx!(test.args.dest.into()), + bx!(test.args.beneficiary.into()), + bx!(test.args.assets.into()), + test.args.fee_asset_item, + test.args.weight_limit, + ) +} + +fn transfer_assets_para_to_para(test: ParaToParaThroughRelayTest) -> DispatchResult { + ::PolkadotXcm::transfer_assets( + test.signed_origin, + bx!(test.args.dest.into()), + bx!(test.args.beneficiary.into()), + bx!(test.args.assets.into()), + test.args.fee_asset_item, + test.args.weight_limit, + ) +} + +// Constructs the SignedExtra component of an extrinsic for the Westend runtime. +fn construct_extrinsic_westend( + sender: sp_keyring::AccountKeyring, + call: westend_runtime::RuntimeCall, +) -> westend_runtime::UncheckedExtrinsic { + type Runtime = ::Runtime; + let account_id = ::AccountId::from(sender.public()); + let tip = 0; + let extra: westend_runtime::SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(sp_runtime::generic::Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&account_id).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + ); + let raw_payload = westend_runtime::SignedPayload::new(call, extra).unwrap(); + let signature = raw_payload.using_encoded(|payload| sender.sign(payload)); + let (call, extra, _) = raw_payload.deconstruct(); + westend_runtime::UncheckedExtrinsic::new_signed( + call, + account_id.into(), + MultiSignature::Sr25519(signature), + extra, + ) +} + +// Constructs the SignedExtra component of an extrinsic for the Westend runtime. +fn construct_extrinsic_penpal( + sender: sp_keyring::AccountKeyring, + call: penpal_runtime::RuntimeCall, +) -> penpal_runtime::UncheckedExtrinsic { + type Runtime = ::Runtime; + let account_id = ::AccountId::from(sender.public()); + let tip = 0; + let extra: penpal_runtime::SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(generic::Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&account_id).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_asset_tx_payment::ChargeAssetTxPayment::::from(tip, None), + ); + type SignedPayload = + generic::SignedPayload; + let raw_payload = SignedPayload::new(call, extra).unwrap(); + let signature = raw_payload.using_encoded(|payload| sender.sign(payload)); + let (call, extra, _) = raw_payload.deconstruct(); + penpal_runtime::UncheckedExtrinsic::new_signed( + call, + account_id.into(), + MultiSignature::Sr25519(signature), + extra, + ) +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 64abedbaac78..888193c5c6ea 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -138,6 +138,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 383751578e57..f81a107fae05 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -100,7 +100,10 @@ use xcm::{ latest::prelude::{AssetId, BodyId}, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, }; -use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; @@ -1281,7 +1284,7 @@ impl_runtime_apis! { } } - impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { let acceptable = vec![ // native token @@ -1320,6 +1323,70 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm_executor::RecordXcm; + use xcm::prelude::*; + + pallet_xcm::Pallet::::set_record_xcm(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm::prelude::*; + + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let program: Xcm = program.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = program.using_encoded(sp_core::hashing::blake2_256); + let result = xcm_executor::XcmExecutor::::prepare_and_execute( + origin_location, + program, + &mut hash, + Weight::MAX, // Max limit available for execution. + Weight::zero(), + ); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index a73c1cc33ea0..664d2b9c9dd5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -424,6 +424,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 3ba53eb3f937..bacc9c1b7c29 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -135,6 +135,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index e96ba3d962d8..b5c3ed5053c4 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -85,17 +85,24 @@ pub use sp_runtime::BuildStorage; use assets_common::{foreign_creators::ForeignCreators, matching::FromSiblingParachain}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; +use xcm::{ + prelude::{VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}, + IntoVersion, +}; + +// We exclude `Assets` since it's the name of a pallet +use xcm::latest::prelude::AssetId; #[cfg(feature = "runtime-benchmarks")] use xcm::latest::prelude::{ Asset, Fungible, Here, InteriorLocation, Junction, Junction::*, Location, NetworkId, NonFungible, Parent, ParentThen, Response, XCM_VERSION, }; -use xcm::{ - latest::prelude::AssetId, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, - VersionedXcm, + +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, }; -use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; @@ -1311,6 +1318,109 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable = vec![ + // native token + VersionedAssetId::from(AssetId(xcm_config::WestendLocation::get())) + ]; + + Ok(acceptable + .into_iter() + .filter_map(|asset| asset.into_version(xcm_version).ok()) + .collect()) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + Ok(asset_id) if asset_id.0 == xcm_config::WestendLocation::get() => { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + }, + Ok(asset_id) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm_executor::RecordXcm; + use xcm::prelude::*; + + pallet_xcm::Pallet::::set_record_xcm(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm::prelude::*; + + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let program: Xcm = program.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = program.using_encoded(sp_core::hashing::blake2_256); + let result = xcm_executor::XcmExecutor::::prepare_and_execute( + origin_location, + program, + &mut hash, + Weight::MAX, // Max limit available for execution. + Weight::zero(), + ); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + } + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi for Runtime { @@ -1374,45 +1484,6 @@ impl_runtime_apis! { } } - impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { - fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { - let acceptable = vec![ - // native token - VersionedAssetId::from(AssetId(xcm_config::WestendLocation::get())) - ]; - - Ok(acceptable - .into_iter() - .filter_map(|asset| asset.into_version(xcm_version).ok()) - .collect()) - } - - fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - match asset.try_as::() { - Ok(asset_id) if asset_id.0 == xcm_config::WestendLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } - } - - fn query_xcm_weight(message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_xcm_weight(message) - } - - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) - } - } - impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index d610bfd768cd..35a42627ad71 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -440,6 +440,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index bd1445bee22c..a0d2e91dffd2 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -239,6 +239,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } pub type PriceForParentDelivery = diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index f147cd9653fe..c2ca8e47f2a6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -206,6 +206,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } pub type PriceForParentDelivery = diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs index 84697c3e3634..c68f230a16dc 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs @@ -220,6 +220,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index ac15ac5b0f0f..8c3371019860 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -202,6 +202,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs index 9095b5b1caaa..c16b40b8675f 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs @@ -224,6 +224,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs index defc57e2d7f5..b12765870bfd 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs @@ -232,6 +232,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs index 9d438a41f8fe..d1fb50c1ab09 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs @@ -91,6 +91,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs index 101d9a180e5f..cca964fb2441 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs @@ -230,6 +230,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs index 0a903f915056..3926ddcf21ef 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs @@ -238,6 +238,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs b/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs index 7f9de0f64b35..741b3bcd752f 100644 --- a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -91,6 +91,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index 028aa002a91e..4ebb95f26cf6 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -64,6 +64,7 @@ polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", def xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../../../../polkadot/xcm/xcm-fee-payment-runtime-api", default-features = false } # Cumulus cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } @@ -134,6 +135,7 @@ std = [ "substrate-wasm-builder", "xcm-builder/std", "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", "xcm/std", ] @@ -164,6 +166,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 89885d77378b..582154fec6d2 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -32,6 +32,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); mod weights; pub mod xcm_config; +use codec::Encode; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -44,7 +45,7 @@ use frame_support::{ AsEnsureOriginWithArg, ConstBool, ConstU32, ConstU64, ConstU8, Everything, TransformOrigin, }, weights::{ - constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, FeePolynomial, + constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, FeePolynomial, WeightToFee as _, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }, PalletId, @@ -80,7 +81,14 @@ pub use sp_runtime::BuildStorage; use parachains_common::{AccountId, Signature}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; -use xcm::latest::prelude::{AssetId as AssetLocationId, BodyId}; +use xcm::{ + latest::prelude::{AssetId as AssetLocationId, BodyId}, + IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, +}; +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; /// Balance of an account. pub type Balance = u128; @@ -835,6 +843,101 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + if !matches!(xcm_version, 3 | 4) { + return Err(XcmPaymentApiError::UnhandledXcmVersion); + } + Ok([VersionedAssetId::V4(xcm_config::RelayLocation::get().into())] + .into_iter() + .filter_map(|asset| asset.into_version(xcm_version).ok()) + .collect()) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let local_asset = VersionedAssetId::V4(xcm_config::RelayLocation::get().into()); + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } + + Ok(WeightToFee::weight_to_fee(&weight)) + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm_executor::RecordXcm; + use xcm::prelude::*; + + pallet_xcm::Pallet::::set_record_xcm(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm::prelude::*; + + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let program: Xcm = program.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = program.using_encoded(sp_core::hashing::blake2_256); + let result = xcm_executor::XcmExecutor::::prepare_and_execute( + origin_location, + program, + &mut hash, + Weight::MAX, // Max limit. + Weight::zero(), + ); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index 711041f6d6e2..08a2da260c57 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -362,6 +362,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Multiplier used for dedicated `TakeFirstAssetTrader` with `ForeignAssets` instance. diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index e762cec9093b..f22e900ba9ef 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -490,6 +490,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 54f40bd01097..64784eb36f84 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -34,8 +34,8 @@ use sp_runtime::{ SaturatedConversion, }; use sp_std::{marker::PhantomData, prelude::*}; -use xcm::{latest::prelude::*, WrapVersion}; -use xcm_builder::TakeRevenue; +use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm, WrapVersion}; +use xcm_builder::{InspectMessageQueues, TakeRevenue}; use xcm_executor::{ traits::{MatchesFungibles, TransactAsset, WeightTrader}, AssetsInHolding, @@ -93,6 +93,14 @@ where } } +impl InspectMessageQueues + for ParentAsUmp +{ + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + T::get_messages() + } +} + /// Contains information to handle refund/payment for xcm-execution #[derive(Clone, Eq, PartialEq, Debug)] struct AssetTraderRefunder { diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index 9688ab556473..7c010778d50d 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -206,6 +206,7 @@ runtime-benchmarks = [ "service/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "westend-runtime?/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index 89613040dca1..5c889552a6ae 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -398,20 +398,30 @@ sp_api::impl_runtime_apis! { } } - impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { - fn query_acceptable_payment_assets(_: xcm::Version) -> Result, xcm_fee_payment_runtime_api::Error> { + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(_: xcm::Version) -> Result, xcm_fee_payment_runtime_api::fees::Error> { unimplemented!() } - fn query_weight_to_asset_fee(_: Weight, _: VersionedAssetId) -> Result { + fn query_weight_to_asset_fee(_: Weight, _: VersionedAssetId) -> Result { unimplemented!() } - fn query_xcm_weight(_: VersionedXcm<()>) -> Result { + fn query_xcm_weight(_: VersionedXcm<()>) -> Result { unimplemented!() } - fn query_delivery_fees(_: VersionedLocation, _: VersionedXcm<()>) -> Result { + fn query_delivery_fees(_: VersionedLocation, _: VersionedXcm<()>) -> Result { + unimplemented!() + } + } + + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(_: ::Extrinsic) -> Result, xcm_fee_payment_runtime_api::dry_run::Error> { + unimplemented!() + } + + fn dry_run_xcm(_: VersionedLocation, _: VersionedXcm<()>) -> Result, xcm_fee_payment_runtime_api::dry_run::Error> { unimplemented!() } } diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs index a712d4381f75..cbec1a8ca103 100644 --- a/polkadot/runtime/common/src/xcm_sender.rs +++ b/polkadot/runtime/common/src/xcm_sender.rs @@ -18,7 +18,7 @@ use frame_support::traits::Get; use frame_system::pallet_prelude::BlockNumberFor; -use parity_scale_codec::Encode; +use parity_scale_codec::{Decode, Encode}; use primitives::Id as ParaId; use runtime_parachains::{ configuration::{self, HostConfiguration}, @@ -27,6 +27,7 @@ use runtime_parachains::{ use sp_runtime::FixedPointNumber; use sp_std::{marker::PhantomData, prelude::*}; use xcm::prelude::*; +use xcm_builder::InspectMessageQueues; use SendError::*; /// Simple value-bearing trait for determining/expressing the assets required to be paid for a @@ -138,6 +139,24 @@ where } } +impl InspectMessageQueues for ChildParachainRouter { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + dmp::DownwardMessageQueues::::iter() + .map(|(para_id, messages)| { + let decoded_messages: Vec> = messages + .iter() + .map(|downward_message| { + let message = VersionedXcm::<()>::decode(&mut &downward_message.msg[..]).unwrap(); + log::trace!(target: "xcm::DownwardMessageQueues::get_messages", "Message: {:?}, sent at: {:?}", message, downward_message.sent_at); + message + }) + .collect(); + (VersionedLocation::V4(Parachain(para_id.into()).into()), decoded_messages) + }) + .collect() + } +} + /// Implementation of `xcm_builder::EnsureDelivery` which helps to ensure delivery to the /// `ParaId` parachain (sibling or child). Deposits existential deposit for origin (if needed). /// Deposits estimated fee to the origin account (if needed). diff --git a/polkadot/runtime/parachains/src/dmp.rs b/polkadot/runtime/parachains/src/dmp.rs index 354b16cc3f08..df2f93e19421 100644 --- a/polkadot/runtime/parachains/src/dmp.rs +++ b/polkadot/runtime/parachains/src/dmp.rs @@ -119,7 +119,7 @@ pub mod pallet { /// The downward messages addressed for a certain para. #[pallet::storage] - pub(crate) type DownwardMessageQueues = StorageMap< + pub type DownwardMessageQueues = StorageMap< _, Twox64Concat, ParaId, diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index bbe19310f970..f4d8fb51b3fa 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -268,6 +268,7 @@ runtime-benchmarks = [ "sp-staking/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "frame-executive/try-runtime", diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 3b2cbc88dc3f..22e6183e5946 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -134,7 +134,10 @@ use governance::{ pallet_custom_origins, AuctionAdmin, Fellows, GeneralAdmin, LeaseAdmin, Treasurer, TreasurySpender, }; -use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; #[cfg(test)] mod tests; @@ -1764,7 +1767,7 @@ sp_api::impl_runtime_apis! { } } - impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { let acceptable = vec![ // native token @@ -1803,6 +1806,66 @@ sp_api::impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm_executor::RecordXcm; + pallet_xcm::Pallet::::set_record_xcm(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let xcm: Xcm = xcm.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + let result = xcm_executor::XcmExecutor::::prepare_and_execute( + origin_location, + xcm, + &mut hash, + Weight::MAX, // Max limit available for execution. + Weight::zero(), + ); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + } + impl sp_api::Metadata for Runtime { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs index c7063bd7ad61..decbc795143f 100644 --- a/polkadot/runtime/rococo/src/xcm_config.rs +++ b/polkadot/runtime/rococo/src/xcm_config.rs @@ -224,6 +224,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } parameter_types! { diff --git a/polkadot/runtime/test-runtime/src/xcm_config.rs b/polkadot/runtime/test-runtime/src/xcm_config.rs index 8411b79f2529..fc3d0dc42a3b 100644 --- a/polkadot/runtime/test-runtime/src/xcm_config.rs +++ b/polkadot/runtime/test-runtime/src/xcm_config.rs @@ -156,6 +156,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } impl pallet_xcm::Config for crate::Runtime { diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index d726adfb8e6e..f02cae0e9d49 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -283,6 +283,7 @@ runtime-benchmarks = [ "sp-staking/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "frame-election-provider-support/try-runtime", diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 8ae95e6e1a83..cae12ab49c02 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -108,7 +108,10 @@ use xcm::{ }; use xcm_builder::PayOverXcm; -use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -2198,7 +2201,7 @@ sp_api::impl_runtime_apis! { } } - impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { let acceptable = vec![ // native token @@ -2237,6 +2240,66 @@ sp_api::impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm_executor::RecordXcm; + pallet_xcm::Pallet::::set_record_xcm(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let xcm: Xcm = xcm.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + let result = xcm_executor::XcmExecutor::::prepare_and_execute( + origin_location, + xcm, + &mut hash, + Weight::MAX, // Max limit available for execution. + Weight::zero(), + ); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + } + impl pallet_nomination_pools_runtime_api::NominationPoolsApi< Block, AccountId, diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs index f661c4b0e4f4..c6c5fb9e72a4 100644 --- a/polkadot/runtime/westend/src/xcm_config.rs +++ b/polkadot/runtime/westend/src/xcm_config.rs @@ -222,6 +222,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } parameter_types! { diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index 7233b46d0cd6..c0dfa91afc78 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -120,6 +120,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } impl crate::Config for Test { diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs index a9f4d37d7a55..f51d34092616 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs @@ -110,6 +110,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } parameter_types! { diff --git a/polkadot/xcm/pallet-xcm/Cargo.toml b/polkadot/xcm/pallet-xcm/Cargo.toml index 460597e6649a..fc4d23426fbc 100644 --- a/polkadot/xcm/pallet-xcm/Cargo.toml +++ b/polkadot/xcm/pallet-xcm/Cargo.toml @@ -69,6 +69,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index af3b66121ea1..37fc121ba217 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -61,7 +61,7 @@ use xcm_executor::{ }, AssetsInHolding, }; -use xcm_fee_payment_runtime_api::Error as FeePaymentError; +use xcm_fee_payment_runtime_api::fees::Error as XcmPaymentApiError; #[cfg(any(feature = "try-runtime", test))] use sp_runtime::TryRuntimeError; @@ -764,6 +764,25 @@ pub mod pallet { #[pallet::storage] pub(super) type XcmExecutionSuspended = StorageValue<_, bool, ValueQuery>; + /// Whether or not incoming XCMs (both executed locally and received) should be recorded. + /// Only one XCM program will be recorded at a time. + /// This is meant to be used in runtime APIs, and it's advised it stays false + /// for all other use cases, so as to not degrade regular performance. + /// + /// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] + /// implementation in the XCM executor configuration. + #[pallet::storage] + pub(crate) type ShouldRecordXcm = StorageValue<_, bool, ValueQuery>; + + /// If [`ShouldRecordXcm`] is set to true, then the last XCM program executed locally + /// will be stored here. + /// Runtime APIs can fetch the XCM that was executed by accessing this value. + /// + /// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] + /// implementation in the XCM executor configuration. + #[pallet::storage] + pub(crate) type RecordedXcm = StorageValue<_, Xcm<()>>; + #[pallet::genesis_config] pub struct GenesisConfig { #[serde(skip)] @@ -2413,35 +2432,37 @@ impl Pallet { AccountIdConversion::::into_account_truncating(&ID) } - pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result { - let message = - Xcm::<()>::try_from(message).map_err(|_| FeePaymentError::VersionedConversionFailed)?; + pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + let message = Xcm::<()>::try_from(message) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; T::Weigher::weight(&mut message.into()).map_err(|()| { log::error!(target: "xcm::pallet_xcm::query_xcm_weight", "Error when querying XCM weight"); - FeePaymentError::WeightNotComputable + XcmPaymentApiError::WeightNotComputable }) } pub fn query_delivery_fees( destination: VersionedLocation, message: VersionedXcm<()>, - ) -> Result { + ) -> Result { let result_version = destination.identify_version().max(message.identify_version()); - let destination = - destination.try_into().map_err(|_| FeePaymentError::VersionedConversionFailed)?; + let destination = destination + .try_into() + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; - let message = message.try_into().map_err(|_| FeePaymentError::VersionedConversionFailed)?; + let message = + message.try_into().map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; let (_, fees) = validate_send::(destination, message).map_err(|error| { log::error!(target: "xcm::pallet_xcm::query_delivery_fees", "Error when querying delivery fees: {:?}", error); - FeePaymentError::Unroutable + XcmPaymentApiError::Unroutable })?; VersionedAssets::from(fees) .into_version(result_version) - .map_err(|_| FeePaymentError::VersionedConversionFailed) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed) } /// Create a new expectation of a query response with the querier being here. @@ -3105,6 +3126,24 @@ impl CheckSuspension for Pallet { } } +impl xcm_executor::traits::RecordXcm for Pallet { + fn should_record() -> bool { + ShouldRecordXcm::::get() + } + + fn set_record_xcm(enabled: bool) { + ShouldRecordXcm::::put(enabled); + } + + fn recorded_xcm() -> Option> { + RecordedXcm::::get() + } + + fn record(xcm: Xcm<()>) { + RecordedXcm::::put(xcm); + } +} + /// Ensure that the origin `o` represents an XCM (`Transact`) origin. /// /// Returns `Ok` with the location of the XCM sender or an `Err` otherwise. diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs index 8e94803e8431..b3b7529217f5 100644 --- a/polkadot/xcm/pallet-xcm/src/mock.rs +++ b/polkadot/xcm/pallet-xcm/src/mock.rs @@ -531,6 +531,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index 782c8bed478e..02aeafd68e83 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -21,8 +21,8 @@ pub(crate) mod assets_transfer; use crate::{ mock::*, pallet::SupportedVersion, AssetTraps, Config, CurrentMigration, Error, ExecuteControllerWeightInfo, LatestVersionedLocation, Pallet, Queries, QueryStatus, - VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, - WeightInfo, + RecordedXcm, ShouldRecordXcm, VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, + VersionNotifyTargets, WeightInfo, }; use frame_support::{ assert_err_ignore_postinfo, assert_noop, assert_ok, @@ -1245,3 +1245,35 @@ fn multistage_migration_works() { assert!(Pallet::::do_try_state().is_ok()); }) } + +#[test] +fn record_xcm_works() { + let balances = vec![(ALICE, INITIAL_BALANCE)]; + new_test_ext_with_balances(balances).execute_with(|| { + let message = Xcm::::builder() + .withdraw_asset((Here, SEND_AMOUNT)) + .buy_execution((Here, SEND_AMOUNT), Unlimited) + .deposit_asset(AllCounted(1), Junction::AccountId32 { network: None, id: BOB.into() }) + .build(); + // Test default values. + assert_eq!(ShouldRecordXcm::::get(), false); + assert_eq!(RecordedXcm::::get(), None); + + // By default the message won't be recorded. + assert_ok!(XcmPallet::execute( + RuntimeOrigin::signed(ALICE), + Box::new(VersionedXcm::from(message.clone())), + BaseXcmWeight::get() * 3, + )); + assert_eq!(RecordedXcm::::get(), None); + + // We explicitly set the record flag to true so we record the XCM. + ShouldRecordXcm::::put(true); + assert_ok!(XcmPallet::execute( + RuntimeOrigin::signed(ALICE), + Box::new(VersionedXcm::from(message.clone())), + BaseXcmWeight::get() * 3, + )); + assert_eq!(RecordedXcm::::get(), Some(message.into())); + }); +} diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index 1ba38d0db836..cc06c298a418 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -120,7 +120,9 @@ mod process_xcm_message; pub use process_xcm_message::ProcessXcmMessage; mod routing; -pub use routing::{EnsureDecodableXcm, EnsureDelivery, WithTopicSource, WithUniqueTopic}; +pub use routing::{ + EnsureDecodableXcm, EnsureDelivery, InspectMessageQueues, WithTopicSource, WithUniqueTopic, +}; mod transactional; pub use transactional::FrameTransactionalProcessor; diff --git a/polkadot/xcm/xcm-builder/src/routing.rs b/polkadot/xcm/xcm-builder/src/routing.rs index 921b9ac5922e..5c284aaf1475 100644 --- a/polkadot/xcm/xcm-builder/src/routing.rs +++ b/polkadot/xcm/xcm-builder/src/routing.rs @@ -18,7 +18,7 @@ use frame_system::unique; use parity_scale_codec::Encode; -use sp_std::{marker::PhantomData, result::Result}; +use sp_std::{marker::PhantomData, result::Result, vec::Vec}; use xcm::prelude::*; use xcm_executor::{traits::FeeReason, FeesMode}; @@ -60,6 +60,11 @@ impl SendXcm for WithUniqueTopic { Ok(unique_id) } } +impl InspectMessageQueues for WithUniqueTopic { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + Inner::get_messages() + } +} pub trait SourceTopic { fn source_topic(entropy: impl Encode) -> XcmHash; @@ -140,6 +145,26 @@ impl EnsureDelivery for Tuple { } } +/// Inspects messages in queues. +/// Meant to be used in runtime APIs, not in runtimes. +pub trait InspectMessageQueues { + /// Get queued messages and their destinations. + fn get_messages() -> Vec<(VersionedLocation, Vec>)>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl InspectMessageQueues for Tuple { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + let mut messages = Vec::new(); + + for_tuples!( #( + messages.append(&mut Tuple::get_messages()); + )* ); + + messages + } +} + /// A wrapper router that attempts to *encode* and *decode* passed XCM `message` to ensure that the /// receiving side will be able to decode, at least with the same XCM version. /// diff --git a/polkadot/xcm/xcm-builder/src/tests/mock.rs b/polkadot/xcm/xcm-builder/src/tests/mock.rs index 7532b97d97b3..f45650ec5404 100644 --- a/polkadot/xcm/xcm-builder/src/tests/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/mock.rs @@ -748,6 +748,7 @@ impl Config for TestConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } pub fn fungible_multi_asset(location: Location, amount: u128) -> Asset { diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index 019113a12b2f..34b204b434d6 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -221,6 +221,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } parameter_types! { diff --git a/polkadot/xcm/xcm-builder/src/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs index d0e3ef3032ea..04ceb7e51688 100644 --- a/polkadot/xcm/xcm-builder/src/universal_exports.rs +++ b/polkadot/xcm/xcm-builder/src/universal_exports.rs @@ -16,6 +16,7 @@ //! Traits and utilities to help with origin mutation and bridging. +use crate::InspectMessageQueues; use frame_support::{ensure, traits::Get}; use parity_scale_codec::{Decode, Encode}; use sp_std::{convert::TryInto, marker::PhantomData, prelude::*}; @@ -335,6 +336,14 @@ impl InspectMessageQueues + for SovereignPaidRemoteExporter +{ + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + Router::get_messages() + } +} + pub trait DispatchBlob { /// Takes an incoming blob from over some point-to-point link (usually from some sort of /// inter-consensus bridge) and then does what needs to be done with it. Usually this means diff --git a/polkadot/xcm/xcm-builder/tests/mock/mod.rs b/polkadot/xcm/xcm-builder/tests/mock/mod.rs index 46ec23beebc1..45bfba235563 100644 --- a/polkadot/xcm/xcm-builder/tests/mock/mod.rs +++ b/polkadot/xcm/xcm-builder/tests/mock/mod.rs @@ -209,6 +209,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/xcm-executor/src/config.rs b/polkadot/xcm/xcm-executor/src/config.rs index b296d32ca2ad..63b113bc250f 100644 --- a/polkadot/xcm/xcm-executor/src/config.rs +++ b/polkadot/xcm/xcm-executor/src/config.rs @@ -17,8 +17,8 @@ use crate::traits::{ AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FeeManager, HandleHrmpChannelAccepted, HandleHrmpChannelClosing, - HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, ShouldExecute, TransactAsset, - VersionChangeNotifier, WeightBounds, WeightTrader, + HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, RecordXcm, ShouldExecute, + TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, }; use frame_support::{ dispatch::{GetDispatchInfo, Parameter, PostDispatchInfo}, @@ -122,4 +122,6 @@ pub trait Config { type HrmpChannelAcceptedHandler: HandleHrmpChannelAccepted; /// Allows optional logic execution for the `HrmpChannelClosing` XCM notification. type HrmpChannelClosingHandler: HandleHrmpChannelClosing; + /// Allows recording the last executed XCM (used by dry-run runtime APIs). + type XcmRecorder: RecordXcm; } diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index a7052328da00..e0b8a8a9c73e 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -37,6 +37,8 @@ use traits::{ XcmAssetTransfers, }; +pub use traits::RecordXcm; + mod assets; pub use assets::AssetsInHolding; mod config; @@ -211,6 +213,13 @@ impl ExecuteXcm for XcmExecutor. + +//! Trait for recording XCMs and a dummy implementation. + +use xcm::latest::Xcm; + +/// Trait for recording XCMs. +pub trait RecordXcm { + /// Whether or not we should record incoming XCMs. + fn should_record() -> bool; + /// Enable or disable recording. + fn set_record_xcm(enabled: bool); + /// Get recorded XCM. + /// Returns `None` if no message was sent, or if recording was off. + fn recorded_xcm() -> Option>; + /// Record `xcm`. + fn record(xcm: Xcm<()>); +} + +impl RecordXcm for () { + fn should_record() -> bool { + false + } + + fn set_record_xcm(_: bool) {} + + fn recorded_xcm() -> Option> { + None + } + + fn record(_: Xcm<()>) {} +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index 30c7c0bac14f..cec76e7327ec 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -26,15 +26,46 @@ sp-weights = { path = "../../../substrate/primitives/weights", default-features xcm = { package = "staging-xcm", path = "../", default-features = false } frame-support = { path = "../../../substrate/frame/support", default-features = false } +[dev-dependencies] +frame-system = { path = "../../../substrate/frame/system", default-features = false } +pallet-xcm = { path = "../pallet-xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false } +sp-io = { path = "../../../substrate/primitives/io", default-features = false } +pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } +pallet-assets = { path = "../../../substrate/frame/assets", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false } +frame-executive = { path = "../../../substrate/frame/executive", default-features = false } +log = { workspace = true } +env_logger = "0.9.0" + [features] default = ["std"] std = [ "codec/std", + "frame-executive/std", "frame-support/std", + "frame-system/std", + "log/std", + "pallet-assets/std", + "pallet-balances/std", + "pallet-xcm/std", "scale-info/std", "sp-api/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", "sp-weights/std", + "xcm-builder/std", + "xcm-executor/std", "xcm/std", ] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", +] diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs new file mode 100644 index 000000000000..62a422d6efeb --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -0,0 +1,83 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Substrate 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. + +// Substrate 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 Polkadot. If not, see . + +//! Runtime API definition for dry-running XCM-related extrinsics. +//! This API can be used to simulate XCMs and, for example, find the fees +//! that need to be paid. + +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::{DispatchResult, TypeInfo}; +use sp_runtime::traits::Block as BlockT; +use sp_std::vec::Vec; +use xcm::prelude::*; + +/// Effects of dry-running an extrinsic. +#[derive(Encode, Decode, Debug, TypeInfo)] +pub struct ExtrinsicDryRunEffects { + /// The result of executing the extrinsic. + pub execution_result: DispatchResult, + /// The list of events fired by the extrinsic. + pub emitted_events: Vec, + /// The local XCM that was attempted to be executed, if any. + pub local_xcm: Option>, + /// The list of XCMs that were queued for sending. + pub forwarded_xcms: Vec<(VersionedLocation, Vec>)>, +} + +/// Effects of dry-running an XCM program. +#[derive(Encode, Decode, Debug, TypeInfo)] +pub struct XcmDryRunEffects { + /// The outcome of the XCM program execution. + pub execution_result: Outcome, + /// List of events fired by the XCM program execution. + pub emitted_events: Vec, + /// List of queued messages for sending. + pub forwarded_xcms: Vec<(VersionedLocation, Vec>)>, +} + +sp_api::decl_runtime_apis! { + /// API for dry-running extrinsics and XCM programs to get the programs that need to be passed to the fees API. + /// + /// All calls return a vector of tuples (location, xcm) where each "xcm" is executed in "location". + /// If there's local execution, the location will be "Here". + /// This vector can be used to calculate both execution and delivery fees. + /// + /// Extrinsics or XCMs might fail when executed, this doesn't mean the result of these calls will be an `Err`. + /// In those cases, there might still be a valid result, with the execution error inside it. + /// The only reasons why these calls might return an error are listed in the [`Error`] enum. + pub trait XcmDryRunApi { + /// Dry run extrinsic. + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, Error>; + + /// Dry run XCM program + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, Error>; + } +} + +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] +pub enum Error { + /// An API call is unsupported. + #[codec(index = 0)] + Unimplemented, + + /// Converting a versioned data structure from one version to another failed. + #[codec(index = 1)] + VersionedConversionFailed, + + /// Extrinsic was invalid. + #[codec(index = 2)] + InvalidExtrinsic, +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs new file mode 100644 index 000000000000..572d4edf5338 --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs @@ -0,0 +1,97 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Substrate 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. + +// Substrate 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 Polkadot. If not, see . + +//! Runtime API definition for getting XCM fees. + +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::TypeInfo; +use sp_std::vec::Vec; +use sp_weights::Weight; +use xcm::{Version, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; + +sp_api::decl_runtime_apis! { + /// A trait of XCM payment API. + /// + /// API provides functionality for obtaining: + /// + /// * the weight required to execute an XCM message, + /// * a list of acceptable `AssetId`s for message execution payment, + /// * the cost of the weight in the specified acceptable `AssetId`. + /// * the fees for an XCM message delivery. + /// + /// To determine the execution weight of the calls required for + /// [`xcm::latest::Instruction::Transact`] instruction, `TransactionPaymentCallApi` can be used. + pub trait XcmPaymentApi { + /// Returns a list of acceptable payment assets. + /// + /// # Arguments + /// + /// * `xcm_version`: Version. + fn query_acceptable_payment_assets(xcm_version: Version) -> Result, Error>; + + /// Returns a weight needed to execute a XCM. + /// + /// # Arguments + /// + /// * `message`: `VersionedXcm`. + fn query_xcm_weight(message: VersionedXcm<()>) -> Result; + + /// Converts a weight into a fee for the specified `AssetId`. + /// + /// # Arguments + /// + /// * `weight`: convertible `Weight`. + /// * `asset`: `VersionedAssetId`. + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result; + + /// Get delivery fees for sending a specific `message` to a `destination`. + /// These always come in a specific asset, defined by the chain. + /// + /// # Arguments + /// * `message`: The message that'll be sent, necessary because most delivery fees are based on the + /// size of the message. + /// * `destination`: The destination to send the message to. Different destinations may use + /// different senders that charge different fees. + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result; + } +} + +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] +pub enum Error { + /// An API part is unsupported. + #[codec(index = 0)] + Unimplemented, + + /// Converting a versioned data structure from one version to another failed. + #[codec(index = 1)] + VersionedConversionFailed, + + /// XCM message weight calculation failed. + #[codec(index = 2)] + WeightNotComputable, + + /// XCM version not able to be handled. + #[codec(index = 3)] + UnhandledXcmVersion, + + /// The given asset is not handled as a fee asset. + #[codec(index = 4)] + AssetNotFound, + + /// Destination is known to be unroutable. + #[codec(index = 5)] + Unroutable, +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs index 50fd4692cb0d..616ee4c2eccb 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs @@ -14,86 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Runtime API definition for xcm transaction payment. +//! Runtime APIs for estimating xcm fee payment. +//! This crate offers two APIs, one for estimating fees, +//! which can be used for any type of message, and another one +//! for returning the specific messages used for transfers, a common +//! feature. +//! Users of these APIs should call the transfers API and pass the result to the +//! fees API. #![cfg_attr(not(feature = "std"), no_std)] -use codec::{Decode, Encode}; -use frame_support::pallet_prelude::TypeInfo; -use sp_std::vec::Vec; -use sp_weights::Weight; -use xcm::{Version, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; - -sp_api::decl_runtime_apis! { - /// A trait of XCM payment API. - /// - /// API provides functionality for obtaining: - /// - /// * the weight required to execute an XCM message, - /// * a list of acceptable `AssetId`s for message execution payment, - /// * the cost of the weight in the specified acceptable `AssetId`. - /// * the fees for an XCM message delivery. - /// - /// To determine the execution weight of the calls required for - /// [`xcm::latest::Instruction::Transact`] instruction, `TransactionPaymentCallApi` can be used. - pub trait XcmPaymentApi { - /// Returns a list of acceptable payment assets. - /// - /// # Arguments - /// - /// * `xcm_version`: desired XCM `Version` of `VersionedAssetId`. - fn query_acceptable_payment_assets(xcm_version: Version) -> Result, Error>; - - /// Returns a weight needed to execute a XCM. - /// - /// # Arguments - /// - /// * `message`: `VersionedXcm`. - fn query_xcm_weight(message: VersionedXcm<()>) -> Result; - - /// Converts a weight into a fee for the specified `AssetId`. - /// - /// # Arguments - /// - /// * `weight`: convertible `Weight`. - /// * `asset`: `VersionedAssetId`. - fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result; - - /// Get delivery fees for sending a specific `message` to a `destination`. - /// These always come in a specific asset, defined by the chain. - /// - /// # Arguments - /// * `message`: The message that'll be sent, necessary because most delivery fees are based on the - /// size of the message. - /// * `destination`: The destination to send the message to. Different destinations may use - /// different senders that charge different fees. - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result; - } -} - -#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] -pub enum Error { - /// An API part is unsupported. - #[codec(index = 0)] - Unimplemented, - - /// Converting a versioned data structure from one version to another failed. - #[codec(index = 1)] - VersionedConversionFailed, - - /// XCM message weight calculation failed. - #[codec(index = 2)] - WeightNotComputable, - - /// XCM version not able to be handled. - #[codec(index = 3)] - UnhandledXcmVersion, - - /// The given asset is not handled as a fee asset. - #[codec(index = 4)] - AssetNotFound, - - /// Destination is known to be unroutable. - #[codec(index = 5)] - Unroutable, -} +/// Dry-run API. +/// Given an extrinsic or an XCM program, it returns the outcome of its execution. +pub mod dry_run; +/// Fee estimation API. +/// Given an XCM program, it will return the fees needed to execute it properly or send it. +pub mod fees; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs new file mode 100644 index 000000000000..7a9bfa4a7968 --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -0,0 +1,370 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Substrate 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. + +// Substrate 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 Polkadot. If not, see . + +//! Tests for using both the XCM fee payment API and the dry-run API. + +use frame_support::{ + dispatch::DispatchInfo, + pallet_prelude::{DispatchClass, Pays}, +}; +use sp_api::ProvideRuntimeApi; +use sp_runtime::testing::H256; +use xcm::prelude::*; +use xcm_fee_payment_runtime_api::{dry_run::XcmDryRunApi, fees::XcmPaymentApi}; + +mod mock; +use mock::{ + extra, fake_message_hash, new_test_ext_with_balances, new_test_ext_with_balances_and_assets, + DeliveryFees, ExistentialDeposit, HereLocation, RuntimeCall, RuntimeEvent, TestClient, TestXt, +}; + +// Scenario: User `1` in the local chain (id 2000) wants to transfer assets to account `[0u8; 32]` +// on "AssetHub". He wants to make sure he has enough for fees, so before he calls the +// `transfer_asset` extrinsic to do the transfer, he decides to use the `XcmDryRunApi` and +// `XcmPaymentApi` runtime APIs to estimate fees. This uses a teleport because we're dealing with +// the native token of the chain, which is registered on "AssetHub". The fees are sent as a reserve +// asset transfer, since they're paid in the relay token. +// +// Teleport Parachain(2000) Token +// Reserve Asset Transfer Relay Token for fees +// Parachain(2000) -------------------------------------------> Parachain(1000) +#[test] +fn fee_estimation_for_teleport() { + let _ = env_logger::builder().is_test(true).try_init(); + let who = 1; // AccountId = u64. + let balances = vec![(who, 100 + DeliveryFees::get() + ExistentialDeposit::get())]; + let assets = vec![(1, who, 50)]; + new_test_ext_with_balances_and_assets(balances, assets).execute_with(|| { + let client = TestClient; + let runtime_api = client.runtime_api(); + let extrinsic = TestXt::new( + RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::V4((Parent, Parachain(1000)).into())), + beneficiary: Box::new(VersionedLocation::V4( + AccountId32 { id: [0u8; 32], network: None }.into(), + )), + assets: Box::new(VersionedAssets::V4( + vec![(Here, 100u128).into(), (Parent, 20u128).into()].into(), + )), + fee_asset_item: 1, // Fees are paid with the RelayToken + weight_limit: Unlimited, + }), + Some((who, extra())), + ); + let dry_run_effects = + runtime_api.dry_run_extrinsic(H256::zero(), extrinsic).unwrap().unwrap(); + + assert_eq!( + dry_run_effects.local_xcm, + Some(VersionedXcm::V4( + Xcm::builder_unsafe() + .withdraw_asset((Parent, 20u128)) + .burn_asset((Parent, 20u128)) + .withdraw_asset((Here, 100u128)) + .burn_asset((Here, 100u128)) + .build() + )), + ); + let send_destination = Location::new(1, [Parachain(1000)]); + let send_message = Xcm::<()>::builder_unsafe() + .withdraw_asset((Parent, 20u128)) + .buy_execution((Parent, 20u128), Unlimited) + .receive_teleported_asset(((Parent, Parachain(2000)), 100u128)) + .clear_origin() + .deposit_asset(AllCounted(2), [0u8; 32]) + .build(); + assert_eq!( + dry_run_effects.forwarded_xcms, + vec![( + VersionedLocation::V4(send_destination.clone()), + vec![VersionedXcm::V4(send_message.clone())], + ),], + ); + + assert_eq!( + dry_run_effects.emitted_events, + vec![ + RuntimeEvent::System(frame_system::Event::NewAccount { + account: 8660274132218572653 // TODO: Why is this not `1`? + }), + RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: 8660274132218572653, + free_balance: 100 + }), + RuntimeEvent::Balances(pallet_balances::Event::Minted { + who: 8660274132218572653, + amount: 100 + }), + RuntimeEvent::AssetsPallet(pallet_assets::Event::Burned { + asset_id: 1, + owner: 1, + balance: 20 + }), + RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 100 }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { + outcome: Outcome::Complete { used: Weight::from_parts(400, 40) }, + }), + RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 20 }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::FeesPaid { + paying: AccountIndex64 { index: 1, network: None }.into(), + fees: (Here, 20u128).into(), + }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { + origin: AccountIndex64 { index: 1, network: None }.into(), + destination: (Parent, Parachain(1000)).into(), + message: send_message.clone(), + message_id: fake_message_hash(&send_message), + }), + RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { + dispatch_info: DispatchInfo { + weight: Weight::from_parts(107074070, 0), /* Will break if weights get + * updated. */ + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + } + }), + ] + ); + + // Weighing the local program is not relevant for extrinsics that already + // take this weight into account. + // In this case, we really only care about delivery fees. + let local_xcm = dry_run_effects.local_xcm.unwrap(); + + // We get a double result since the actual call returns a result and the runtime api returns + // results. + let weight = + runtime_api.query_xcm_weight(H256::zero(), local_xcm.clone()).unwrap().unwrap(); + assert_eq!(weight, Weight::from_parts(400, 40)); + let execution_fees = runtime_api + .query_weight_to_asset_fee( + H256::zero(), + weight, + VersionedAssetId::V4(HereLocation::get().into()), + ) + .unwrap() + .unwrap(); + assert_eq!(execution_fees, 440); + + let mut forwarded_xcms_iter = dry_run_effects.forwarded_xcms.into_iter(); + + let (destination, remote_messages) = forwarded_xcms_iter.next().unwrap(); + let remote_message = &remote_messages[0]; + + let delivery_fees = runtime_api + .query_delivery_fees(H256::zero(), destination.clone(), remote_message.clone()) + .unwrap() + .unwrap(); + assert_eq!(delivery_fees, VersionedAssets::V4((Here, 20u128).into())); + + // This would have to be the runtime API of the destination, + // which we have the location for. + // If I had a mock runtime configured for "AssetHub" then I would use the + // runtime APIs from that. + let remote_execution_weight = runtime_api + .query_xcm_weight(H256::zero(), remote_message.clone()) + .unwrap() + .unwrap(); + let remote_execution_fees = runtime_api + .query_weight_to_asset_fee( + H256::zero(), + remote_execution_weight, + VersionedAssetId::V4(HereLocation::get().into()), + ) + .unwrap() + .unwrap(); + assert_eq!(remote_execution_fees, 550); + + // Now we know that locally we need to use `execution_fees` and + // `delivery_fees`. + // On the message we forward to the destination, we need to + // put `remote_execution_fees` in `BuyExecution`. + // For the `transfer_assets` extrinsic, it just means passing the correct amount + // of fees in the parameters. + }); +} + +// Same scenario as in `fee_estimation_for_teleport`, but the user in parachain 2000 wants +// to send relay tokens over to parachain 1000. +// +// Reserve Asset Transfer Relay Token +// Reserve Asset Transfer Relay Token for fees +// Parachain(2000) -------------------------------------------> Parachain(1000) +#[test] +fn dry_run_reserve_asset_transfer() { + let _ = env_logger::builder().is_test(true).try_init(); + let who = 1; // AccountId = u64. + // Native token used for fees. + let balances = vec![(who, DeliveryFees::get() + ExistentialDeposit::get())]; + // Relay token is the one we want to transfer. + let assets = vec![(1, who, 100)]; // id, account_id, balance. + new_test_ext_with_balances_and_assets(balances, assets).execute_with(|| { + let client = TestClient; + let runtime_api = client.runtime_api(); + let extrinsic = TestXt::new( + RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::V4((Parent, Parachain(1000)).into())), + beneficiary: Box::new(VersionedLocation::V4( + AccountId32 { id: [0u8; 32], network: None }.into(), + )), + assets: Box::new(VersionedAssets::V4((Parent, 100u128).into())), + fee_asset_item: 0, + weight_limit: Unlimited, + }), + Some((who, extra())), + ); + let dry_run_effects = + runtime_api.dry_run_extrinsic(H256::zero(), extrinsic).unwrap().unwrap(); + + assert_eq!( + dry_run_effects.local_xcm, + Some(VersionedXcm::V4( + Xcm::builder_unsafe() + .withdraw_asset((Parent, 100u128)) + .burn_asset((Parent, 100u128)) + .build() + )), + ); + + // In this case, the transfer type is `DestinationReserve`, so the remote xcm just withdraws + // the assets. + let send_destination = Location::new(1, Parachain(1000)); + let send_message = Xcm::<()>::builder_unsafe() + .withdraw_asset((Parent, 100u128)) + .clear_origin() + .buy_execution((Parent, 100u128), Unlimited) + .deposit_asset(AllCounted(1), [0u8; 32]) + .build(); + assert_eq!( + dry_run_effects.forwarded_xcms, + vec![( + VersionedLocation::V4(send_destination.clone()), + vec![VersionedXcm::V4(send_message.clone())], + ),], + ); + + assert_eq!( + dry_run_effects.emitted_events, + vec![ + RuntimeEvent::AssetsPallet(pallet_assets::Event::Burned { + asset_id: 1, + owner: 1, + balance: 100 + }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { + outcome: Outcome::Complete { used: Weight::from_parts(200, 20) } + }), + RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 20 }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::FeesPaid { + paying: AccountIndex64 { index: 1, network: None }.into(), + fees: (Here, 20u128).into() + }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { + origin: AccountIndex64 { index: 1, network: None }.into(), + destination: send_destination.clone(), + message: send_message.clone(), + message_id: fake_message_hash(&send_message), + }), + RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { + dispatch_info: DispatchInfo { + weight: Weight::from_parts(107074066, 0), /* Will break if weights get + * updated. */ + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + } + }), + ] + ); + }); +} + +#[test] +fn dry_run_xcm() { + let _ = env_logger::builder().is_test(true).try_init(); + let who = 1; // AccountId = u64. + let transfer_amount = 100u128; + // We need to build the XCM to weigh it and then build the real XCM that can pay for fees. + let inner_xcm = Xcm::<()>::builder_unsafe() + .buy_execution((Here, 1u128), Unlimited) // We'd need to query the destination chain for fees. + .deposit_asset(AllCounted(1), [0u8; 32]) + .build(); + let xcm_to_weigh = Xcm::::builder_unsafe() + .withdraw_asset((Here, transfer_amount)) + .clear_origin() + .buy_execution((Here, transfer_amount), Unlimited) + .deposit_reserve_asset(AllCounted(1), (Parent, Parachain(2100)), inner_xcm.clone()) + .build(); + let client = TestClient; + let runtime_api = client.runtime_api(); + let xcm_weight = runtime_api + .query_xcm_weight(H256::zero(), VersionedXcm::V4(xcm_to_weigh.clone().into())) + .unwrap() + .unwrap(); + let execution_fees = runtime_api + .query_weight_to_asset_fee(H256::zero(), xcm_weight, VersionedAssetId::V4(Here.into())) + .unwrap() + .unwrap(); + let xcm = Xcm::::builder_unsafe() + .withdraw_asset((Here, transfer_amount + execution_fees)) + .clear_origin() + .buy_execution((Here, execution_fees), Unlimited) + .deposit_reserve_asset(AllCounted(1), (Parent, Parachain(2100)), inner_xcm.clone()) + .build(); + let balances = vec![( + who, + transfer_amount + execution_fees + DeliveryFees::get() + ExistentialDeposit::get(), + )]; + new_test_ext_with_balances(balances).execute_with(|| { + let dry_run_effects = runtime_api + .dry_run_xcm( + H256::zero(), + VersionedLocation::V4(AccountIndex64 { index: 1, network: None }.into()), + VersionedXcm::V4(xcm), + ) + .unwrap() + .unwrap(); + assert_eq!( + dry_run_effects.forwarded_xcms, + vec![( + VersionedLocation::V4((Parent, Parachain(2100)).into()), + vec![VersionedXcm::V4( + Xcm::<()>::builder_unsafe() + .reserve_asset_deposited(( + (Parent, Parachain(2000)), + transfer_amount + execution_fees - DeliveryFees::get() + )) + .clear_origin() + .buy_execution((Here, 1u128), Unlimited) + .deposit_asset(AllCounted(1), [0u8; 32]) + .build() + )], + ),] + ); + + assert_eq!( + dry_run_effects.emitted_events, + vec![ + RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 540 }), + RuntimeEvent::System(frame_system::Event::NewAccount { account: 2100 }), + RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: 2100, + free_balance: 520 + }), + RuntimeEvent::Balances(pallet_balances::Event::Minted { who: 2100, amount: 520 }), + ] + ); + }); +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs new file mode 100644 index 000000000000..d7b18d90a501 --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -0,0 +1,525 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Substrate 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. + +// Substrate 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 Polkadot. If not, see . + +//! Mock runtime for tests. +//! Implements both runtime APIs for fee estimation and getting the messages for transfers. + +use codec::Encode; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{ + AsEnsureOriginWithArg, ConstU128, ConstU32, Contains, ContainsPair, Everything, Nothing, + OriginTrait, + }, + weights::WeightToFee as WeightToFeeT, +}; +use frame_system::{EnsureRoot, RawOrigin as SystemRawOrigin}; +use pallet_xcm::TestWeightInfo; +use sp_runtime::{ + traits::{Block as BlockT, Get, IdentityLookup, MaybeEquivalence, TryConvert}, + BuildStorage, SaturatedConversion, +}; +use sp_std::{cell::RefCell, marker::PhantomData}; +use xcm::{prelude::*, Version as XcmVersion}; +use xcm_builder::{ + AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, + FixedWeightBounds, FungibleAdapter, FungiblesAdapter, IsConcrete, MintLocation, NoChecking, + TakeWeightCredit, +}; +use xcm_executor::{ + traits::{ConvertLocation, JustTry}, + XcmExecutor, +}; + +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunApi, XcmDryRunEffects}, + fees::{Error as XcmPaymentApiError, XcmPaymentApi}, +}; + +construct_runtime! { + pub enum TestRuntime { + System: frame_system, + Balances: pallet_balances, + AssetsPallet: pallet_assets, + XcmPallet: pallet_xcm, + } +} + +pub type SignedExtra = ( + // frame_system::CheckEra, + // frame_system::CheckNonce, + frame_system::CheckWeight, +); +pub type TestXt = sp_runtime::testing::TestXt; +type Block = sp_runtime::testing::Block; +type Balance = u128; +type AssetIdForAssetsPallet = u32; +type AccountId = u64; + +pub fn extra() -> SignedExtra { + (frame_system::CheckWeight::new(),) +} + +type Executive = frame_executive::Executive< + TestRuntime, + Block, + frame_system::ChainContext, + TestRuntime, + AllPalletsWithSystem, + (), +>; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for TestRuntime { + type Block = Block; + type AccountId = AccountId; + type AccountData = pallet_balances::AccountData; + type Lookup = IdentityLookup; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for TestRuntime { + type AccountStore = System; + type Balance = Balance; + type ExistentialDeposit = ExistentialDeposit; +} + +#[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)] +impl pallet_assets::Config for TestRuntime { + type AssetId = AssetIdForAssetsPallet; + type Balance = Balance; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type Freezer = (); + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +thread_local! { + pub static SENT_XCM: RefCell)>> = const { RefCell::new(Vec::new()) }; +} + +pub(crate) fn sent_xcm() -> Vec<(Location, Xcm<()>)> { + SENT_XCM.with(|q| (*q.borrow()).clone()) +} + +pub struct TestXcmSender; +impl SendXcm for TestXcmSender { + type Ticket = (Location, Xcm<()>); + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult { + let ticket = (dest.take().unwrap(), msg.take().unwrap()); + let fees: Assets = (HereLocation::get(), DeliveryFees::get()).into(); + Ok((ticket, fees)) + } + fn deliver(ticket: Self::Ticket) -> Result { + let hash = fake_message_hash(&ticket.1); + SENT_XCM.with(|q| q.borrow_mut().push(ticket)); + Ok(hash) + } +} + +pub(crate) fn fake_message_hash(message: &Xcm) -> XcmHash { + message.using_encoded(sp_io::hashing::blake2_256) +} + +pub type XcmRouter = TestXcmSender; + +parameter_types! { + pub const DeliveryFees: u128 = 20; // Random value. + pub const ExistentialDeposit: u128 = 1; // Random value. + pub const BaseXcmWeight: Weight = Weight::from_parts(100, 10); // Random value. + pub const MaxInstructions: u32 = 100; + pub const NativeTokenPerSecondPerByte: (AssetId, u128, u128) = (AssetId(HereLocation::get()), 1, 1); + pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Westend), Parachain(2000)].into(); + pub static AdvertisedXcmVersion: XcmVersion = 4; + pub const HereLocation: Location = Location::here(); + pub const RelayLocation: Location = Location::parent(); + pub const MaxAssetsIntoHolding: u32 = 64; + pub CheckAccount: AccountId = XcmPallet::check_account(); + pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); + pub const AnyNetwork: Option = None; +} + +/// Simple `WeightToFee` implementation that adds the ref_time by the proof_size. +pub struct WeightToFee; +impl WeightToFeeT for WeightToFee { + type Balance = Balance; + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_add(Self::Balance::saturated_from(weight.proof_size())) + } +} + +type Weigher = FixedWeightBounds; + +/// Matches the pair (NativeToken, AssetHub). +/// This is used in the `IsTeleporter` configuration item, meaning we accept our native token +/// coming from AssetHub as a teleport. +pub struct NativeTokenToAssetHub; +impl ContainsPair for NativeTokenToAssetHub { + fn contains(asset: &Asset, origin: &Location) -> bool { + matches!(asset.id.0.unpack(), (0, [])) && matches!(origin.unpack(), (1, [Parachain(1000)])) + } +} + +/// Matches the pair (RelayToken, AssetHub). +/// This is used in the `IsReserve` configuration item, meaning we accept the relay token +/// coming from AssetHub as a reserve asset transfer. +pub struct RelayTokenToAssetHub; +impl ContainsPair for RelayTokenToAssetHub { + fn contains(asset: &Asset, origin: &Location) -> bool { + matches!(asset.id.0.unpack(), (1, [])) && matches!(origin.unpack(), (1, [Parachain(1000)])) + } +} + +/// Converts locations that are only the `AccountIndex64` junction into local u64 accounts. +pub struct AccountIndex64Aliases(PhantomData<(Network, AccountId)>); +impl>, AccountId: From> ConvertLocation + for AccountIndex64Aliases +{ + fn convert_location(location: &Location) -> Option { + let index = match location.unpack() { + (0, [AccountIndex64 { index, network: None }]) => index, + (0, [AccountIndex64 { index, network }]) if *network == Network::get() => index, + _ => return None, + }; + Some((*index).into()) + } +} + +/// Custom location converter to turn sibling chains into u64 accounts. +pub struct SiblingChainToIndex64; +impl ConvertLocation for SiblingChainToIndex64 { + fn convert_location(location: &Location) -> Option { + let index = match location.unpack() { + (1, [Parachain(id)]) => id, + _ => return None, + }; + Some((*index).into()) + } +} + +/// We alias local account locations to actual local accounts. +/// We also allow sovereign accounts for other sibling chains. +pub type LocationToAccountId = (AccountIndex64Aliases, SiblingChainToIndex64); + +pub type NativeTokenTransactor = FungibleAdapter< + // We use pallet-balances for handling this fungible asset. + Balances, + // The fungible asset handled by this transactor is the native token of the chain. + IsConcrete, + // How we convert locations to accounts. + LocationToAccountId, + // We need to specify the AccountId type. + AccountId, + // We mint the native tokens locally, so we track how many we've sent away via teleports. + LocalCheckAccount, +>; + +pub struct LocationToAssetIdForAssetsPallet; +impl MaybeEquivalence for LocationToAssetIdForAssetsPallet { + fn convert(location: &Location) -> Option { + match location.unpack() { + (1, []) => Some(1 as AssetIdForAssetsPallet), + _ => None, + } + } + + fn convert_back(id: &AssetIdForAssetsPallet) -> Option { + match id { + 1 => Some(Location::new(1, [])), + _ => None, + } + } +} + +/// AssetTransactor for handling the relay chain token. +pub type RelayTokenTransactor = FungiblesAdapter< + // We use pallet-assets for handling the relay token. + AssetsPallet, + // Matches the relay token. + ConvertedConcreteId, + // How we convert locations to accounts. + LocationToAccountId, + // We need to specify the AccountId type. + AccountId, + // We don't track teleports. + NoChecking, + (), +>; + +pub type AssetTransactors = (NativeTokenTransactor, RelayTokenTransactor); + +pub struct HereAndInnerLocations; +impl Contains for HereAndInnerLocations { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (0, []) | (0, _)) + } +} + +pub type Barrier = ( + TakeWeightCredit, // We need this for pallet-xcm's extrinsics to work. + AllowTopLevelPaidExecutionFrom, /* TODO: Technically, we should allow + * messages from "AssetHub". */ +); + +pub type Trader = FixedRateOfFungible; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = (); + type IsReserve = RelayTokenToAssetHub; + type IsTeleporter = NativeTokenToAssetHub; + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = Weigher; + type Trader = Trader; + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = (); + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Nothing; + type Aliasers = Nothing; + type TransactionalProcessor = (); + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; +} + +/// Converts a signed origin of a u64 account into a location with only the `AccountIndex64` +/// junction. +pub struct SignedToAccountIndex64( + PhantomData<(RuntimeOrigin, AccountId)>, +); +impl> TryConvert + for SignedToAccountIndex64 +where + RuntimeOrigin::PalletsOrigin: From> + + TryInto, Error = RuntimeOrigin::PalletsOrigin>, +{ + fn try_convert(origin: RuntimeOrigin) -> Result { + origin.try_with_caller(|caller| match caller.try_into() { + Ok(SystemRawOrigin::Signed(who)) => + Ok(Junction::AccountIndex64 { network: None, index: who.into() }.into()), + Ok(other) => Err(other.into()), + Err(other) => Err(other), + }) + } +} + +pub type LocalOriginToLocation = SignedToAccountIndex64; + +impl pallet_xcm::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; // Put everything instead of something more restricted. + type XcmReserveTransferFilter = Everything; // Same. + type Weigher = Weigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = AdvertisedXcmVersion; + type AdminOrigin = EnsureRoot; + type TrustedLockers = (); + type SovereignAccountOf = (); + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type MaxLockers = ConstU32<0>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type WeightInfo = TestWeightInfo; +} + +pub fn new_test_ext_with_balances(balances: Vec<(AccountId, Balance)>) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +pub fn new_test_ext_with_balances_and_assets( + balances: Vec<(AccountId, Balance)>, + assets: Vec<(AssetIdForAssetsPallet, AccountId, Balance)>, +) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_assets::GenesisConfig:: { + assets: vec![ + // id, owner, is_sufficient, min_balance. + // We don't actually need this to be sufficient, since we use the native assets in + // tests for the existential deposit. + (1, 0, true, 1), + ], + metadata: vec![ + // id, name, symbol, decimals. + (1, "Relay Token".into(), "RLY".into(), 12), + ], + accounts: assets, + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +#[derive(Clone)] +pub(crate) struct TestClient; + +pub(crate) struct RuntimeApi { + _inner: TestClient, +} + +impl sp_api::ProvideRuntimeApi for TestClient { + type Api = RuntimeApi; + fn runtime_api(&self) -> sp_api::ApiRef { + RuntimeApi { _inner: self.clone() }.into() + } +} + +sp_api::mock_impl_runtime_apis! { + impl XcmPaymentApi for RuntimeApi { + fn query_acceptable_payment_assets(xcm_version: XcmVersion) -> Result, XcmPaymentApiError> { + if xcm_version != 4 { return Err(XcmPaymentApiError::UnhandledXcmVersion) }; + Ok(vec![VersionedAssetId::V4(HereLocation::get().into())]) + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + XcmPallet::query_xcm_weight(message) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let local_asset = VersionedAssetId::V4(HereLocation::get().into()); + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } + + Ok(WeightToFee::weight_to_fee(&weight)) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + XcmPallet::query_delivery_fees(destination, message) + } + } + + impl XcmDryRunApi for RuntimeApi { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_executor::RecordXcm; + // We want to record the XCM that's executed, so we can return it. + pallet_xcm::Pallet::::set_record_xcm(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + // Nothing gets committed to storage in runtime APIs, so there's no harm in leaving the flag as true. + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); + let forwarded_xcms = sent_xcm() + .into_iter() + .map(|(location, message)| ( + VersionedLocation::V4(location), + vec![VersionedXcm::V4(message)], + )).collect(); + let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let xcm: Xcm = xcm.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = fake_message_hash(&xcm); + let result = XcmExecutor::::prepare_and_execute( + origin_location, + xcm, + &mut hash, + Weight::MAX, // Max limit available for execution. + Weight::zero(), + ); + let forwarded_xcms = sent_xcm() + .into_iter() + .map(|(location, message)| ( + VersionedLocation::V4(location), + vec![VersionedXcm::V4(message)], + )).collect(); + let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + } +} diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/mod.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/mod.rs index 0ba02aab9bf9..a6b55d1bd9be 100644 --- a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/mod.rs +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/mod.rs @@ -60,4 +60,5 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs index a7a8bae51567..c5d5fa66732b 100644 --- a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs @@ -59,4 +59,5 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs index d8d65fbf0ce7..502bcca2d442 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -159,6 +159,7 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } #[frame_support::pallet] diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index cf3ca0de2bb4..4740aee83d87 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -160,6 +160,7 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/prdoc/pr_3872.prdoc b/prdoc/pr_3872.prdoc new file mode 100644 index 000000000000..3a5be3d2bc74 --- /dev/null +++ b/prdoc/pr_3872.prdoc @@ -0,0 +1,86 @@ +title: XcmDryRunApi - Runtime API for dry-running extrinsics and XCM programs. + +doc: + - audience: Runtime Dev + description: | + This PR introduces a new runtime API, the XcmDryRunApi, that allows dry-running + extrinsics and XCM programs to get their execution effects. + These effects include: + - Local execution result, either pass or fail + - Emitted events + - Forwarded XCMs + - In the case of extrinsics, the XCM program that they execute + This API can be used on its own to test extrinsics or XCM programs, + or used alongside the XcmPaymentApi to estimate execution and delivery + fees. + + This PR also adds a new configuration item to XCM: XcmRecorder. + This can be set to either (), the xcm pallet, or some custom implementation. + If set to (), the dry run API will not return the local XCM program executed + by running an extrinsic. + After this PR, it is necessary to add the new configuration item to your xcm + configs. + - audience: Runtime User + description: | + This PR introduces a new runtime API, the XcmDryRunApi, that allows dry-running + extrinsics and XCM programs to get their execution effects. + These effects include: + - Local execution result, either pass or fail + - Emitted events + - Forwarded XCMs + - In the case of extrinsics, the XCM program that they execute + This API can be used on its own to test extrinsics or XCM programs, + or used alongside the XcmPaymentApi to estimate execution and delivery + fees. + +crates: + - name: xcm-fee-payment-runtime-api + bump: major + - name: pallet-xcm + bump: minor + - name: staging-xcm-executor + bump: minor + - name: staging-xcm-builder + bump: minor + - name: rococo-runtime + bump: minor + - name: westend-runtime + bump: minor + - name: pallet-xcm-benchmarks + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: bridge-hub-rococo-runtime + bump: minor + - name: bridge-hub-westend-runtime + bump: minor + - name: collectives-westend-runtime + bump: minor + - name: contracts-rococo-runtime + bump: minor + - name: coretime-rococo-runtime + bump: minor + - name: coretime-westend-runtime + bump: minor + - name: glutton-westend-runtime + bump: minor + - name: people-rococo-runtime + bump: minor + - name: people-westend-runtime + bump: minor + - name: shell-runtime + bump: minor + - name: penpal-runtime + bump: minor + - name: rococo-parachain-runtime + bump: minor + - name: polkadot-service + bump: minor + - name: polkadot-test-runtime + bump: minor + - name: parachain-template-runtime + bump: minor + - name: pallet-contracts-mock-network + bump: minor diff --git a/substrate/frame/contracts/mock-network/src/parachain.rs b/substrate/frame/contracts/mock-network/src/parachain.rs index 843efab1502e..b46d7df6c2bc 100644 --- a/substrate/frame/contracts/mock-network/src/parachain.rs +++ b/substrate/frame/contracts/mock-network/src/parachain.rs @@ -285,6 +285,7 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } impl mock_msg_queue::Config for Runtime { diff --git a/substrate/frame/contracts/mock-network/src/relay_chain.rs b/substrate/frame/contracts/mock-network/src/relay_chain.rs index d5e0ec9c83fa..36a7de499ba9 100644 --- a/substrate/frame/contracts/mock-network/src/relay_chain.rs +++ b/substrate/frame/contracts/mock-network/src/relay_chain.rs @@ -185,6 +185,7 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/templates/parachain/runtime/src/configs/xcm_config.rs b/templates/parachain/runtime/src/configs/xcm_config.rs index c6b6e8da1b89..e162bcbf8868 100644 --- a/templates/parachain/runtime/src/configs/xcm_config.rs +++ b/templates/parachain/runtime/src/configs/xcm_config.rs @@ -142,6 +142,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. From c3e57c1b3b9fdd768a57e91d353d57f6bbff7ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Pa=C4=8Dandi?= <3002868+Dinonard@users.noreply.github.com> Date: Wed, 8 May 2024 09:38:11 +0200 Subject: [PATCH 02/10] [pallet-balances] `burn_allow_death` extrinsic (#3964) Adds an additional extrinsic call to the `pallet-balances` to _burn_ tokens. Depending on the `keep_alive` flag, the call might or might not reap the account. Required modification of the _fungible's_ `Mutate` trait, `burn_from` function to allow the `Preservation` argument. **TODO** - [x] run benchmarks & update weights - [x] make sure prdoc is required & properly formatted Related issue: https://github.com/paritytech/polkadot-sdk/issues/3943 --------- Co-authored-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> --- .../src/weights/pallet_balances.rs | 60 ++++--- .../src/weights/pallet_balances.rs | 60 ++++--- .../src/weights/pallet_balances.rs | 60 ++++--- .../src/weights/pallet_balances.rs | 60 ++++--- .../src/weights/pallet_balances.rs | 60 ++++--- .../src/weights/pallet_balances.rs | 60 ++++--- .../src/weights/pallet_balances.rs | 76 +++++---- .../src/weights/pallet_balances.rs | 152 ++++++++++-------- .../src/weights/pallet_balances.rs | 152 ++++++++++-------- .../src/weights/pallet_balances_balances.rs | 62 ++++--- ...allet_balances_nis_counterpart_balances.rs | 70 +++++--- .../westend/src/weights/pallet_balances.rs | 62 ++++--- .../xcm/xcm-builder/src/fungible_adapter.rs | 10 +- .../xcm/xcm-builder/src/fungibles_adapter.rs | 11 +- prdoc/pr_3964.prdoc | 16 ++ .../frame/asset-conversion/ops/src/lib.rs | 3 + substrate/frame/asset-conversion/src/lib.rs | 9 +- substrate/frame/balances/src/benchmarking.rs | 38 +++++ substrate/frame/balances/src/lib.rs | 26 +++ .../balances/src/tests/currency_tests.rs | 2 +- .../balances/src/tests/dispatchable_tests.rs | 44 +++++ substrate/frame/balances/src/weights.rs | 126 +++++++++------ .../frame/nft-fractionalization/src/lib.rs | 4 +- substrate/frame/nis/src/lib.rs | 3 +- substrate/frame/nis/src/tests.rs | 8 +- .../conformance_tests/inspect_mutate.rs | 9 +- .../conformance_tests/regular/mutate.rs | 9 +- .../src/traits/tokens/fungible/item_of.rs | 10 +- .../src/traits/tokens/fungible/regular.rs | 17 +- .../src/traits/tokens/fungible/union_of.rs | 20 ++- .../src/traits/tokens/fungibles/regular.rs | 8 +- .../src/traits/tokens/fungibles/union_of.rs | 21 ++- 32 files changed, 876 insertions(+), 452 deletions(-) create mode 100644 prdoc/pr_3964.prdoc diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs index 299a801ebd59..35d7e1985c51 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_balances` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-01-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-8idpd4bs-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 42_706_000 picoseconds. - Weight::from_parts(43_378_000, 0) + // Minimum execution time: 43_472_000 picoseconds. + Weight::from_parts(44_389_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 33_090_000 picoseconds. - Weight::from_parts(33_703_000, 0) + // Minimum execution time: 34_211_000 picoseconds. + Weight::from_parts(35_075_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 12_678_000 picoseconds. - Weight::from_parts(13_068_000, 0) + // Minimum execution time: 12_751_000 picoseconds. + Weight::from_parts(13_221_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 17_336_000 picoseconds. - Weight::from_parts(17_824_000, 0) + // Minimum execution time: 17_530_000 picoseconds. + Weight::from_parts(17_979_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -102,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 44_817_000 picoseconds. - Weight::from_parts(45_453_000, 0) + // Minimum execution time: 45_913_000 picoseconds. + Weight::from_parts(47_447_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -114,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 41_468_000 picoseconds. - Weight::from_parts(42_093_000, 0) + // Minimum execution time: 42_435_000 picoseconds. + Weight::from_parts(44_712_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 15_344_000 picoseconds. - Weight::from_parts(15_878_000, 0) + // Minimum execution time: 15_407_000 picoseconds. + Weight::from_parts(16_104_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -139,11 +139,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 15_067_000 picoseconds. - Weight::from_parts(15_281_000, 0) + // Minimum execution time: 15_494_000 picoseconds. + Weight::from_parts(15_793_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 11_009 - .saturating_add(Weight::from_parts(13_050_024, 0).saturating_mul(u.into())) + // Standard Error: 11_778 + .saturating_add(Weight::from_parts(13_198_951, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -154,9 +154,25 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1501` - // Minimum execution time: 5_139_000 picoseconds. - Weight::from_parts(5_511_000, 0) + // Minimum execution time: 5_368_000 picoseconds. + Weight::from_parts(5_674_000, 0) .saturating_add(Weight::from_parts(0, 1501)) .saturating_add(T::DbWeight::get().reads(1)) } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 27_491_000 picoseconds. + Weight::from_parts(28_444_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_290_000 picoseconds. + Weight::from_parts(19_227_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_balances.rs index 68aceca14c15..bb8ae8e5f97e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_balances.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_balances` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-01-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-8idpd4bs-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 43_122_000 picoseconds. - Weight::from_parts(43_640_000, 0) + // Minimum execution time: 45_289_000 picoseconds. + Weight::from_parts(46_764_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 33_636_000 picoseconds. - Weight::from_parts(34_571_000, 0) + // Minimum execution time: 35_052_000 picoseconds. + Weight::from_parts(36_494_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 12_101_000 picoseconds. - Weight::from_parts(12_511_000, 0) + // Minimum execution time: 12_361_000 picoseconds. + Weight::from_parts(12_668_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 17_077_000 picoseconds. - Weight::from_parts(17_362_000, 0) + // Minimum execution time: 17_253_000 picoseconds. + Weight::from_parts(17_733_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -102,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 44_352_000 picoseconds. - Weight::from_parts(45_045_000, 0) + // Minimum execution time: 45_674_000 picoseconds. + Weight::from_parts(47_981_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -114,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 41_836_000 picoseconds. - Weight::from_parts(43_201_000, 0) + // Minimum execution time: 45_021_000 picoseconds. + Weight::from_parts(46_292_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 14_413_000 picoseconds. - Weight::from_parts(14_743_000, 0) + // Minimum execution time: 15_071_000 picoseconds. + Weight::from_parts(15_406_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -139,11 +139,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 14_542_000 picoseconds. - Weight::from_parts(14_731_000, 0) + // Minimum execution time: 14_779_000 picoseconds. + Weight::from_parts(15_129_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 11_213 - .saturating_add(Weight::from_parts(13_160_721, 0).saturating_mul(u.into())) + // Standard Error: 10_629 + .saturating_add(Weight::from_parts(13_558_995, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -154,9 +154,25 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1501` - // Minimum execution time: 5_208_000 picoseconds. - Weight::from_parts(5_619_000, 0) + // Minimum execution time: 5_274_000 picoseconds. + Weight::from_parts(5_727_000, 0) .saturating_add(Weight::from_parts(0, 1501)) .saturating_add(T::DbWeight::get().reads(1)) } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 28_088_000 picoseconds. + Weight::from_parts(28_980_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 19_002_000 picoseconds. + Weight::from_parts(19_480_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs index 861ccfc51fd8..d67ae4dee92a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_balances` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-01-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-8idpd4bs-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 41_696_000 picoseconds. - Weight::from_parts(42_201_000, 0) + // Minimum execution time: 41_898_000 picoseconds. + Weight::from_parts(42_690_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 32_855_000 picoseconds. - Weight::from_parts(33_554_000, 0) + // Minimum execution time: 32_745_000 picoseconds. + Weight::from_parts(33_686_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 12_977_000 picoseconds. - Weight::from_parts(13_473_000, 0) + // Minimum execution time: 13_352_000 picoseconds. + Weight::from_parts(13_808_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_617_000 picoseconds. - Weight::from_parts(18_234_000, 0) + // Minimum execution time: 18_248_000 picoseconds. + Weight::from_parts(18_763_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -102,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 43_174_000 picoseconds. - Weight::from_parts(43_685_000, 0) + // Minimum execution time: 43_626_000 picoseconds. + Weight::from_parts(45_333_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -114,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 41_125_000 picoseconds. - Weight::from_parts(41_636_000, 0) + // Minimum execution time: 41_702_000 picoseconds. + Weight::from_parts(43_366_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 15_749_000 picoseconds. - Weight::from_parts(16_163_000, 0) + // Minimum execution time: 15_944_000 picoseconds. + Weight::from_parts(16_512_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -139,11 +139,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 14_238_000 picoseconds. - Weight::from_parts(14_469_000, 0) + // Minimum execution time: 14_351_000 picoseconds. + Weight::from_parts(14_568_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 11_818 - .saturating_add(Weight::from_parts(12_621_051, 0).saturating_mul(u.into())) + // Standard Error: 11_289 + .saturating_add(Weight::from_parts(13_163_759, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -154,9 +154,25 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1501` - // Minimum execution time: 4_904_000 picoseconds. - Weight::from_parts(5_459_000, 0) + // Minimum execution time: 5_174_000 picoseconds. + Weight::from_parts(5_490_000, 0) .saturating_add(Weight::from_parts(0, 1501)) .saturating_add(T::DbWeight::get().reads(1)) } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 26_681_000 picoseconds. + Weight::from_parts(27_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_105_000 picoseconds. + Weight::from_parts(19_246_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_balances.rs index 3afef6564bdb..34ce487216f2 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_balances.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_balances` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-01-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-8idpd4bs-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 42_912_000 picoseconds. - Weight::from_parts(43_690_000, 0) + // Minimum execution time: 42_637_000 picoseconds. + Weight::from_parts(44_357_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 33_823_000 picoseconds. - Weight::from_parts(34_415_000, 0) + // Minimum execution time: 33_463_000 picoseconds. + Weight::from_parts(34_484_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 13_226_000 picoseconds. - Weight::from_parts(13_557_000, 0) + // Minimum execution time: 13_115_000 picoseconds. + Weight::from_parts(13_749_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 18_055_000 picoseconds. - Weight::from_parts(18_407_000, 0) + // Minimum execution time: 17_825_000 picoseconds. + Weight::from_parts(18_471_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -102,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 44_442_000 picoseconds. - Weight::from_parts(45_101_000, 0) + // Minimum execution time: 43_669_000 picoseconds. + Weight::from_parts(45_781_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -114,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 42_485_000 picoseconds. - Weight::from_parts(43_157_000, 0) + // Minimum execution time: 41_572_000 picoseconds. + Weight::from_parts(43_812_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_002_000 picoseconds. - Weight::from_parts(16_425_000, 0) + // Minimum execution time: 15_538_000 picoseconds. + Weight::from_parts(16_227_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -139,11 +139,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 14_526_000 picoseconds. - Weight::from_parts(14_825_000, 0) + // Minimum execution time: 13_979_000 picoseconds. + Weight::from_parts(14_195_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 10_967 - .saturating_add(Weight::from_parts(13_376_293, 0).saturating_mul(u.into())) + // Standard Error: 11_039 + .saturating_add(Weight::from_parts(13_102_916, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -154,9 +154,25 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1501` - // Minimum execution time: 5_151_000 picoseconds. - Weight::from_parts(5_419_000, 0) + // Minimum execution time: 4_959_000 picoseconds. + Weight::from_parts(5_377_000, 0) .saturating_add(Weight::from_parts(0, 1501)) .saturating_add(T::DbWeight::get().reads(1)) } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 26_604_000 picoseconds. + Weight::from_parts(27_641_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_106_000 picoseconds. + Weight::from_parts(18_637_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_balances.rs index 602e7ca50c13..b100b0f2b1a8 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_balances.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_balances` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-01-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-8idpd4bs-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 45_085_000 picoseconds. - Weight::from_parts(45_772_000, 0) + // Minimum execution time: 46_316_000 picoseconds. + Weight::from_parts(46_965_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 35_447_000 picoseconds. - Weight::from_parts(36_143_000, 0) + // Minimum execution time: 36_337_000 picoseconds. + Weight::from_parts(36_803_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 12_314_000 picoseconds. - Weight::from_parts(12_679_000, 0) + // Minimum execution time: 12_331_000 picoseconds. + Weight::from_parts(12_774_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 17_455_000 picoseconds. - Weight::from_parts(17_902_000, 0) + // Minimum execution time: 17_532_000 picoseconds. + Weight::from_parts(17_948_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -102,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 46_785_000 picoseconds. - Weight::from_parts(47_436_000, 0) + // Minimum execution time: 47_251_000 picoseconds. + Weight::from_parts(48_164_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -114,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 43_948_000 picoseconds. - Weight::from_parts(44_680_000, 0) + // Minimum execution time: 45_319_000 picoseconds. + Weight::from_parts(46_094_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 15_267_000 picoseconds. - Weight::from_parts(15_499_000, 0) + // Minimum execution time: 15_263_000 picoseconds. + Weight::from_parts(15_632_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -139,11 +139,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 14_817_000 picoseconds. - Weight::from_parts(15_287_000, 0) + // Minimum execution time: 15_106_000 picoseconds. + Weight::from_parts(15_353_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 11_738 - .saturating_add(Weight::from_parts(13_511_800, 0).saturating_mul(u.into())) + // Standard Error: 11_570 + .saturating_add(Weight::from_parts(13_765_985, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -154,9 +154,25 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1501` - // Minimum execution time: 5_382_000 picoseconds. - Weight::from_parts(5_768_000, 0) + // Minimum execution time: 5_277_000 picoseconds. + Weight::from_parts(5_560_000, 0) .saturating_add(Weight::from_parts(0, 1501)) .saturating_add(T::DbWeight::get().reads(1)) } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 28_810_000 picoseconds. + Weight::from_parts(29_155_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_957_000 picoseconds. + Weight::from_parts(19_292_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_balances.rs index aac7e1093661..a021d1147848 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_balances.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_balances` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-01-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-8idpd4bs-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 41_557_000 picoseconds. - Weight::from_parts(42_618_000, 0) + // Minimum execution time: 43_792_000 picoseconds. + Weight::from_parts(44_475_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 33_046_000 picoseconds. - Weight::from_parts(33_550_000, 0) + // Minimum execution time: 34_144_000 picoseconds. + Weight::from_parts(34_887_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 11_804_000 picoseconds. - Weight::from_parts(12_007_000, 0) + // Minimum execution time: 11_864_000 picoseconds. + Weight::from_parts(12_253_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 16_261_000 picoseconds. - Weight::from_parts(16_655_000, 0) + // Minimum execution time: 16_448_000 picoseconds. + Weight::from_parts(17_008_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -102,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 42_967_000 picoseconds. - Weight::from_parts(43_870_000, 0) + // Minimum execution time: 44_353_000 picoseconds. + Weight::from_parts(45_131_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -114,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 41_022_000 picoseconds. - Weight::from_parts(41_475_000, 0) + // Minimum execution time: 42_899_000 picoseconds. + Weight::from_parts(43_749_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 14_339_000 picoseconds. - Weight::from_parts(14_641_000, 0) + // Minimum execution time: 14_308_000 picoseconds. + Weight::from_parts(15_020_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -139,11 +139,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 14_241_000 picoseconds. - Weight::from_parts(14_463_000, 0) + // Minimum execution time: 14_369_000 picoseconds. + Weight::from_parts(14_525_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 12_290 - .saturating_add(Weight::from_parts(12_903_900, 0).saturating_mul(u.into())) + // Standard Error: 11_260 + .saturating_add(Weight::from_parts(13_056_576, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -154,9 +154,25 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1501` - // Minimum execution time: 5_116_000 picoseconds. - Weight::from_parts(5_345_000, 0) + // Minimum execution time: 5_198_000 picoseconds. + Weight::from_parts(5_430_000, 0) .saturating_add(Weight::from_parts(0, 1501)) .saturating_add(T::DbWeight::get().reads(1)) } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 27_335_000 picoseconds. + Weight::from_parts(28_146_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_390_000 picoseconds. + Weight::from_parts(18_893_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_balances.rs index c4770a7c9438..7024c58d97f9 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_balances.rs @@ -17,25 +17,23 @@ //! Autogenerated weights for `pallet_balances` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=coretime-westend-dev -// --wasm-execution=compiled -// --pallet=pallet_balances -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=coretime-westend-dev // --header=./cumulus/file_header.txt // --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ @@ -56,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 42_773_000 picoseconds. - Weight::from_parts(43_292_000, 0) + // Minimum execution time: 44_250_000 picoseconds. + Weight::from_parts(45_303_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -68,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 34_023_000 picoseconds. - Weight::from_parts(34_513_000, 0) + // Minimum execution time: 34_451_000 picoseconds. + Weight::from_parts(35_413_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -80,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 11_685_000 picoseconds. - Weight::from_parts(12_103_000, 0) + // Minimum execution time: 11_886_000 picoseconds. + Weight::from_parts(12_158_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -92,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 16_233_000 picoseconds. - Weight::from_parts(16_706_000, 0) + // Minimum execution time: 16_457_000 picoseconds. + Weight::from_parts(16_940_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -104,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 43_909_000 picoseconds. - Weight::from_parts(44_683_000, 0) + // Minimum execution time: 45_416_000 picoseconds. + Weight::from_parts(46_173_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -116,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 42_081_000 picoseconds. - Weight::from_parts(42_553_000, 0) + // Minimum execution time: 43_502_000 picoseconds. + Weight::from_parts(44_060_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -128,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 14_413_000 picoseconds. - Weight::from_parts(14_827_000, 0) + // Minimum execution time: 14_790_000 picoseconds. + Weight::from_parts(15_451_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -141,11 +139,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 14_189_000 picoseconds. - Weight::from_parts(14_587_000, 0) + // Minimum execution time: 14_582_000 picoseconds. + Weight::from_parts(14_797_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 10_909 - .saturating_add(Weight::from_parts(13_040_864, 0).saturating_mul(u.into())) + // Standard Error: 12_074 + .saturating_add(Weight::from_parts(13_220_968, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -156,9 +154,25 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1501` - // Minimum execution time: 5_218_000 picoseconds. - Weight::from_parts(5_562_000, 0) + // Minimum execution time: 4_939_000 picoseconds. + Weight::from_parts(5_403_000, 0) .saturating_add(Weight::from_parts(0, 1501)) .saturating_add(T::DbWeight::get().reads(1)) } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 27_479_000 picoseconds. + Weight::from_parts(28_384_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_174_000 picoseconds. + Weight::from_parts(18_737_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_balances.rs index 126d816afcdb..4990e8c12d5a 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_balances.rs @@ -1,40 +1,41 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . //! Autogenerated weights for `pallet_balances` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-kusama-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-rococo-dev")`, DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=people-kusama-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_balances -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-kusama/src/weights/pallet_balances.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=people-rococo-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,112 +48,131 @@ use core::marker::PhantomData; /// Weight functions for `pallet_balances`. pub struct WeightInfo(PhantomData); impl pallet_balances::WeightInfo for WeightInfo { - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 63_775_000 picoseconds. - Weight::from_parts(64_181_000, 0) + // Minimum execution time: 42_847_000 picoseconds. + Weight::from_parts(44_471_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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: `0` // Estimated: `3593` - // Minimum execution time: 47_986_000 picoseconds. - Weight::from_parts(48_308_000, 0) + // Minimum execution time: 33_076_000 picoseconds. + Weight::from_parts(35_052_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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_set_balance_creating() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 18_083_000 picoseconds. - Weight::from_parts(18_380_000, 0) + // Minimum execution time: 13_422_000 picoseconds. + Weight::from_parts(13_682_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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_set_balance_killing() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 26_341_000 picoseconds. - Weight::from_parts(26_703_000, 0) + // Minimum execution time: 18_360_000 picoseconds. + Weight::from_parts(18_721_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 66_227_000 picoseconds. - Weight::from_parts(67_321_000, 0) + // Minimum execution time: 44_647_000 picoseconds. + Weight::from_parts(46_142_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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_all() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 59_472_000 picoseconds. - Weight::from_parts(60_842_000, 0) + // Minimum execution time: 41_807_000 picoseconds. + Weight::from_parts(44_490_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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_unreserve() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 21_497_000 picoseconds. - Weight::from_parts(21_684_000, 0) + // Minimum execution time: 16_032_000 picoseconds. + Weight::from_parts(16_694_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `System::Account` (r:999 w:999) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `u` is `[1, 1000]`. + fn upgrade_accounts(u: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + u * (136 ±0)` + // Estimated: `990 + u * (2603 ±0)` + // Minimum execution time: 14_593_000 picoseconds. + Weight::from_parts(14_767_000, 0) + .saturating_add(Weight::from_parts(0, 990)) + // Standard Error: 11_218 + .saturating_add(Weight::from_parts(13_432_648, 0).saturating_mul(u.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) + } /// Storage: `Balances::InactiveIssuance` (r:1 w:0) /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) fn force_adjust_total_issuance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1501` - // Minimum execution time: 5_132_000 picoseconds. - Weight::from_parts(5_467_000, 0) + // Minimum execution time: 5_044_000 picoseconds. + Weight::from_parts(5_368_000, 0) .saturating_add(Weight::from_parts(0, 1501)) .saturating_add(T::DbWeight::get().reads(1)) } - fn upgrade_accounts(u: u32, ) -> Weight { + fn burn_allow_death() -> Weight { // Proof Size summary in bytes: - // Measured: `0 + u * (136 ±0)` - // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 20_385_000 picoseconds. - Weight::from_parts(20_587_000, 0) - .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 10_001 - .saturating_add(Weight::from_parts(16_801_557, 0).saturating_mul(u.into())) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) - .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 26_868_000 picoseconds. + Weight::from_parts(27_921_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 17_988_000 picoseconds. + Weight::from_parts(18_962_000, 0) + .saturating_add(Weight::from_parts(0, 0)) } } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_balances.rs index 1a3df158a0d0..2649c1557a2f 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_balances.rs @@ -1,40 +1,41 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . //! Autogenerated weights for `pallet_balances` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-polkadot-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-westend-dev")`, DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=people-polkadot-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_balances -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-polkadot/src/weights/pallet_balances.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=people-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,112 +48,131 @@ use core::marker::PhantomData; /// Weight functions for `pallet_balances`. pub struct WeightInfo(PhantomData); impl pallet_balances::WeightInfo for WeightInfo { - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 59_580_000 picoseconds. - Weight::from_parts(60_317_000, 0) + // Minimum execution time: 42_705_000 picoseconds. + Weight::from_parts(43_367_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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: `0` // Estimated: `3593` - // Minimum execution time: 45_490_000 picoseconds. - Weight::from_parts(45_910_000, 0) + // Minimum execution time: 33_334_000 picoseconds. + Weight::from_parts(34_183_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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_set_balance_creating() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_353_000 picoseconds. - Weight::from_parts(17_676_000, 0) + // Minimum execution time: 13_036_000 picoseconds. + Weight::from_parts(13_392_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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_set_balance_killing() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 25_017_000 picoseconds. - Weight::from_parts(25_542_000, 0) + // Minimum execution time: 17_734_000 picoseconds. + Weight::from_parts(18_504_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 61_161_000 picoseconds. - Weight::from_parts(61_665_000, 0) + // Minimum execution time: 44_343_000 picoseconds. + Weight::from_parts(44_783_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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_all() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 55_422_000 picoseconds. - Weight::from_parts(55_880_000, 0) + // Minimum execution time: 41_562_000 picoseconds. + Weight::from_parts(42_397_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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_unreserve() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 20_477_000 picoseconds. - Weight::from_parts(20_871_000, 0) + // Minimum execution time: 15_547_000 picoseconds. + Weight::from_parts(16_072_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `System::Account` (r:999 w:999) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `u` is `[1, 1000]`. + fn upgrade_accounts(u: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + u * (136 ±0)` + // Estimated: `990 + u * (2603 ±0)` + // Minimum execution time: 13_969_000 picoseconds. + Weight::from_parts(14_302_000, 0) + .saturating_add(Weight::from_parts(0, 990)) + // Standard Error: 12_004 + .saturating_add(Weight::from_parts(12_993_439, 0).saturating_mul(u.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) + } /// Storage: `Balances::InactiveIssuance` (r:1 w:0) /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) fn force_adjust_total_issuance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1501` - // Minimum execution time: 5_132_000 picoseconds. - Weight::from_parts(5_467_000, 0) + // Minimum execution time: 4_854_000 picoseconds. + Weight::from_parts(5_148_000, 0) .saturating_add(Weight::from_parts(0, 1501)) .saturating_add(T::DbWeight::get().reads(1)) } - fn upgrade_accounts(u: u32, ) -> Weight { + fn burn_allow_death() -> Weight { // Proof Size summary in bytes: - // Measured: `0 + u * (136 ±0)` - // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 19_501_000 picoseconds. - Weight::from_parts(19_726_000, 0) - .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 9_495 - .saturating_add(Weight::from_parts(15_658_957, 0).saturating_mul(u.into())) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) - .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 26_532_000 picoseconds. + Weight::from_parts(27_418_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_148_000 picoseconds. + Weight::from_parts(18_809_000, 0) + .saturating_add(Weight::from_parts(0, 0)) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs b/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs index 1b0ae1eeece4..d37bb9369c68 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_balances` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-05-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 44_127_000 picoseconds. - Weight::from_parts(45_099_000, 0) + // Minimum execution time: 44_771_000 picoseconds. + Weight::from_parts(45_635_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 34_265_000 picoseconds. - Weight::from_parts(35_083_000, 0) + // Minimum execution time: 34_225_000 picoseconds. + Weight::from_parts(35_622_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 12_189_000 picoseconds. - Weight::from_parts(12_655_000, 0) + // Minimum execution time: 12_443_000 picoseconds. + Weight::from_parts(12_944_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_910_000 picoseconds. - Weight::from_parts(17_474_000, 0) + // Minimum execution time: 17_189_000 picoseconds. + Weight::from_parts(17_922_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -102,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 45_212_000 picoseconds. - Weight::from_parts(46_320_000, 0) + // Minimum execution time: 45_925_000 picoseconds. + Weight::from_parts(47_021_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -114,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 42_500_000 picoseconds. - Weight::from_parts(43_991_000, 0) + // Minimum execution time: 43_775_000 picoseconds. + Weight::from_parts(44_955_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 15_197_000 picoseconds. - Weight::from_parts(15_749_000, 0) + // Minimum execution time: 15_358_000 picoseconds. + Weight::from_parts(15_958_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -140,11 +140,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (135 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 14_414_000 picoseconds. - Weight::from_parts(14_685_000, 0) + // Minimum execution time: 14_283_000 picoseconds. + Weight::from_parts(14_888_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 7_918 - .saturating_add(Weight::from_parts(13_095_420, 0).saturating_mul(u.into())) + // Standard Error: 8_164 + .saturating_add(Weight::from_parts(13_730_103, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -153,8 +153,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_239_000 picoseconds. - Weight::from_parts(5_617_000, 0) + // Minimum execution time: 5_167_000 picoseconds. + Weight::from_parts(5_505_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 27_587_000 picoseconds. + Weight::from_parts(28_493_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_023_000 picoseconds. + Weight::from_parts(18_694_000, 0) .saturating_add(Weight::from_parts(0, 0)) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs b/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs index 6cca9b9320a6..706653aeb769 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_balances` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-05-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -56,8 +56,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6164` - // Minimum execution time: 41_978_000 picoseconds. - Weight::from_parts(42_989_000, 0) + // Minimum execution time: 42_331_000 picoseconds. + Weight::from_parts(43_215_000, 0) .saturating_add(Weight::from_parts(0, 6164)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) @@ -70,8 +70,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6164` - // Minimum execution time: 32_250_000 picoseconds. - Weight::from_parts(33_074_000, 0) + // Minimum execution time: 32_674_000 picoseconds. + Weight::from_parts(33_564_000, 0) .saturating_add(Weight::from_parts(0, 6164)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) @@ -82,8 +82,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3577` - // Minimum execution time: 9_906_000 picoseconds. - Weight::from_parts(10_397_000, 0) + // Minimum execution time: 9_813_000 picoseconds. + Weight::from_parts(10_111_000, 0) .saturating_add(Weight::from_parts(0, 3577)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -96,8 +96,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `277` // Estimated: `3593` - // Minimum execution time: 16_298_000 picoseconds. - Weight::from_parts(17_115_000, 0) + // Minimum execution time: 16_467_000 picoseconds. + Weight::from_parts(17_088_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -110,8 +110,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `206` // Estimated: `6196` - // Minimum execution time: 43_283_000 picoseconds. - Weight::from_parts(44_033_000, 0) + // Minimum execution time: 43_846_000 picoseconds. + Weight::from_parts(45_059_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) @@ -124,8 +124,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6164` - // Minimum execution time: 40_564_000 picoseconds. - Weight::from_parts(41_597_000, 0) + // Minimum execution time: 41_260_000 picoseconds. + Weight::from_parts(42_367_000, 0) .saturating_add(Weight::from_parts(0, 6164)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) @@ -138,8 +138,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `277` // Estimated: `3593` - // Minimum execution time: 15_018_000 picoseconds. - Weight::from_parts(15_532_000, 0) + // Minimum execution time: 14_914_000 picoseconds. + Weight::from_parts(15_631_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -154,11 +154,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (256 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 14_470_000 picoseconds. - Weight::from_parts(14_828_000, 0) + // Minimum execution time: 14_630_000 picoseconds. + Weight::from_parts(14_924_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 15_515 - .saturating_add(Weight::from_parts(14_505_553, 0).saturating_mul(u.into())) + // Standard Error: 15_311 + .saturating_add(Weight::from_parts(14_920_201, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -167,8 +167,32 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_277_000 picoseconds. - Weight::from_parts(5_628_000, 0) + // Minimum execution time: 5_193_000 picoseconds. + Weight::from_parts(5_403_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `103` + // Estimated: `3577` + // Minimum execution time: 27_002_000 picoseconds. + Weight::from_parts(27_785_000, 0) + .saturating_add(Weight::from_parts(0, 3577)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `103` + // Estimated: `3577` + // Minimum execution time: 17_533_000 picoseconds. + Weight::from_parts(18_338_000, 0) + .saturating_add(Weight::from_parts(0, 3577)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/polkadot/runtime/westend/src/weights/pallet_balances.rs b/polkadot/runtime/westend/src/weights/pallet_balances.rs index 25626e940209..5e91f31920ca 100644 --- a/polkadot/runtime/westend/src/weights/pallet_balances.rs +++ b/polkadot/runtime/westend/src/weights/pallet_balances.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_balances` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 43_680_000 picoseconds. - Weight::from_parts(45_012_000, 0) + // Minimum execution time: 43_248_000 picoseconds. + Weight::from_parts(43_872_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 34_038_000 picoseconds. - Weight::from_parts(35_771_000, 0) + // Minimum execution time: 33_990_000 picoseconds. + Weight::from_parts(34_693_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 12_609_000 picoseconds. - Weight::from_parts(13_142_000, 0) + // Minimum execution time: 12_681_000 picoseconds. + Weight::from_parts(13_183_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_533_000 picoseconds. - Weight::from_parts(18_061_000, 0) + // Minimum execution time: 17_474_000 picoseconds. + Weight::from_parts(18_063_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -102,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 45_278_000 picoseconds. - Weight::from_parts(46_670_000, 0) + // Minimum execution time: 45_699_000 picoseconds. + Weight::from_parts(46_099_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -114,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 43_125_000 picoseconds. - Weight::from_parts(43_925_000, 0) + // Minimum execution time: 42_453_000 picoseconds. + Weight::from_parts(43_133_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 15_580_000 picoseconds. - Weight::from_parts(16_023_000, 0) + // Minimum execution time: 15_066_000 picoseconds. + Weight::from_parts(15_605_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -139,11 +139,11 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 14_868_000 picoseconds. - Weight::from_parts(15_130_000, 0) + // Minimum execution time: 14_180_000 picoseconds. + Weight::from_parts(14_598_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 10_719 - .saturating_add(Weight::from_parts(13_394_926, 0).saturating_mul(u.into())) + // Standard Error: 13_221 + .saturating_add(Weight::from_parts(13_422_901, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -152,8 +152,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_174_000 picoseconds. - Weight::from_parts(5_457_000, 0) + // Minimum execution time: 5_130_000 picoseconds. + Weight::from_parts(5_257_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 27_328_000 picoseconds. + Weight::from_parts(27_785_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 17_797_000 picoseconds. + Weight::from_parts(18_103_000, 0) .saturating_add(Weight::from_parts(0, 0)) } } diff --git a/polkadot/xcm/xcm-builder/src/fungible_adapter.rs b/polkadot/xcm/xcm-builder/src/fungible_adapter.rs index 21c828922b33..45a0e2bdca28 100644 --- a/polkadot/xcm/xcm-builder/src/fungible_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/fungible_adapter.rs @@ -19,7 +19,11 @@ use super::MintLocation; use frame_support::traits::{ tokens::{ - fungible, Fortitude::Polite, Precision::Exact, Preservation::Preserve, Provenance::Minted, + fungible, + Fortitude::Polite, + Precision::Exact, + Preservation::{Expendable, Preserve}, + Provenance::Minted, }, Get, }; @@ -100,7 +104,7 @@ impl< } fn reduce_checked(checking_account: AccountId, amount: Fungible::Balance) { - let ok = Fungible::burn_from(&checking_account, amount, Exact, Polite).is_ok(); + let ok = Fungible::burn_from(&checking_account, amount, Expendable, Exact, Polite).is_ok(); debug_assert!(ok, "`can_reduce_checked` must have returned `true` immediately prior; qed"); } } @@ -210,7 +214,7 @@ impl< let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?; let who = AccountIdConverter::convert_location(who) .ok_or(MatchError::AccountIdConversionFailed)?; - Fungible::burn_from(&who, amount, Exact, Polite) + Fungible::burn_from(&who, amount, Expendable, Exact, Polite) .map_err(|error| XcmError::FailedToTransactAsset(error.into()))?; Ok(what.clone().into()) } diff --git a/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs b/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs index b4c418ebf1c9..88bbf01d9e1f 100644 --- a/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs @@ -18,7 +18,11 @@ use frame_support::traits::{ tokens::{ - fungibles, Fortitude::Polite, Precision::Exact, Preservation::Preserve, Provenance::Minted, + fungibles, + Fortitude::Polite, + Precision::Exact, + Preservation::{Expendable, Preserve}, + Provenance::Minted, }, Contains, Get, }; @@ -176,7 +180,8 @@ impl< } fn reduce_checked(asset_id: Assets::AssetId, amount: Assets::Balance) { let checking_account = CheckingAccount::get(); - let ok = Assets::burn_from(asset_id, &checking_account, amount, Exact, Polite).is_ok(); + let ok = Assets::burn_from(asset_id, &checking_account, amount, Expendable, Exact, Polite) + .is_ok(); debug_assert!(ok, "`can_reduce_checked` must have returned `true` immediately prior; qed"); } } @@ -295,7 +300,7 @@ impl< let (asset_id, amount) = Matcher::matches_fungibles(what)?; let who = AccountIdConverter::convert_location(who) .ok_or(MatchError::AccountIdConversionFailed)?; - Assets::burn_from(asset_id, &who, amount, Exact, Polite) + Assets::burn_from(asset_id, &who, amount, Expendable, Exact, Polite) .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; Ok(what.clone().into()) } diff --git a/prdoc/pr_3964.prdoc b/prdoc/pr_3964.prdoc new file mode 100644 index 000000000000..adf0d95b36a4 --- /dev/null +++ b/prdoc/pr_3964.prdoc @@ -0,0 +1,16 @@ +title: Burn extrinsic call and `fn burn_from` `Preservation` argument + +doc: + - audience: Runtime Dev + description: | + pallet-balances extrinsic calls has been expanded with `burn` call. + An argument flag is allowed to specify whether the account should be kept alive or not. + This in turn required a change to the fungible's `pub trait Mutate` `burn_from` function which now + also accepts `Preservation` as an argument. + In order to keep the behavior same as before, developers should simply specify `Preservation::Expandable`. + +crates: + - name: frame-support + bump: major + - name: pallet-balances + bump: minor diff --git a/substrate/frame/asset-conversion/ops/src/lib.rs b/substrate/frame/asset-conversion/ops/src/lib.rs index 6cc7bfa2e996..a655a9cb4452 100644 --- a/substrate/frame/asset-conversion/ops/src/lib.rs +++ b/substrate/frame/asset-conversion/ops/src/lib.rs @@ -248,6 +248,7 @@ pub mod pallet { T::DepositAsset::burn_from( &depositor, deposit + deposit_asset_ed, + Preservation::Expendable, Precision::Exact, Fortitude::Force, )?; @@ -260,6 +261,7 @@ pub mod pallet { T::DepositAsset::burn_from( &depositor, deposit + deposit_asset_ed, + Preservation::Expendable, Precision::Exact, Fortitude::Force, )?; @@ -272,6 +274,7 @@ pub mod pallet { T::DepositAsset::burn_from( &depositor, deposit + deposit_asset_ed, + Preservation::Expendable, Precision::Exact, Fortitude::Force, )?; diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index bb6e70a7fe93..62acb693efb1 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -596,7 +596,14 @@ pub mod pallet { ); // burn the provided lp token amount that includes the fee - T::PoolAssets::burn_from(pool.lp_token.clone(), &sender, lp_token_burn, Exact, Polite)?; + T::PoolAssets::burn_from( + pool.lp_token.clone(), + &sender, + lp_token_burn, + Expendable, + Exact, + Polite, + )?; T::Assets::transfer(*asset1, &pool_account, &withdraw_to, amount1, Expendable)?; T::Assets::transfer(*asset2, &pool_account, &withdraw_to, amount2, Expendable)?; diff --git a/substrate/frame/balances/src/benchmarking.rs b/substrate/frame/balances/src/benchmarking.rs index 14f0ede5e0f2..e4229f2d7f0f 100644 --- a/substrate/frame/balances/src/benchmarking.rs +++ b/substrate/frame/balances/src/benchmarking.rs @@ -297,6 +297,44 @@ mod benchmarks { assert_eq!(Balances::::total_issuance(), ti + delta); } + /// Benchmark `burn` extrinsic with the worst possible condition - burn kills the account. + #[benchmark] + fn burn_allow_death() { + let existential_deposit = T::ExistentialDeposit::get(); + let caller = whitelisted_caller(); + + // Give some multiple of the existential deposit + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&caller, balance); + + // Burn enough to kill the account. + let burn_amount = balance - existential_deposit + 1u32.into(); + + #[extrinsic_call] + burn(RawOrigin::Signed(caller.clone()), burn_amount, false); + + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); + } + + // Benchmark `burn` extrinsic with the case where account is kept alive. + #[benchmark] + fn burn_keep_alive() { + let existential_deposit = T::ExistentialDeposit::get(); + let caller = whitelisted_caller(); + + // Give some multiple of the existential deposit + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&caller, balance); + + // Burn minimum possible amount which should not kill the account. + let burn_amount = 1u32.into(); + + #[extrinsic_call] + burn(RawOrigin::Signed(caller.clone()), burn_amount, true); + + assert_eq!(Balances::::free_balance(&caller), balance - burn_amount); + } + impl_benchmark_test_suite! { Balances, crate::tests::ExtBuilder::default().build(), diff --git a/substrate/frame/balances/src/lib.rs b/substrate/frame/balances/src/lib.rs index bd811955d63c..8d904d3d21b8 100644 --- a/substrate/frame/balances/src/lib.rs +++ b/substrate/frame/balances/src/lib.rs @@ -767,6 +767,32 @@ pub mod pallet { Ok(()) } + + /// Burn the specified liquid free balance from the origin account. + /// + /// If the origin's account ends up below the existential deposit as a result + /// of the burn and `keep_alive` is false, the account will be reaped. + /// + /// Unlike sending funds to a _burn_ address, which merely makes the funds inaccessible, + /// this `burn` operation will reduce total issuance by the amount _burned_. + #[pallet::call_index(10)] + #[pallet::weight(if *keep_alive {T::WeightInfo::burn_allow_death() } else {T::WeightInfo::burn_keep_alive()})] + pub fn burn( + origin: OriginFor, + #[pallet::compact] value: T::Balance, + keep_alive: bool, + ) -> DispatchResult { + let source = ensure_signed(origin)?; + let preservation = if keep_alive { Preserve } else { Expendable }; + >::burn_from( + &source, + value, + preservation, + Precision::Exact, + Polite, + )?; + Ok(()) + } } impl, I: 'static> Pallet { diff --git a/substrate/frame/balances/src/tests/currency_tests.rs b/substrate/frame/balances/src/tests/currency_tests.rs index 450b1a84aa87..9ad4aca64406 100644 --- a/substrate/frame/balances/src/tests/currency_tests.rs +++ b/substrate/frame/balances/src/tests/currency_tests.rs @@ -701,7 +701,7 @@ fn account_removal_on_free_too_low() { fn burn_must_work() { ExtBuilder::default().monied(true).build_and_execute_with(|| { let init_total_issuance = Balances::total_issuance(); - let imbalance = Balances::burn(10); + let imbalance = >::burn(10); assert_eq!(Balances::total_issuance(), init_total_issuance - 10); drop(imbalance); assert_eq!(Balances::total_issuance(), init_total_issuance); diff --git a/substrate/frame/balances/src/tests/dispatchable_tests.rs b/substrate/frame/balances/src/tests/dispatchable_tests.rs index 4be68f61693b..4bc96f6b43d9 100644 --- a/substrate/frame/balances/src/tests/dispatchable_tests.rs +++ b/substrate/frame/balances/src/tests/dispatchable_tests.rs @@ -335,3 +335,47 @@ fn force_adjust_total_issuance_rejects_more_than_inactive() { assert_eq!(Balances::active_issuance(), 10); }); } + +#[test] +fn burn_works() { + ExtBuilder::default().build().execute_with(|| { + // Prepare account with initial balance + let (account, init_balance) = (1, 37); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account, init_balance)); + let init_issuance = Balances::total_issuance(); + let (keep_alive, allow_death) = (true, false); + + // 1. Cannot burn more than what's available + assert_noop!( + Balances::burn(Some(account).into(), init_balance + 1, allow_death), + TokenError::FundsUnavailable, + ); + + // 2. Burn some funds, without reaping the account + let burn_amount_1 = 1; + assert_ok!(Balances::burn(Some(account).into(), burn_amount_1, allow_death)); + System::assert_last_event(RuntimeEvent::Balances(Event::Burned { + who: account, + amount: burn_amount_1, + })); + assert_eq!(Balances::total_issuance(), init_issuance - burn_amount_1); + assert_eq!(Balances::total_balance(&account), init_balance - burn_amount_1); + + // 3. Cannot burn funds below existential deposit if `keep_alive` is `true` + let burn_amount_2 = + init_balance - burn_amount_1 - ::ExistentialDeposit::get() + 1; + assert_noop!( + Balances::burn(Some(account).into(), init_balance + 1, keep_alive), + TokenError::FundsUnavailable, + ); + + // 4. Burn some more funds, this time reaping the account + assert_ok!(Balances::burn(Some(account).into(), burn_amount_2, allow_death)); + System::assert_last_event(RuntimeEvent::Balances(Event::Burned { + who: account, + amount: burn_amount_2, + })); + assert_eq!(Balances::total_issuance(), init_issuance - burn_amount_1 - burn_amount_2); + assert!(Balances::total_balance(&account).is_zero()); + }); +} diff --git a/substrate/frame/balances/src/weights.rs b/substrate/frame/balances/src/weights.rs index f99fc4510761..e82c97160efc 100644 --- a/substrate/frame/balances/src/weights.rs +++ b/substrate/frame/balances/src/weights.rs @@ -18,27 +18,25 @@ //! Autogenerated weights for `pallet_balances` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-04-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-anb7yjbi-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/production/substrate-node +// target/production/substrate-node // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_balances -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --output=./substrate/frame/balances/src/weights.rs +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=dev // --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/balances/src/weights.rs // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -60,6 +58,8 @@ pub trait WeightInfo { fn force_unreserve() -> Weight; fn upgrade_accounts(u: u32, ) -> Weight; fn force_adjust_total_issuance() -> Weight; + fn burn_allow_death() -> Weight; + fn burn_keep_alive() -> Weight; } /// Weights for `pallet_balances` using the Substrate node and recommended hardware. @@ -71,8 +71,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 50_062_000 picoseconds. - Weight::from_parts(51_214_000, 3593) + // Minimum execution time: 47_552_000 picoseconds. + Weight::from_parts(48_363_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -82,8 +82,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 39_700_000 picoseconds. - Weight::from_parts(40_754_000, 3593) + // Minimum execution time: 37_565_000 picoseconds. + Weight::from_parts(38_159_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -93,8 +93,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 14_939_000 picoseconds. - Weight::from_parts(15_302_000, 3593) + // Minimum execution time: 14_147_000 picoseconds. + Weight::from_parts(14_687_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -104,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 20_516_000 picoseconds. - Weight::from_parts(21_179_000, 3593) + // Minimum execution time: 19_188_000 picoseconds. + Weight::from_parts(19_929_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -115,8 +115,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 51_281_000 picoseconds. - Weight::from_parts(52_145_000, 6196) + // Minimum execution time: 48_903_000 picoseconds. + Weight::from_parts(49_944_000, 6196) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -126,8 +126,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 48_988_000 picoseconds. - Weight::from_parts(49_935_000, 3593) + // Minimum execution time: 46_573_000 picoseconds. + Weight::from_parts(47_385_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -137,8 +137,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_594_000 picoseconds. - Weight::from_parts(18_232_000, 3593) + // Minimum execution time: 16_750_000 picoseconds. + Weight::from_parts(17_233_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -149,10 +149,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + u * (135 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 16_701_000 picoseconds. - Weight::from_parts(16_897_000, 990) - // Standard Error: 11_684 - .saturating_add(Weight::from_parts(14_375_201, 0).saturating_mul(u.into())) + // Minimum execution time: 16_333_000 picoseconds. + Weight::from_parts(16_588_000, 990) + // Standard Error: 12_254 + .saturating_add(Weight::from_parts(13_973_659, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -161,8 +161,22 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_692_000 picoseconds. - Weight::from_parts(7_140_000, 0) + // Minimum execution time: 6_265_000 picoseconds. + Weight::from_parts(6_594_000, 0) + } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 30_151_000 picoseconds. + Weight::from_parts(30_968_000, 0) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 20_055_000 picoseconds. + Weight::from_parts(20_711_000, 0) } } @@ -174,8 +188,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 50_062_000 picoseconds. - Weight::from_parts(51_214_000, 3593) + // Minimum execution time: 47_552_000 picoseconds. + Weight::from_parts(48_363_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -185,8 +199,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 39_700_000 picoseconds. - Weight::from_parts(40_754_000, 3593) + // Minimum execution time: 37_565_000 picoseconds. + Weight::from_parts(38_159_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -196,8 +210,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 14_939_000 picoseconds. - Weight::from_parts(15_302_000, 3593) + // Minimum execution time: 14_147_000 picoseconds. + Weight::from_parts(14_687_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -207,8 +221,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 20_516_000 picoseconds. - Weight::from_parts(21_179_000, 3593) + // Minimum execution time: 19_188_000 picoseconds. + Weight::from_parts(19_929_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -218,8 +232,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 51_281_000 picoseconds. - Weight::from_parts(52_145_000, 6196) + // Minimum execution time: 48_903_000 picoseconds. + Weight::from_parts(49_944_000, 6196) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -229,8 +243,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 48_988_000 picoseconds. - Weight::from_parts(49_935_000, 3593) + // Minimum execution time: 46_573_000 picoseconds. + Weight::from_parts(47_385_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -240,8 +254,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 17_594_000 picoseconds. - Weight::from_parts(18_232_000, 3593) + // Minimum execution time: 16_750_000 picoseconds. + Weight::from_parts(17_233_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -252,10 +266,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + u * (135 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 16_701_000 picoseconds. - Weight::from_parts(16_897_000, 990) - // Standard Error: 11_684 - .saturating_add(Weight::from_parts(14_375_201, 0).saturating_mul(u.into())) + // Minimum execution time: 16_333_000 picoseconds. + Weight::from_parts(16_588_000, 990) + // Standard Error: 12_254 + .saturating_add(Weight::from_parts(13_973_659, 0).saturating_mul(u.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -264,7 +278,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_692_000 picoseconds. - Weight::from_parts(7_140_000, 0) + // Minimum execution time: 6_265_000 picoseconds. + Weight::from_parts(6_594_000, 0) + } + fn burn_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 30_151_000 picoseconds. + Weight::from_parts(30_968_000, 0) + } + fn burn_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 20_055_000 picoseconds. + Weight::from_parts(20_711_000, 0) } } diff --git a/substrate/frame/nft-fractionalization/src/lib.rs b/substrate/frame/nft-fractionalization/src/lib.rs index e97d3802fd20..cb269f464c48 100644 --- a/substrate/frame/nft-fractionalization/src/lib.rs +++ b/substrate/frame/nft-fractionalization/src/lib.rs @@ -75,7 +75,7 @@ pub mod pallet { AssetId, Balance as AssetBalance, Fortitude::Polite, Precision::{BestEffort, Exact}, - Preservation::Preserve, + Preservation::{Expendable, Preserve}, }, }, BoundedVec, PalletId, @@ -374,7 +374,7 @@ pub mod pallet { account: &T::AccountId, amount: AssetBalanceOf, ) -> DispatchResult { - T::Assets::burn_from(asset_id.clone(), account, amount, Exact, Polite)?; + T::Assets::burn_from(asset_id.clone(), account, amount, Expendable, Exact, Polite)?; T::Assets::start_destroy(asset_id, None) } diff --git a/substrate/frame/nis/src/lib.rs b/substrate/frame/nis/src/lib.rs index 63287f6a1802..f38755836fb9 100644 --- a/substrate/frame/nis/src/lib.rs +++ b/substrate/frame/nis/src/lib.rs @@ -808,7 +808,7 @@ pub mod pallet { ensure!(summary.thawed <= throttle, Error::::Throttled); let cp_amount = T::CounterpartAmount::convert(receipt.proportion); - T::Counterpart::burn_from(&who, cp_amount, Exact, Polite)?; + T::Counterpart::burn_from(&who, cp_amount, Expendable, Exact, Polite)?; // Multiply the proportion it is by the total issued. let our_account = Self::account_id(); @@ -897,6 +897,7 @@ pub mod pallet { T::Counterpart::burn_from( &who, T::CounterpartAmount::convert(receipt.proportion), + Expendable, Exact, Polite, )?; diff --git a/substrate/frame/nis/src/tests.rs b/substrate/frame/nis/src/tests.rs index 01724999ae7e..a17aaf421827 100644 --- a/substrate/frame/nis/src/tests.rs +++ b/substrate/frame/nis/src/tests.rs @@ -24,7 +24,7 @@ use frame_support::{ traits::{ fungible::{hold::Inspect as InspectHold, Inspect as FunInspect, Mutate as FunMutate}, nonfungible::{Inspect, Transfer}, - tokens::{Fortitude::Force, Precision::Exact}, + tokens::{Fortitude::Force, Precision::Exact, Preservation::Expendable}, }, }; use sp_arithmetic::Perquintill; @@ -646,9 +646,9 @@ fn thaw_when_issuance_lower_works() { enlarge(100, 1); // Everybody else's balances goes down by 25% - assert_ok!(Balances::burn_from(&2, 25, Exact, Force)); - assert_ok!(Balances::burn_from(&3, 25, Exact, Force)); - assert_ok!(Balances::burn_from(&4, 25, Exact, Force)); + assert_ok!(Balances::burn_from(&2, 25, Expendable, Exact, Force)); + assert_ok!(Balances::burn_from(&3, 25, Expendable, Exact, Force)); + assert_ok!(Balances::burn_from(&4, 25, Expendable, Exact, Force)); run_to_block(4); assert_ok!(Nis::thaw_private(signed(1), 0, None)); diff --git a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/inspect_mutate.rs b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/inspect_mutate.rs index 732742cca9b5..fb52eb7037db 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/inspect_mutate.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/inspect_mutate.rs @@ -166,9 +166,10 @@ where // Test: Burn an exact amount from the account let amount_to_burn = T::Balance::from(5); + let preservation = Preservation::Expendable; let precision = Precision::Exact; let force = Fortitude::Polite; - T::burn_from(&account, amount_to_burn, precision, force).unwrap(); + T::burn_from(&account, amount_to_burn, preservation, precision, force).unwrap(); // Verify: The balance and total issuance should be reduced by the burned amount assert_eq!(T::balance(&account), initial_balance - amount_to_burn); @@ -209,10 +210,11 @@ where // Test: Burn a best effort amount from the account that is greater than the reducible balance let amount_to_burn = reducible_balance + 5.into(); + let preservation = Preservation::Expendable; let precision = Precision::BestEffort; assert!(amount_to_burn > reducible_balance); assert!(amount_to_burn > T::balance(&account)); - T::burn_from(&account, amount_to_burn, precision, force).unwrap(); + T::burn_from(&account, amount_to_burn, preservation, precision, force).unwrap(); // Verify: The balance and total issuance should be reduced by the reducible_balance assert_eq!(T::balance(&account), initial_balance - reducible_balance); @@ -248,9 +250,10 @@ where // Verify: Burn an amount greater than the account's balance with Exact precision returns Err let amount_to_burn = initial_balance + 10.into(); + let preservation = Preservation::Expendable; let precision = Precision::Exact; let force = Fortitude::Polite; - T::burn_from(&account, amount_to_burn, precision, force).unwrap_err(); + T::burn_from(&account, amount_to_burn, preservation, precision, force).unwrap_err(); // Verify: The balance and total issuance should remain unchanged assert_eq!(T::balance(&account), initial_balance); diff --git a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/mutate.rs b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/mutate.rs index 95b5256bb491..b17ce6f518c0 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/mutate.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/mutate.rs @@ -137,9 +137,10 @@ where // Test: Burn an exact amount from the account let amount_to_burn = T::Balance::from(5); + let preservation = Preservation::Expendable; let precision = Precision::Exact; let force = Fortitude::Polite; - T::burn_from(&account, amount_to_burn, precision, force).unwrap(); + T::burn_from(&account, amount_to_burn, preservation, precision, force).unwrap(); // Verify: The balance and total issuance should be reduced by the burned amount assert_eq!(T::balance(&account), initial_balance - amount_to_burn); @@ -174,10 +175,11 @@ where // Test: Burn a best effort amount from the account that is greater than the reducible // balance let amount_to_burn = reducible_balance + 5.into(); + let preservation = Preservation::Expendable; let precision = Precision::BestEffort; assert!(amount_to_burn > reducible_balance); assert!(amount_to_burn > T::balance(&account)); - T::burn_from(&account, amount_to_burn, precision, force).unwrap(); + T::burn_from(&account, amount_to_burn, preservation, precision, force).unwrap(); // Verify: The balance and total issuance should be reduced by the reducible_balance assert_eq!(T::balance(&account), initial_balance - reducible_balance); @@ -207,9 +209,10 @@ where // Verify: Burn an amount greater than the account's balance with Exact precision returns // Err let amount_to_burn = initial_balance + 10.into(); + let preservation = Preservation::Expendable; let precision = Precision::Exact; let force = Fortitude::Polite; - T::burn_from(&account, amount_to_burn, precision, force).unwrap_err(); + T::burn_from(&account, amount_to_burn, preservation, precision, force).unwrap_err(); // Verify: The balance and total issuance should remain unchanged assert_eq!(T::balance(&account), initial_balance); diff --git a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs index 5374cc52bab7..2aa53d622dbf 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs @@ -235,10 +235,18 @@ impl< fn burn_from( who: &AccountId, amount: Self::Balance, + preservation: Preservation, precision: Precision, force: Fortitude, ) -> Result { - >::burn_from(A::get(), who, amount, precision, force) + >::burn_from( + A::get(), + who, + amount, + preservation, + precision, + force, + ) } fn shelve(who: &AccountId, amount: Self::Balance) -> Result { >::shelve(A::get(), who, amount) diff --git a/substrate/frame/support/src/traits/tokens/fungible/regular.rs b/substrate/frame/support/src/traits/tokens/fungible/regular.rs index 4ed31dcf9fb1..c46614be4734 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/regular.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/regular.rs @@ -254,19 +254,23 @@ where Ok(actual) } - /// Decrease the balance of `who` by at least `amount`, possibly slightly more in the case of - /// minimum-balance requirements, burning the tokens. If that isn't possible then an `Err` is - /// returned and nothing is changed. If successful, the amount of tokens reduced is returned. + /// Attempt to decrease the balance of `who`, burning the tokens. + /// The actual amount burned is derived from the `amount`, `preservation`, `precision` and + /// `force`, and might end up being more, less or equal to the `amount` specified. + /// + /// If the burn isn't possible then an `Err` is returned and nothing is changed. + /// If successful, the amount of tokens reduced is returned. fn burn_from( who: &AccountId, amount: Self::Balance, + preservation: Preservation, precision: Precision, force: Fortitude, ) -> Result { - let actual = Self::reducible_balance(who, Expendable, force).min(amount); + let actual = Self::reducible_balance(who, preservation, force).min(amount); ensure!(actual == amount || precision == BestEffort, TokenError::FundsUnavailable); Self::total_issuance().checked_sub(&actual).ok_or(ArithmeticError::Overflow)?; - let actual = Self::decrease_balance(who, actual, BestEffort, Expendable, force)?; + let actual = Self::decrease_balance(who, actual, BestEffort, preservation, force)?; Self::set_total_issuance(Self::total_issuance().saturating_sub(actual)); Self::done_burn_from(who, actual); Ok(actual) @@ -342,7 +346,8 @@ where fn set_balance(who: &AccountId, amount: Self::Balance) -> Self::Balance { let b = Self::balance(who); if b > amount { - Self::burn_from(who, b - amount, BestEffort, Force).map(|d| b.saturating_sub(d)) + Self::burn_from(who, b - amount, Expendable, BestEffort, Force) + .map(|d| b.saturating_sub(d)) } else { Self::mint_into(who, amount - b).map(|d| b.saturating_add(d)) } diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs index db44b2f43a4e..63791b052237 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs @@ -442,14 +442,26 @@ impl< asset: Self::AssetId, who: &AccountId, amount: Self::Balance, + preservation: Preservation, precision: Precision, force: Fortitude, ) -> Result { match Criterion::convert(asset) { - Left(()) => - >::burn_from(who, amount, precision, force), - Right(a) => - >::burn_from(a, who, amount, precision, force), + Left(()) => >::burn_from( + who, + amount, + preservation, + precision, + force, + ), + Right(a) => >::burn_from( + a, + who, + amount, + preservation, + precision, + force, + ), } } fn shelve( diff --git a/substrate/frame/support/src/traits/tokens/fungibles/regular.rs b/substrate/frame/support/src/traits/tokens/fungibles/regular.rs index b30e0ae3a2a3..946c4756cff6 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/regular.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/regular.rs @@ -283,16 +283,17 @@ where asset: Self::AssetId, who: &AccountId, amount: Self::Balance, + preservation: Preservation, precision: Precision, force: Fortitude, ) -> Result { - let actual = Self::reducible_balance(asset.clone(), who, Expendable, force).min(amount); + let actual = Self::reducible_balance(asset.clone(), who, preservation, force).min(amount); ensure!(actual == amount || precision == BestEffort, TokenError::FundsUnavailable); Self::total_issuance(asset.clone()) .checked_sub(&actual) .ok_or(ArithmeticError::Overflow)?; let actual = - Self::decrease_balance(asset.clone(), who, actual, BestEffort, Expendable, force)?; + Self::decrease_balance(asset.clone(), who, actual, BestEffort, preservation, force)?; Self::set_total_issuance( asset.clone(), Self::total_issuance(asset.clone()).saturating_sub(actual), @@ -392,7 +393,8 @@ where fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> Self::Balance { let b = Self::balance(asset.clone(), who); if b > amount { - Self::burn_from(asset, who, b - amount, BestEffort, Force).map(|d| b.saturating_sub(d)) + Self::burn_from(asset, who, b - amount, Expendable, BestEffort, Force) + .map(|d| b.saturating_sub(d)) } else { Self::mint_into(asset, who, amount - b).map(|d| b.saturating_add(d)) } diff --git a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs index 2c7d4bab7baa..f4259a78f0a2 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs @@ -389,14 +389,27 @@ impl< asset: Self::AssetId, who: &AccountId, amount: Self::Balance, + preservation: Preservation, precision: Precision, force: Fortitude, ) -> Result { match Criterion::convert(asset) { - Left(a) => - >::burn_from(a, who, amount, precision, force), - Right(a) => - >::burn_from(a, who, amount, precision, force), + Left(a) => >::burn_from( + a, + who, + amount, + preservation, + precision, + force, + ), + Right(a) => >::burn_from( + a, + who, + amount, + preservation, + precision, + force, + ), } } fn shelve( From 17b56fae2d976a3df87f34076875de8c26da0355 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 8 May 2024 11:26:57 +0300 Subject: [PATCH 03/10] Bridge: check bridge GRANDPA pallet call limits from signed extension (#4385) silent, because it'll be deployed with the https://github.com/paritytech/polkadot-sdk/pull/4102, where this code has been introduced I've planned originally to avoid doing that check in the runtime code, because it **may be** checked offchain. But actually, the check is quite cheap and we could do that onchain too. --- bridges/modules/grandpa/src/call_ext.rs | 67 ++++++++++++++++++++++++- bridges/modules/grandpa/src/lib.rs | 3 ++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/bridges/modules/grandpa/src/call_ext.rs b/bridges/modules/grandpa/src/call_ext.rs index 6fa62ec0cff4..98fbeaa30bba 100644 --- a/bridges/modules/grandpa/src/call_ext.rs +++ b/bridges/modules/grandpa/src/call_ext.rs @@ -148,8 +148,13 @@ impl, I: 'static> SubmitFinalityProofHelper { } } - // we do not check whether the header matches free submission criteria here - it is the - // relayer responsibility to check that + // let's also check whether the header submission fits the hardcoded limits. A normal + // relayer would check that before submitting a transaction (since limits are constants + // and do not depend on a volatile runtime state), but the ckeck itself is cheap, so + // let's do it here too + if !call_info.fits_limits() { + return Err(Error::::HeaderOverflowLimits); + } Ok(improved_by) } @@ -468,6 +473,64 @@ mod tests { }) } + #[test] + fn extension_rejects_new_header_if_it_overflow_size_limits() { + run_test(|| { + let mut large_finality_target = test_header(10 + FreeHeadersInterval::get() as u64); + large_finality_target + .digest_mut() + .push(DigestItem::Other(vec![42u8; 1024 * 1024])); + let justification_params = JustificationGeneratorParams { + header: large_finality_target.clone(), + ..Default::default() + }; + let large_justification = make_justification_for_header(justification_params); + + let bridge_grandpa_call = crate::Call::::submit_finality_proof_ex { + finality_target: Box::new(large_finality_target), + justification: large_justification, + current_set_id: 0, + is_free_execution_expected: true, + }; + sync_to_header_10(); + + // if overflow size limits => Err + FreeHeadersRemaining::::put(2); + assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa( + bridge_grandpa_call.clone(), + ),) + .is_err()); + }) + } + + #[test] + fn extension_rejects_new_header_if_it_overflow_weight_limits() { + run_test(|| { + let finality_target = test_header(10 + FreeHeadersInterval::get() as u64); + let justification_params = JustificationGeneratorParams { + header: finality_target.clone(), + ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY, + ..Default::default() + }; + let justification = make_justification_for_header(justification_params); + + let bridge_grandpa_call = crate::Call::::submit_finality_proof_ex { + finality_target: Box::new(finality_target), + justification, + current_set_id: 0, + is_free_execution_expected: true, + }; + sync_to_header_10(); + + // if overflow weight limits => Err + FreeHeadersRemaining::::put(2); + assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa( + bridge_grandpa_call.clone(), + ),) + .is_err()); + }) + } + #[test] fn extension_rejects_new_header_if_free_execution_is_requested_and_improved_by_is_below_expected( ) { diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index efcbfb1654b3..a927882aaaa2 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -504,6 +504,9 @@ pub mod pallet { /// The submitter wanted free execution, but the difference between best known and /// bundled header numbers is below the `FreeHeadersInterval`. BelowFreeHeaderInterval, + /// The header (and its finality) submission overflows hardcoded chain limits: size + /// and/or weight are larger than expected. + HeaderOverflowLimits, } /// Called when new free header is imported. From 37b1544b51aeba183350d4c8d76987c32e6c9ca7 Mon Sep 17 00:00:00 2001 From: gupnik Date: Wed, 8 May 2024 17:20:23 +0530 Subject: [PATCH 04/10] Adds benchmarking and try-runtime support in frame crate (#4406) --- Cargo.lock | 4 ++++ prdoc/pr_4406.prdoc | 10 ++++++++++ substrate/frame/Cargo.toml | 28 ++++++++++++++++++++++++++++ substrate/frame/src/lib.rs | 10 ++++++++++ 4 files changed, 52 insertions(+) create mode 100644 prdoc/pr_4406.prdoc diff --git a/Cargo.lock b/Cargo.lock index 0dda623c14ab..5ee267d691aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13888,10 +13888,13 @@ name = "polkadot-sdk-frame" version = "0.1.0" dependencies = [ "docify", + "frame-benchmarking", "frame-executive", "frame-support", "frame-system", + "frame-system-benchmarking", "frame-system-rpc-runtime-api", + "frame-try-runtime", "log", "pallet-examples", "parity-scale-codec", @@ -13908,6 +13911,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", ] diff --git a/prdoc/pr_4406.prdoc b/prdoc/pr_4406.prdoc new file mode 100644 index 000000000000..9372b532512b --- /dev/null +++ b/prdoc/pr_4406.prdoc @@ -0,0 +1,10 @@ +title: Adds benchmarking and try-runtime support in `polkadot-sdk-frame` crate + +doc: + - audience: Runtime Dev + description: | + Adds benchmarking and try-runtime support in `polkadot-sdk-frame` crate + +crates: + - name: polkadot-sdk-frame + bump: minor diff --git a/substrate/frame/Cargo.toml b/substrate/frame/Cargo.toml index 729df227be03..44e8d681b01c 100644 --- a/substrate/frame/Cargo.toml +++ b/substrate/frame/Cargo.toml @@ -46,10 +46,18 @@ sp-session = { default-features = false, path = "../primitives/session", optiona sp-consensus-aura = { default-features = false, path = "../primitives/consensus/aura", optional = true } sp-consensus-grandpa = { default-features = false, path = "../primitives/consensus/grandpa", optional = true } sp-inherents = { default-features = false, path = "../primitives/inherents", optional = true } +sp-storage = { default-features = false, path = "../primitives/storage", optional = true } frame-executive = { default-features = false, path = "../frame/executive", optional = true } frame-system-rpc-runtime-api = { default-features = false, path = "../frame/system/rpc/runtime-api", optional = true } +# Used for runtime benchmarking +frame-benchmarking = { default-features = false, path = "../frame/benchmarking", optional = true } +frame-system-benchmarking = { default-features = false, path = "../frame/system/benchmarking", optional = true } + +# Used for try-runtime +frame-try-runtime = { default-features = false, path = "../frame/try-runtime", optional = true } + docify = "0.2.8" log = { workspace = true } @@ -67,6 +75,7 @@ runtime = [ "sp-inherents", "sp-offchain", "sp-session", + "sp-storage", "sp-transaction-pool", "sp-version", @@ -74,10 +83,13 @@ runtime = [ "frame-system-rpc-runtime-api", ] std = [ + "frame-benchmarking?/std", "frame-executive?/std", "frame-support/std", + "frame-system-benchmarking?/std", "frame-system-rpc-runtime-api?/std", "frame-system/std", + "frame-try-runtime?/std", "log/std", "parity-scale-codec/std", "scale-info/std", @@ -93,6 +105,22 @@ std = [ "sp-runtime/std", "sp-session?/std", "sp-std/std", + "sp-storage/std", "sp-transaction-pool?/std", "sp-version?/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-examples/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index 90c446808daf..f6507cd02c71 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -363,5 +363,15 @@ pub mod deps { #[cfg(feature = "runtime")] pub use sp_offchain; #[cfg(feature = "runtime")] + pub use sp_storage; + #[cfg(feature = "runtime")] pub use sp_version; + + #[cfg(feature = "runtime-benchmarks")] + pub use frame_benchmarking; + #[cfg(feature = "runtime-benchmarks")] + pub use frame_system_benchmarking; + + #[cfg(feature = "frame-try-runtime")] + pub use frame_try_runtime; } From 6fdb522ded3813f43a539964af78d5fc6d9f1e97 Mon Sep 17 00:00:00 2001 From: Lulu Date: Wed, 8 May 2024 17:17:09 +0100 Subject: [PATCH 05/10] Add semver CI check (#4279) This checks changed files against API surface changes against what the prdoc says. It will error if the detected semver change is greater than the one listed in the prdoc. It will also error if any crates were touched but not mentioned in the prdoc. --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi --- .github/workflows/check-publish.yml | 4 +- .github/workflows/check-semver.yml | 55 +++++++++++++++++++++++ .github/workflows/claim-crates.yml | 4 +- Cargo.lock | 2 +- substrate/client/network/types/Cargo.toml | 2 +- 5 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/check-semver.yml diff --git a/.github/workflows/check-publish.yml b/.github/workflows/check-publish.yml index b16b3d4e5c5c..9b5b89e34475 100644 --- a/.github/workflows/check-publish.yml +++ b/.github/workflows/check-publish.yml @@ -20,7 +20,7 @@ jobs: cache-on-failure: true - name: install parity-publish - run: cargo install parity-publish@0.3.0 + run: cargo install parity-publish@0.5.1 - name: parity-publish check - run: parity-publish check --allow-unpublished + run: parity-publish --color always check --allow-unpublished diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml new file mode 100644 index 000000000000..f0e076e8a168 --- /dev/null +++ b/.github/workflows/check-semver.yml @@ -0,0 +1,55 @@ +name: Check semver + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - prdoc/*.prdoc + +jobs: + check-semver: + runs-on: ubuntu-latest + container: + image: docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v20240408 + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Rust Cache + uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 + with: + cache-on-failure: true + + - name: Rust compilation prerequisites + run: | + rustup default nightly-2024-03-01 + rustup target add wasm32-unknown-unknown --toolchain nightly-2024-03-01 + rustup component add rust-src --toolchain nightly-2024-03-01 + + - name: install parity-publish + run: cargo install parity-publish@0.5.1 + + - name: extra git setup + run: | + git config --global --add safe.directory '*' + git fetch --no-tags --no-recurse-submodules --depth=1 origin master + git branch old origin/master + + - name: check semver + run: | + export CARGO_TARGET_DIR=target + export RUSTFLAGS='-A warnings -A missing_docs' + if ! parity-publish --color always prdoc --since old --validate prdoc/pr_$PR.prdoc --toolchain nightly-2024-03-01 -v; then + cat < Date: Thu, 9 May 2024 09:23:59 +0200 Subject: [PATCH 06/10] rpc: add option to `whitelist ips` in rate limiting (#3701) This PR adds two new CLI options to disable rate limiting for certain ip addresses and whether to trust "proxy header". After going back in forth I decided to use ip addr instead host because we don't want rely on the host header which can be spoofed but another solution is to resolve the ip addr from the socket to host name. Example: ```bash $ polkadot --rpc-rate-limit 10 --rpc-rate-limit-whitelisted-ips 127.0.0.1/8 --rpc-rate-limit-trust-proxy-headers ``` The ip addr is read from the HTTP proxy headers `Forwarded`, `X-Forwarded-For` `X-Real-IP` if `--rpc-rate-limit-trust-proxy-headers` is enabled if that is not enabled or the headers are not found then the ip address is read from the socket. //cc @BulatSaif can you test this and give some feedback on it? --- Cargo.lock | 18 ++ cumulus/test/service/src/lib.rs | 2 + polkadot/node/test/service/src/lib.rs | 2 + prdoc/pr_3701.prdoc | 11 + .../bin/node/cli/benches/block_production.rs | 2 + .../bin/node/cli/benches/transaction_pool.rs | 2 + substrate/client/cli/src/commands/run_cmd.rs | 28 ++- substrate/client/cli/src/config.rs | 14 +- substrate/client/cli/src/runner.rs | 2 + substrate/client/rpc-servers/Cargo.toml | 14 +- substrate/client/rpc-servers/src/lib.rs | 94 +++------ substrate/client/rpc-servers/src/utils.rs | 189 ++++++++++++++++++ substrate/client/service/src/config.rs | 5 + substrate/client/service/src/lib.rs | 2 + substrate/client/service/test/src/lib.rs | 2 + 15 files changed, 318 insertions(+), 69 deletions(-) create mode 100644 prdoc/pr_3701.prdoc create mode 100644 substrate/client/rpc-servers/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 32c50ea350d2..d43088704b5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5585,6 +5585,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "forwarded-header-value" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" +dependencies = [ + "nonempty", + "thiserror", +] + [[package]] name = "fraction" version = "0.13.1" @@ -9051,6 +9061,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + [[package]] name = "nonzero_ext" version = "0.3.0" @@ -17302,10 +17318,12 @@ dependencies = [ name = "sc-rpc-server" version = "11.0.0" dependencies = [ + "forwarded-header-value", "futures", "governor", "http", "hyper", + "ip_network", "jsonrpsee", "log", "serde_json", diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index 11aa2e5b9f35..f2a612803861 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -824,6 +824,8 @@ pub fn node_config( rpc_message_buffer_capacity: Default::default(), rpc_batch_config: RpcBatchRequestConfig::Unlimited, rpc_rate_limit: None, + rpc_rate_limit_whitelisted_ips: Default::default(), + rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/polkadot/node/test/service/src/lib.rs b/polkadot/node/test/service/src/lib.rs index 87fbc7c20f31..35156a3a9372 100644 --- a/polkadot/node/test/service/src/lib.rs +++ b/polkadot/node/test/service/src/lib.rs @@ -216,6 +216,8 @@ pub fn node_config( rpc_message_buffer_capacity: Default::default(), rpc_batch_config: RpcBatchRequestConfig::Unlimited, rpc_rate_limit: None, + rpc_rate_limit_whitelisted_ips: Default::default(), + rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/prdoc/pr_3701.prdoc b/prdoc/pr_3701.prdoc new file mode 100644 index 000000000000..6f9fcc92ad30 --- /dev/null +++ b/prdoc/pr_3701.prdoc @@ -0,0 +1,11 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: add option to whitelist peers in rpc rate limiting + +doc: + - audience: Node Operator + description: | + This PR adds two new CLI options to disable rate limiting for certain ip addresses and whether to trust "proxy headers". + +crates: [ ] diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index f60610873d8c..ef7ae4fdf263 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -86,6 +86,8 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { rpc_message_buffer_capacity: Default::default(), rpc_batch_config: RpcBatchRequestConfig::Unlimited, rpc_rate_limit: None, + rpc_rate_limit_whitelisted_ips: Default::default(), + rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/substrate/bin/node/cli/benches/transaction_pool.rs b/substrate/bin/node/cli/benches/transaction_pool.rs index 1906ae697e90..c4488415b983 100644 --- a/substrate/bin/node/cli/benches/transaction_pool.rs +++ b/substrate/bin/node/cli/benches/transaction_pool.rs @@ -82,6 +82,8 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { rpc_message_buffer_capacity: Default::default(), rpc_batch_config: RpcBatchRequestConfig::Unlimited, rpc_rate_limit: None, + rpc_rate_limit_whitelisted_ips: Default::default(), + rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/substrate/client/cli/src/commands/run_cmd.rs b/substrate/client/cli/src/commands/run_cmd.rs index 221c32affd5a..c1288b502c95 100644 --- a/substrate/client/cli/src/commands/run_cmd.rs +++ b/substrate/client/cli/src/commands/run_cmd.rs @@ -30,7 +30,9 @@ use crate::{ use clap::Parser; use regex::Regex; use sc_service::{ - config::{BasePath, PrometheusConfig, RpcBatchRequestConfig, TransactionPoolOptions}, + config::{ + BasePath, IpNetwork, PrometheusConfig, RpcBatchRequestConfig, TransactionPoolOptions, + }, ChainSpec, Role, }; use sc_telemetry::TelemetryEndpoints; @@ -94,6 +96,22 @@ pub struct RunCmd { #[arg(long)] pub rpc_rate_limit: Option, + /// Disable RPC rate limiting for certain ip addresses. + /// + /// Each IP address must be in CIDR notation such as `1.2.3.4/24`. + #[arg(long, num_args = 1..)] + pub rpc_rate_limit_whitelisted_ips: Vec, + + /// Trust proxy headers for disable rate limiting. + /// + /// By default the rpc server will not trust headers such `X-Real-IP`, `X-Forwarded-For` and + /// `Forwarded` and this option will make the rpc server to trust these headers. + /// + /// For instance this may be secure if the rpc server is behind a reverse proxy and that the + /// proxy always sets these headers. + #[arg(long)] + pub rpc_rate_limit_trust_proxy_headers: bool, + /// Set the maximum RPC request payload size for both HTTP and WS in megabytes. #[arg(long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)] pub rpc_max_request_size: u32, @@ -439,6 +457,14 @@ impl CliConfiguration for RunCmd { Ok(self.rpc_rate_limit) } + fn rpc_rate_limit_whitelisted_ips(&self) -> Result> { + Ok(self.rpc_rate_limit_whitelisted_ips.clone()) + } + + fn rpc_rate_limit_trust_proxy_headers(&self) -> Result { + Ok(self.rpc_rate_limit_trust_proxy_headers) + } + fn transaction_pool(&self, is_dev: bool) -> Result { Ok(self.pool_config.transaction_pool(is_dev)) } diff --git a/substrate/client/cli/src/config.rs b/substrate/client/cli/src/config.rs index 70a4885e5eef..783c9313121f 100644 --- a/substrate/client/cli/src/config.rs +++ b/substrate/client/cli/src/config.rs @@ -26,7 +26,7 @@ use log::warn; use names::{Generator, Name}; use sc_service::{ config::{ - BasePath, Configuration, DatabaseSource, KeystoreConfig, NetworkConfiguration, + BasePath, Configuration, DatabaseSource, IpNetwork, KeystoreConfig, NetworkConfiguration, NodeKeyConfig, OffchainWorkerConfig, OutputFormat, PrometheusConfig, PruningMode, Role, RpcBatchRequestConfig, RpcMethods, TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod, @@ -349,6 +349,16 @@ pub trait CliConfiguration: Sized { Ok(None) } + /// RPC rate limit whitelisted ip addresses. + fn rpc_rate_limit_whitelisted_ips(&self) -> Result> { + Ok(vec![]) + } + + /// RPC rate limit trust proxy headers. + fn rpc_rate_limit_trust_proxy_headers(&self) -> Result { + Ok(false) + } + /// Get the prometheus configuration (`None` if disabled) /// /// By default this is `None`. @@ -523,6 +533,8 @@ pub trait CliConfiguration: Sized { rpc_message_buffer_capacity: self.rpc_buffer_capacity_per_connection()?, rpc_batch_config: self.rpc_batch_config()?, rpc_rate_limit: self.rpc_rate_limit()?, + rpc_rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips()?, + rpc_rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers()?, prometheus_config: self .prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?, telemetry_endpoints, diff --git a/substrate/client/cli/src/runner.rs b/substrate/client/cli/src/runner.rs index 4201a0f4062f..3bf276807840 100644 --- a/substrate/client/cli/src/runner.rs +++ b/substrate/client/cli/src/runner.rs @@ -273,6 +273,8 @@ mod tests { rpc_port: 9944, rpc_batch_config: sc_service::config::RpcBatchRequestConfig::Unlimited, rpc_rate_limit: None, + rpc_rate_limit_whitelisted_ips: Default::default(), + rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml index bc21b5b1582f..7837c852a1c9 100644 --- a/substrate/client/rpc-servers/Cargo.toml +++ b/substrate/client/rpc-servers/Cargo.toml @@ -16,14 +16,16 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +forwarded-header-value = "0.1.1" +futures = "0.3.30" +governor = "0.6.0" +http = "0.2.8" +hyper = "0.14.27" +ip_network = "0.4.1" jsonrpsee = { version = "0.22", features = ["server"] } log = { workspace = true, default-features = true } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["parking_lot"] } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } -tower-http = { version = "0.4.0", features = ["cors"] } tower = { version = "0.4.13", features = ["util"] } -http = "0.2.8" -hyper = "0.14.27" -futures = "0.3.30" -governor = "0.6.0" +tower-http = { version = "0.4.0", features = ["cors"] } diff --git a/substrate/client/rpc-servers/src/lib.rs b/substrate/client/rpc-servers/src/lib.rs index ad4b444c7ff4..ba1fcf5e3677 100644 --- a/substrate/client/rpc-servers/src/lib.rs +++ b/substrate/client/rpc-servers/src/lib.rs @@ -21,27 +21,28 @@ #![warn(missing_docs)] pub mod middleware; +pub mod utils; use std::{ convert::Infallible, error::Error as StdError, net::SocketAddr, num::NonZeroU32, time::Duration, }; -use http::header::HeaderValue; use hyper::{ server::conn::AddrStream, service::{make_service_fn, service_fn}, }; use jsonrpsee::{ server::{ - middleware::http::{HostFilterLayer, ProxyGetRequestLayer}, - stop_channel, ws, PingConfig, StopHandle, TowerServiceBuilder, + middleware::http::ProxyGetRequestLayer, stop_channel, ws, PingConfig, StopHandle, + TowerServiceBuilder, }, Methods, RpcModule, }; use tokio::net::TcpListener; use tower::Service; -use tower_http::cors::{AllowOrigin, CorsLayer}; +use utils::{build_rpc_api, format_cors, get_proxy_ip, host_filtering, try_into_cors}; +pub use ip_network::IpNetwork; pub use jsonrpsee::{ core::{ id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, @@ -85,6 +86,10 @@ pub struct Config<'a, M: Send + Sync + 'static> { pub batch_config: BatchRequestConfig, /// Rate limit calls per minute. pub rate_limit: Option, + /// Disable rate limit for certain ips. + pub rate_limit_whitelisted_ips: Vec, + /// Trust proxy headers for rate limiting. + pub rate_limit_trust_proxy_headers: bool, } #[derive(Debug, Clone)] @@ -117,11 +122,13 @@ where tokio_handle, rpc_api, rate_limit, + rate_limit_whitelisted_ips, + rate_limit_trust_proxy_headers, } = config; let std_listener = TcpListener::bind(addrs.as_slice()).await?.into_std()?; let local_addr = std_listener.local_addr().ok(); - let host_filter = hosts_filtering(cors.is_some(), local_addr); + let host_filter = host_filtering(cors.is_some(), local_addr); let http_middleware = tower::ServiceBuilder::new() .option_layer(host_filter) @@ -160,20 +167,39 @@ where stop_handle: stop_handle.clone(), }; - let make_service = make_service_fn(move |_conn: &AddrStream| { + let make_service = make_service_fn(move |addr: &AddrStream| { let cfg = cfg.clone(); + let rate_limit_whitelisted_ips = rate_limit_whitelisted_ips.clone(); + let ip = addr.remote_addr().ip(); async move { let cfg = cfg.clone(); + let rate_limit_whitelisted_ips = rate_limit_whitelisted_ips.clone(); Ok::<_, Infallible>(service_fn(move |req| { + let proxy_ip = + if rate_limit_trust_proxy_headers { get_proxy_ip(&req) } else { None }; + + let rate_limit_cfg = if rate_limit_whitelisted_ips + .iter() + .any(|ips| ips.contains(proxy_ip.unwrap_or(ip))) + { + log::debug!(target: "rpc", "ip={ip}, proxy_ip={:?} is trusted, disabling rate-limit", proxy_ip); + None + } else { + if !rate_limit_whitelisted_ips.is_empty() { + log::debug!(target: "rpc", "ip={ip}, proxy_ip={:?} is not trusted, rate-limit enabled", proxy_ip); + } + rate_limit + }; + let PerConnection { service_builder, metrics, tokio_handle, stop_handle, methods } = cfg.clone(); let is_websocket = ws::is_upgrade_request(&req); let transport_label = if is_websocket { "ws" } else { "http" }; - let middleware_layer = match (metrics, rate_limit) { + let middleware_layer = match (metrics, rate_limit_cfg) { (None, None) => None, (Some(metrics), None) => Some( MiddlewareLayer::new().with_metrics(Metrics::new(metrics, transport_label)), @@ -227,57 +253,3 @@ where Ok(server_handle) } - -fn hosts_filtering(enabled: bool, addr: Option) -> Option { - // If the local_addr failed, fallback to wildcard. - let port = addr.map_or("*".to_string(), |p| p.port().to_string()); - - if enabled { - // NOTE: The listening addresses are whitelisted by default. - let hosts = - [format!("localhost:{port}"), format!("127.0.0.1:{port}"), format!("[::1]:{port}")]; - Some(HostFilterLayer::new(hosts).expect("Valid hosts; qed")) - } else { - None - } -} - -fn build_rpc_api(mut rpc_api: RpcModule) -> RpcModule { - let mut available_methods = rpc_api.method_names().collect::>(); - // The "rpc_methods" is defined below and we want it to be part of the reported methods. - available_methods.push("rpc_methods"); - available_methods.sort(); - - rpc_api - .register_method("rpc_methods", move |_, _| { - serde_json::json!({ - "methods": available_methods, - }) - }) - .expect("infallible all other methods have their own address space; qed"); - - rpc_api -} - -fn try_into_cors( - maybe_cors: Option<&Vec>, -) -> Result> { - if let Some(cors) = maybe_cors { - let mut list = Vec::new(); - for origin in cors { - list.push(HeaderValue::from_str(origin)?); - } - Ok(CorsLayer::new().allow_origin(AllowOrigin::list(list))) - } else { - // allow all cors - Ok(CorsLayer::permissive()) - } -} - -fn format_cors(maybe_cors: Option<&Vec>) -> String { - if let Some(cors) = maybe_cors { - format!("{:?}", cors) - } else { - format!("{:?}", ["*"]) - } -} diff --git a/substrate/client/rpc-servers/src/utils.rs b/substrate/client/rpc-servers/src/utils.rs new file mode 100644 index 000000000000..d99b8e637d9d --- /dev/null +++ b/substrate/client/rpc-servers/src/utils.rs @@ -0,0 +1,189 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Substrate RPC server utils. + +use std::{ + error::Error as StdError, + net::{IpAddr, SocketAddr}, + str::FromStr, +}; + +use forwarded_header_value::ForwardedHeaderValue; +use hyper::{ + header::{HeaderName, HeaderValue}, + Request, +}; +use jsonrpsee::{server::middleware::http::HostFilterLayer, RpcModule}; +use tower_http::cors::{AllowOrigin, CorsLayer}; + +const X_FORWARDED_FOR: HeaderName = HeaderName::from_static("x-forwarded-for"); +const X_REAL_IP: HeaderName = HeaderName::from_static("x-real-ip"); +const FORWARDED: HeaderName = HeaderName::from_static("forwarded"); + +pub(crate) fn host_filtering(enabled: bool, addr: Option) -> Option { + // If the local_addr failed, fallback to wildcard. + let port = addr.map_or("*".to_string(), |p| p.port().to_string()); + + if enabled { + // NOTE: The listening addresses are whitelisted by default. + let hosts = + [format!("localhost:{port}"), format!("127.0.0.1:{port}"), format!("[::1]:{port}")]; + Some(HostFilterLayer::new(hosts).expect("Valid hosts; qed")) + } else { + None + } +} + +pub(crate) fn build_rpc_api(mut rpc_api: RpcModule) -> RpcModule { + let mut available_methods = rpc_api.method_names().collect::>(); + // The "rpc_methods" is defined below and we want it to be part of the reported methods. + available_methods.push("rpc_methods"); + available_methods.sort(); + + rpc_api + .register_method("rpc_methods", move |_, _| { + serde_json::json!({ + "methods": available_methods, + }) + }) + .expect("infallible all other methods have their own address space; qed"); + + rpc_api +} + +pub(crate) fn try_into_cors( + maybe_cors: Option<&Vec>, +) -> Result> { + if let Some(cors) = maybe_cors { + let mut list = Vec::new(); + for origin in cors { + list.push(HeaderValue::from_str(origin)?); + } + Ok(CorsLayer::new().allow_origin(AllowOrigin::list(list))) + } else { + // allow all cors + Ok(CorsLayer::permissive()) + } +} + +pub(crate) fn format_cors(maybe_cors: Option<&Vec>) -> String { + if let Some(cors) = maybe_cors { + format!("{:?}", cors) + } else { + format!("{:?}", ["*"]) + } +} + +/// Extracts the IP addr from the HTTP request. +/// +/// It is extracted in the following order: +/// 1. `Forwarded` header. +/// 2. `X-Forwarded-For` header. +/// 3. `X-Real-Ip`. +pub(crate) fn get_proxy_ip(req: &Request) -> Option { + if let Some(ip) = req + .headers() + .get(&FORWARDED) + .and_then(|v| v.to_str().ok()) + .and_then(|v| ForwardedHeaderValue::from_forwarded(v).ok()) + .and_then(|v| v.remotest_forwarded_for_ip()) + { + return Some(ip); + } + + if let Some(ip) = req + .headers() + .get(&X_FORWARDED_FOR) + .and_then(|v| v.to_str().ok()) + .and_then(|v| ForwardedHeaderValue::from_x_forwarded_for(v).ok()) + .and_then(|v| v.remotest_forwarded_for_ip()) + { + return Some(ip); + } + + if let Some(ip) = req + .headers() + .get(&X_REAL_IP) + .and_then(|v| v.to_str().ok()) + .and_then(|v| IpAddr::from_str(v).ok()) + { + return Some(ip); + } + + None +} + +#[cfg(test)] +mod tests { + use super::*; + use hyper::header::HeaderValue; + + fn request() -> hyper::Request { + hyper::Request::builder().body(hyper::Body::empty()).unwrap() + } + + #[test] + fn empty_works() { + let req = request(); + let host = get_proxy_ip(&req); + assert!(host.is_none()) + } + + #[test] + fn host_from_x_real_ip() { + let mut req = request(); + + req.headers_mut().insert(&X_REAL_IP, HeaderValue::from_static("127.0.0.1")); + let ip = get_proxy_ip(&req); + assert_eq!(Some(IpAddr::from_str("127.0.0.1").unwrap()), ip); + } + + #[test] + fn ip_from_forwarded_works() { + let mut req = request(); + + req.headers_mut().insert( + &FORWARDED, + HeaderValue::from_static("for=192.0.2.60;proto=http;by=203.0.113.43;host=example.com"), + ); + let ip = get_proxy_ip(&req); + assert_eq!(Some(IpAddr::from_str("192.0.2.60").unwrap()), ip); + } + + #[test] + fn ip_from_forwarded_multiple() { + let mut req = request(); + + req.headers_mut().append(&FORWARDED, HeaderValue::from_static("for=127.0.0.1")); + req.headers_mut().append(&FORWARDED, HeaderValue::from_static("for=192.0.2.60")); + req.headers_mut().append(&FORWARDED, HeaderValue::from_static("for=192.0.2.61")); + let ip = get_proxy_ip(&req); + assert_eq!(Some(IpAddr::from_str("127.0.0.1").unwrap()), ip); + } + + #[test] + fn ip_from_x_forwarded_works() { + let mut req = request(); + + req.headers_mut() + .insert(&X_FORWARDED_FOR, HeaderValue::from_static("127.0.0.1,192.0.2.60,0.0.0.1")); + let ip = get_proxy_ip(&req); + assert_eq!(Some(IpAddr::from_str("127.0.0.1").unwrap()), ip); + } +} diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs index 59e307d7f93b..187e18aa3cac 100644 --- a/substrate/client/service/src/config.rs +++ b/substrate/client/service/src/config.rs @@ -34,6 +34,7 @@ pub use sc_network::{ }, Multiaddr, }; +pub use sc_rpc_server::IpNetwork; pub use sc_telemetry::TelemetryEndpoints; pub use sc_transaction_pool::Options as TransactionPoolOptions; use sp_core::crypto::SecretString; @@ -108,6 +109,10 @@ pub struct Configuration { pub rpc_batch_config: RpcBatchRequestConfig, /// RPC rate limit per minute. pub rpc_rate_limit: Option, + /// RPC rate limit whitelisted ip addresses. + pub rpc_rate_limit_whitelisted_ips: Vec, + /// RPC rate limit trust proxy headers. + pub rpc_rate_limit_trust_proxy_headers: bool, /// Prometheus endpoint configuration. `None` if disabled. pub prometheus_config: Option, /// Telemetry service URL. `None` if disabled. diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index d0f315c30c89..444cb4a06eb9 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -407,6 +407,8 @@ where cors: config.rpc_cors.as_ref(), tokio_handle: config.tokio_handle.clone(), rate_limit: config.rpc_rate_limit, + rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), + rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, }; // TODO: https://github.com/paritytech/substrate/issues/13773 diff --git a/substrate/client/service/test/src/lib.rs b/substrate/client/service/test/src/lib.rs index b9abd8446f7d..f19b5a19739e 100644 --- a/substrate/client/service/test/src/lib.rs +++ b/substrate/client/service/test/src/lib.rs @@ -252,6 +252,8 @@ fn node_config< rpc_message_buffer_capacity: Default::default(), rpc_batch_config: RpcBatchRequestConfig::Unlimited, rpc_rate_limit: None, + rpc_rate_limit_whitelisted_ips: Default::default(), + rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, From 657df04cd901559cc6e33a8dfe70395bddb079f2 Mon Sep 17 00:00:00 2001 From: gupnik Date: Fri, 10 May 2024 14:49:43 +0530 Subject: [PATCH 07/10] Fixes `frame-support` reference in `try_decode_entire_state` (#4425) --- substrate/frame/support/procedural/src/pallet/expand/storage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/support/procedural/src/pallet/expand/storage.rs b/substrate/frame/support/procedural/src/pallet/expand/storage.rs index 937b068cfabd..3cc8a843e3b1 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/storage.rs @@ -854,7 +854,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { for #pallet_ident<#type_use_gen> #completed_where_clause { fn try_decode_entire_state() -> Result> { - let pallet_name = <::PalletInfo as frame_support::traits::PalletInfo> + let pallet_name = <::PalletInfo as #frame_support::traits::PalletInfo> ::name::<#pallet_ident<#type_use_gen>>() .expect("Every active pallet has a name in the runtime; qed"); From 2ed0f3e8318df7b78e311933cbec8778ed699e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 10 May 2024 14:05:57 +0200 Subject: [PATCH 08/10] rustls: Disable logging (#4426) Disable logging of rustls to get rid off the following log lines: ``` Sending fatal alert BadCertificate ``` Upstream also removed them: https://github.com/rustls/rustls/pull/1278 Closes: https://github.com/paritytech/polkadot-sdk/issues/3252 --- prdoc/pr_4426.prdoc | 15 +++++++++++++++ substrate/client/tracing/src/logging/mod.rs | 4 ++++ 2 files changed, 19 insertions(+) create mode 100644 prdoc/pr_4426.prdoc diff --git a/prdoc/pr_4426.prdoc b/prdoc/pr_4426.prdoc new file mode 100644 index 000000000000..5beccbd2a57a --- /dev/null +++ b/prdoc/pr_4426.prdoc @@ -0,0 +1,15 @@ +title: "Remove warning about `BadCertificate`" + +doc: + - audience: Node Operator + description: | + The node was printing the following warning from time to time: + ``` + Sending fatal alert BadCertificate + ``` + + This is not an user error and thus, the warning will now not be printed + anymore. + +crates: + - name: sc-cli diff --git a/substrate/client/tracing/src/logging/mod.rs b/substrate/client/tracing/src/logging/mod.rs index 8b2ad9b598b5..46fd4efb339a 100644 --- a/substrate/client/tracing/src/logging/mod.rs +++ b/substrate/client/tracing/src/logging/mod.rs @@ -141,6 +141,10 @@ where .add_directive( parse_default_directive("libp2p_mdns::behaviour::iface=off") .expect("provided directive is valid"), + ) + .add_directive( + parse_default_directive("rustls::common_state=off") + .expect("provided directive is valid"), ); if let Ok(lvl) = std::env::var("RUST_LOG") { From 00440779d42b754292783612fc0f7e99d7cde2d2 Mon Sep 17 00:00:00 2001 From: Maciej Date: Fri, 10 May 2024 13:31:53 +0100 Subject: [PATCH 09/10] Disabling Strategy Implementers Guide (#2955) Closes #1961 --- .../roadmap/implementers-guide/src/SUMMARY.md | 1 + .../src/protocol-disputes.md | 6 +- .../src/protocol-validator-disabling.md | 437 ++++++++++++++++++ 3 files changed, 441 insertions(+), 3 deletions(-) create mode 100644 polkadot/roadmap/implementers-guide/src/protocol-validator-disabling.md diff --git a/polkadot/roadmap/implementers-guide/src/SUMMARY.md b/polkadot/roadmap/implementers-guide/src/SUMMARY.md index bb19390c7af4..41485e5df8ec 100644 --- a/polkadot/roadmap/implementers-guide/src/SUMMARY.md +++ b/polkadot/roadmap/implementers-guide/src/SUMMARY.md @@ -8,6 +8,7 @@ - [Disputes Process](protocol-disputes.md) - [Dispute Flow](disputes-flow.md) - [Chain Selection and Finalization](protocol-chain-selection.md) + - [Validator Disabling](protocol-validator-disabling.md) - [Architecture Overview](architecture.md) - [Messaging Overview](messaging.md) - [PVF Pre-checking](pvf-prechecking.md) diff --git a/polkadot/roadmap/implementers-guide/src/protocol-disputes.md b/polkadot/roadmap/implementers-guide/src/protocol-disputes.md index 2a4082cc07f9..922cc3c3e2b5 100644 --- a/polkadot/roadmap/implementers-guide/src/protocol-disputes.md +++ b/polkadot/roadmap/implementers-guide/src/protocol-disputes.md @@ -8,9 +8,9 @@ All parachain blocks that end up in the finalized relay chain should be valid. T only backed, but not included. We have two primary components for ensuring that nothing invalid ends up in the finalized relay chain: - * Approval Checking, as described [here](./protocol-approval.md) and implemented according to the [Approval - Voting](node/approval/approval-voting.md) subsystem. This protocol can be shown to prevent invalid parachain blocks - from making their way into the finalized relay chain as long as the amount of attempts are limited. + * Approval Checking, as described [here](./protocol-approval.md) and implemented accordingly in the [Approval +Voting](node/approval/approval-voting.md) subsystem. This protocol can be shown to prevent invalid parachain blocks +from making their way into the finalized relay chain as long as the amount of attempts are limited. * Disputes, this protocol, which ensures that each attempt to include something bad is caught, and the offending validators are punished. Disputes differ from backing and approval process (and can not be part of those) in that a dispute is independent of a particular fork, while both backing and approval operate on particular forks. This diff --git a/polkadot/roadmap/implementers-guide/src/protocol-validator-disabling.md b/polkadot/roadmap/implementers-guide/src/protocol-validator-disabling.md new file mode 100644 index 000000000000..9fd44c00fa0a --- /dev/null +++ b/polkadot/roadmap/implementers-guide/src/protocol-validator-disabling.md @@ -0,0 +1,437 @@ +# Validator Disabling + +## Background + +As established in the [approval process](protocol-approval.md) dealing with bad parablocks is a three step process: + +1. Detection +1. Escalation +1. Consequences + +The main system responsible for dispensing **consequences** for malicious actors is the [dispute +system](protocol-disputes.md) which eventually dispenses slash events. The slashes itself can be dispensed quickly (a +matter of blocks) but for an extra layer of auditing all slashes are deferred for 27 days (in Polkadot/Kusama) which +gives time for Governance to investigate and potentially alter the punishment. Dispute concluding by itself does not +immediately remove the validator from the active validator set. + +> **Note:** \ +> There was an additional mechanism of automatically chilling the validator which removed their intent to participate in +> the next election, but the removed validator could simply re-register his intent to validate. + +There is a need to have a more immediate way to deal with malicious validators. This is where the validator disabling +comes in. It is focused on dispensing **low latency** consequences for malicious actors. It is important to note that +the validator disabling is not a replacement for the dispute or slashing systems. It is a complementary system that is +focused on lighter but immediate consequences usually in the form of restricted validator privileges. + +The primary goals are: +- Eliminate or minimize cases where attackers can get free attempts at attacking the network +- Eliminate or minimize the risks of honest nodes being pushed out of consensus when getting unjustly slashed (defense + in depth) + +The above two goals are generally at odds so a careful balance has to be struck between them. We will achieve them by +sacrificing some **liveness** in favor of **soundness** when the network is under stress. Maintaining some liveness but +absolute soundness is paramount. + +> **Note:** \ +> Liveness = Valid candidates can go through (at a decent pace) \ +> Security = Invalid candidates cannot go through (or are statistically very improbable) + +Side goals are: +- Reduce the damages to honest nodes that had a fault which might cause repeated slashes +- Reduce liveness impact of individual malicious attackers + +## System Overview + +High level assumptions and goals of the validator disabling system that will be further discussed in the following +sections: + +1. If validator gets slashed (even 0%) we mark them as disabled in the runtime and on the node side. +1. We only disable up to byzantine threshold of the validators. +1. If there are more offenders than byzantine threshold disable only the highest offenders. (Some might get re-enabled.) +1. Disablement lasts for 1 era. +1. Disabled validators remain in the active validator set but have some limited permissions. +1. Disabled validators can get re-elected. +1. Disabled validators can participate in approval checking. +1. Disabled validators can participate in GRANDPA/BEEFY, but equivocations cause disablement. +1. Disabled validators cannot author blocks. +1. Disabled validators cannot back candidates. +1. Disabled validators cannot initiate disputes, but their votes are still counted if a dispute occurs. +1. Disabled validators making dispute statements no-show in approval checking. + +


+ +# Risks + +## Risks of NOT having validator disabling + +Assume that if an offense is committed a slash is deposited but the perpetrator can still act normally. He will be +slashed 100% with a long delay (slash deferral duration which is 27 days). This is akin to the current design. + +A simple argument for disabling is that if someone is already slashed 100% and they have nothing to lose they could +cause harm to the network and should be silenced. + +What harm could they cause? + +**1. Liveness attacks:** + +- 1.1. Break sharding (with mass no-shows or mass disputes): It forces everyone to do all the work which affects + liveness but doesn't kill it completely. The chain can progress at a slow rate. + +- 1.2. Mass invalid candidate backing: Spawns a lot of worthless work that needs to be done but it is bounded by backing + numbers. Honest backers will still back valid candidates and that cannot be stopped. Honest block authors will + eventually select valid candidates and even if disputed they will win and progress the chain. + +**2. Soundness attacks:** + +- 2.1. The best and possibly only way to affect soundness is by getting lucky in the approval process. If by chance all + approval voters would be malicious, the attackers could get a single invalid candidate through. Their chances would be + relatively low but in general this risk has to be taken seriously as it significantly reduces the safety buffer around + approval checking. + +> **Note:** With 30 approvals needed chance that a malicious candidate going through is around 4\*10^-15. Assuming +> attackers can back invalid candidates on 50 cores for 48 hours straight and only those candidates get included it +> still gives a 7\*10^-9 chance of success which is still relatively small considering the cost (all malicious stake +> slashed). + +Attacks 1.2 and 2.1 should generally be pretty futile as a solo attacker while 1.1 could be possible with mass disputes +even from a single attacker. Nevertheless whatever the attack vector within the old system the attackers would get +*eventually* get slashed and pushed out of the active validator set but they had plenty of time to wreck havoc. + +## Risks of having validator disabling + +Assume we fully push out validator when they commit offenses. + +The primary risk behind having any sort of disabling is that it is a double-edged sword that in case of any dispute bugs +or sources of PVF non-determinism could disable honest nodes or be abused by attackers to specifically silence honest +nodes. + +Validators being pushed out of the validator set are an issue because that can greatly skew the numbers game in approval +checking (% for 30-ish malicious in a row). + +There are also censorship or liveness issues if backing is suddenly dominated by malicious nodes but in general even if +some honest blocks get backed liveness should be preserved. + +> **Note:** It is worth noting that is is fundamentally a defense in depth strategy because if we assume disputes are +> perfect it should not be a real concern. In reality disputes and determinism are difficult to get right, and +> non-determinism and happen so defense in depth is crucial when handling those subsystems. + +


+ +# Risks Mitigation + +## Addressing the risks of having validator disabling + +One safety measure is bounding the disabled number to 1/3 ([**Point 2.**](#system-overview)) or to be exact the +byzantine threshold. If for any reason more than 1/3 of validators are getting disabled it means that some part of the +protocol failed or there is more than 1/3 malicious nodes which breaks the assumptions. + +Even in such a dire situation where more than 1/3 got disabled the most likely scenario is a non-determinism bug or +sacrifice attack bug. Those attacks generally cause minor slashes to multiple honest nodes. In such a case the situation +could be salvaged by prioritizing highest offenders for disabling ([**Point 3.**](#system-overview)). + +> **Note:** \ +> System can be launched with re-enabling and will still provide some security improvements. Re-enabling will be +> launched in an upgrade after the initial deployment. + +Fully pushing out offending validator out of the validator set it too risky in case of a dispute bug, non-determinism or +sacrifice attacks. Main issue lies in skewing the numbers in approval checking so instead of fully blocking disabled +nodes a different approach can be taken - one were only some functionalities are disabled ([**Point +5.**](#system-overview)). Once of those functionalities can be approval voting which as pointed above is so crucial that +even in a disabled state nodes should be able to participate in it ([**Point 7.**](#system-overview)). + +> **Note:** \ +> Approval Checking statement are implicitly valid. Sending a statement for an invalid candidate is a part of the +> dispute logic which we did not yet discuss. For now we only allow nodes to state that a candidate is valid or remain +> silent. But this solves the main risk of disabling. + +Because we capped the number of disabled nodes to 1/3 there will always be at least 1/3 honest nodes to participate in +backing so liveness should be preserved. That means that backing **COULD** be safely disabled for disabled nodes +([**Point 10.**](#system-overview)). + + +## Addressing the risks of NOT having validator disabling + +To determine if backing **SHOULD** be disabled the attack vector of 1.2 (Mass invalid candidate backing) and 2.1 +(Getting lucky in approval voting) need to be considered. In both of those cases having extra backed malicious +candidates gives attackers extra chances to get lucky in approval checking. The solution is to not allow for backing in +disablement. ([**Point 10.**](#system-overview)) + +The attack vector 1.1 (Break sharding) requires a bit more nuance. If we assume that the attacker is a single entity and +that he can get a lot of disputes through he could potentially incredibly easily break sharding. This generally points +into the direction of disallowing that during disablement ([**Point 11.**](#system-overview)). + +This might seem like an issue because it takes away the escalation privileges of disabled approval checkers but this is +NOT true. By issuing a dispute statement those nodes remain silent in approval checking because they skip their approval +statement and thus will count as a no-show. This will create a mini escalation for that particular candidate. This means +that disabled nodes maintain just enough escalation that they can protect soundness (same argument as soundness +protection during a DoS attack on approval checking) but they lose their extreme escalation privilege which are only +given to flawlessly performing nodes ([**Point 12.**](#system-overview)). + +As a defense in depth measure dispute statements from disabled validators count toward confirming disputes (byzantine +threshold needed to confirm). If a dispute is confirmed everyone participates in it. This protects us from situations +where due to a bug more than byzantine threshold of validators would be disabled. + +> **Note:** \ +> The way this behavior is achieved easily in implementation is that honest nodes note down dispute statements from +> disabled validators just like they would for normal nodes, but they do not release their own dispute statements unless +> the dispute is confirmed already. This simply stops the escalation process of disputes. + +

+ +# Disabling Duration + +## Context + +A crucial point to understand is that as of the time of writing all slashing events as alluded to in the begging are +delayed for 27 days before being executed. This is primarily because it gives governance enough time to investigate and +potentially intervene. For that duration when the slash is pending the stake is locked and cannot be moved. Time to +unbond you stake is 28 days which ensures that the stake will eventually be slashed before being withdrawn. + +## Design + +A few options for the duration of disablement were considered: +- 1 epoch (4h in Polkadot) +- 1 era (24h in Polkadot) +- 2-26 eras +- 27 eras + +1 epoch is a short period and between a few epochs the validator will most likely be exactly the same. It is also very +difficult to fix any local node issues for honest validator in such a short time so the chance for a repeated offense is +high. + +1 era gives a bit more time to fix any minor issues. Additionally, it guarantees a validator set change at so many of +the currently disabled validator might no longer be present anyway. It also gives the time for the validator to chill +themselves if they have identified a cause and want to spend more time fixing it. ([**Point 4.**](#system-overview)) + +Higher values could be considered and the main arguments for those are based around the fact that it reduces the number +of repeated attacks that will be allowed before the slash execution. Generally 1 attack per era for 27 eras resulting in +27 attacks at most should not compromise our safety assumptions. Although this direction could be further explored and +might be parametrized for governance to decide. + +


+ +# Economic consequences of Disablement + +Disablement is generally a form of punishment and that will be reflected in the rewards at the end of an era. A disabled +validator will not receive any rewards for backing or block authoring. which will reduce its profits. + +That means that the opportunity cost of being disabled is a punishment by itself and thus it can be used for some cases +where a minor punishment is needed. Current implementation was using 0% slashes to mark nodes for chilling and similar +approach of 0% slashes can be used to mark validators for disablement. ([**Point 1.**](#system-overview)) 0% slashes +could for instance be used to punish approval checkers voting invalid on valid candidates. + +Anything higher than 0% will of course also lead to a disablement. + +> **Notes:** \ +> Alternative designs incorporating disabling proportional to offenses were explored but they were deemed too complex +> and not worth the effort. Main issue with those is that proportional disabling would cause back and forth between +> disabled and enabled which complicated tracking the state of disabled validators and messes with optimistic node +> optimizations. Main benefits were that minor slashes will be barely disabled which has nice properties against +> sacrifice attacks. + +


+ +# Redundancy + +Some systems can be greatly simplified or outright removed thanks to the above changes. This leads to reduced complexity +around the systems that were hard to reason about and were sources of potential bugs or new attack vectors. + +## Automatic Chilling + +Chilling is process of a validator dropping theirs intent to validate. This removes them from the upcoming NPoS +elections and effectively pushes them out of the validator set as quickly as of the next era (or 2 era in case of late +offenses). All nominators of that validator were also getting unsubscribed from that validator. Validator could +re-register their intent to validate at any time. The intent behind this logic was to protect honest stakes from +repeated slashes caused by unnoticed bugs. It would give time for validators to fix their issue before continuing as a +validator. + +Chilling had a myriad of problems. It assumes that validators and nominators remain very active and monitor everything. +If a validator got slashed he was getting automatically chilled and his nominators were getting unsubscribed. This was +an issue because of minor non-malicious slashes due to node operator mistakes or small bugs. Validators got those bugs +fixed quickly and were reimbursed but nominator had to manually re-subscribe to the validator, which they often +postponed for very lengthy amounts of time most likely due to simply not checking their stake. **This forced +unsubscribing of nominators was later disabled.** + +Automatic chilling was achieving its goals in ideal scenarios (no attackers, no lazy nominators) but it opened new +vulnerabilities for attackers. The biggest issue was that chilling in case of honest node slashes could lead to honest +validators being quickly pushed out of the next validator set within the next era. This retains the validator set size +but gives an edge to attackers as they can more easily win slots in the NPoS election. + +Disabling allows for punishment that limits the damages malicious actors can cause without having to resort to kicking +them out of the validator set. This protects us from the edge case of honest validators getting quickly pushed out of +the set by slashes. ([**Point 6.**](#system-overview)) + +> **Notes:** \ +> As long as honest slashes absolutely cannot occur automatic chilling is a sensible and desirable. This means it could +> be re-enabled once PolkaVM introduces deterministic gas metering. Then best of both worlds could be achieved. + +## Forcing New Era + +Previous implementation of disabling had some limited mechanisms allowing for validators disablement and if too many +were disabled forcing a new era (new election). Frame staking pallet offered the ability to force a new era but it was +also deemed unsafe as it could be abused and compromised the security of the network for instance by weakening the +randomness used throughout the protocol. + +


+ +# Other types of slashing + +Above slashes were specifically referring to slashing events coming from disputes against candidates, but in Polkadot +other types of offenses exist for example GRANDPA equivocations or block authoring offenses. Question is if the above +defined design can handle those offenses. + +## GRANDPA/BEEFY Offenses + +The main offences for GRANDPA/BEEFY are equivocations. It is not a very serious offense and some nodes committing do not +endanger the system and performance is barely affected. If more than byzantine threshold of nodes equivocate it is a +catastrophic failure potentially resulting in 2 finalized blocks on the same height in the case of GRANDPA. + +Honest nodes generally should not commit those offenses so the goal of protecting them does not apply here. + +> **Note:** \ +> A validator running multiple nodes with the same identity might equivocate. Doing that is highly not advised but it +> has happened before. + +It's not a game of chance so giving attackers extra chances does not compromise soundness. Also it requires a +supermajority of honest nodes to successfully finalize blocks so any disabling of honest nodes from GRANDPA might +compromise liveness. + +Best approach is to allow disabled nodes to participate in GRANDPA/BEEFY as normal and as mentioned before +GRANDPA/BABE/BEEFY equivocations should not happen to honest nodes so we can safely disable the offenders. Additionally +the slashes for singular equivocations will be very low so those offenders would easily get re-enabled in the case of +more serious offenders showing up. ([**Point 8.**](#system-overview)) + +## Block Authoring Offenses (BABE Equivocations) + +Even if all honest nodes are disabled in Block Authoring (BA) liveness is generally preserved. At least 50% of blocks +produced should still be honest. Soundness wise disabled nodes can create a decent amount of wasted work by creating bad +blocks but they only get to do it in bounded amounts. + +Disabling in BA is not a requirement as both liveness and soundness are preserved but it is the current default behavior +as well as it offers a bit less wasted work. + +Offenses in BA just like in backing can be caused by faulty PVFs or bugs. They might happen to honest nodes and +disabling here while not a requirement can also ensure that this node does not repeat the offense as it might not be +trusted with it's PVF anymore. + +Both points above don't present significant risks when disabling so the default behavior is to disable in BA and because +of offenses in BA. ([**Point 9.**](#system-overview)) This filters out honest faulty nodes as well as protects from some +attackers. + +


+ +# Extra Design Considerations + +## Disabling vs Accumulating Slashes + +Instant disabling generally allows us to remove the need for accumulating slashes. It is a more immediate punishment and +it is a more lenient punishment for honest nodes. + +The current architecture of using max slashing can be used and it works around the problems of delaying the slash for a +long period. + +An alternative design with immediate slashing and acclimating slashing could relevant to other systems but it goes +against the governance auditing mechanisms so it's not be suitable for Polkadot. + +## Disabling vs Getting Pushed Out of NPoS Elections + +Validator disabling and getting forced ouf of NPoS elections (1 era) due to slashes are actually very similar processes +in terms of outcomes but there are some differences: + +- **latency** (next few blocks for validator disabling and 27 days for getting pushed out organically) +- **pool restriction** (validator disabling could effectively lower the number of active validators during an era if we + fully disable) +- **granularity** (validator disabling could remove only a portion of validator privileges instead of all) + +Granularity is particularly crucial in the final design as only a few select functions are disabled while others remain. + +## Enabling Approval Voter Slashes + +The original Polkadot 1.0 design describes that all validators on the loosing side of the dispute are slashed. In the +current system only the backers are slashed and any approval voters on the wrong side will not be slashed. This creates +some undesirable incentives: + +- Lazy approval checkers (approvals yay`ing everything) +- Spammy approval checkers (approval voters nay`ing everything) + +Initially those slashes were disabled to reduce the complexity and to minimize the risk surface in case the system +malfunctioned. This is especially risky in case any nondeterministic bugs are present in the system. Once validator +re-enabling is launched approval voter slashes can be re-instated. Numbers need to be further explored but slashes +between 0-2% are reasonable. 0% would still disable which with the opportunity cost consideration should be enough. + + > **Note:** \ +> Spammy approval checkers are in fact not a big issue as a side effect of the offchain-disabling introduced by the +> Defense Against Past-Era Dispute Spam (**Node**) [#2225](https://github.com/paritytech/polkadot-sdk/issues/2225). It +> makes it so all validators loosing a dispute are locally disabled and ignored for dispute initiation so it effectively +> silences spammers. They can still no-show but the damage is minimized. + + +## Interaction with all types of misbehaviors + +With re-enabling in place and potentially approval voter slashes enabled the overall misbehaviour-punishment system can +be as highlighted in the table below: + +|Misbehaviour |Slash % |Onchain Disabling |Offchain Disabling |Chilling |Reputation Costs | +|------------ |------- |----------------- |------------------ |-------- |----------------- | +|Backing Invalid |100% |Yes (High Prio) |Yes (High Prio) |No |No | +|ForInvalid Vote |2% |Yes (Mid Prio) |Yes (Mid Prio) |No |No | +|AgainstValid Vote |0% |Yes (Low Prio) |Yes (Low Prio) |No |No | +|GRANDPA / BABE / BEEFY Equivocations |0.01-100% |Yes (Varying Prio) |No |No |No | +|Seconded + Valid Equivocation |- |No |No |No |No | +|Double Seconded Equivocation |- |No |No |No |Yes | + + +*Ignoring AURA offences. + +**There are some other misbehaviour types handled in rep only (DoS prevention etc) but they are not relevant to this strategy. + +*** BEEFY will soon introduce new slash types so this strategy table will need to be revised but no major changes are expected. + +


+ +# Implementation + +Implementation of the above design covers a few additional areas that allow for node-side optimizations. + +## Core Features + +1. Disabled Validators Tracking (**Runtime**) [#2950](https://github.com/paritytech/polkadot-sdk/issues/2950) + - Expose a ``disabled_validators`` map through a Runtime API +1. Enforce Backing Disabling (**Runtime**) [#1592](https://github.com/paritytech/polkadot-sdk/issues/1592) + - Filter out votes from ``disabled_validators`` in ``BackedCandidates`` in ``process_inherent_data`` +1. Substrate Byzantine Threshold (BZT) as Limit for Disabling + [#1963](https://github.com/paritytech/polkadot-sdk/issues/1963) + - Can be parametrized but default to BZT + - Disable only up to 1/3 of validators +1. Respect Disabling in Backing Statement Distribution (**Node**) + [#1591](https://github.com/paritytech/polkadot-sdk/issues/1951) + - This is an optimization as in the end it would get filtered in the runtime anyway + - Filter out backing statements coming from ``disabled_validators`` +1. Respect Disablement in Backing (**Node**) [#2951](https://github.com/paritytech/polkadot-sdk/issues/2951) + - This is an optimization as in the end it would get filtered in the runtime anyway + - Don't start backing new candidates when disabled + - Don't react to backing requests when disabled +1. Stop Automatic Chilling of Offenders [#1962](https://github.com/paritytech/polkadot-sdk/issues/1962) + - Chilling still persists as a state but is no longer automatically applied on offenses +1. Respect Disabling in Dispute Participation (**Node**) [#2225](https://github.com/paritytech/polkadot-sdk/issues/2225) + - Receive dispute statements from ``disabled_validators`` but do not release own statements + - Ensure dispute confirmation when BZT statements from disabled +1. Remove Liveness Slashes [#1964](https://github.com/paritytech/polkadot-sdk/issues/1964) + - Remove liveness slashes from the system + - The are other incentives to be online and they could be abused to attack the system +1. Defense Against Past-Era Dispute Spam (**Node**) [#2225](https://github.com/paritytech/polkadot-sdk/issues/2225) + - This is needed because runtime cannot disable validators which it no longer knows about + - Add a node-side parallel store of ``disabled_validators`` + - Add new disabled validators to node-side store when they loose a dispute in any leaf in scope + - Runtime ``disabled_validators`` always have priority over node-side ``disabled_validators`` + - Respect the BZT threshold + > **Note:** \ + > An alternative design here was considered where instead of tracking new incoming leaves a relay parent is used. + > This would guarantee determinism as different nodes can see different leaves, but this approach was leaving too + > wide of a window because of Async-Backing. Relay Parent could have been significantly in the past and it would + > give a lot of time for past session disputes to be spammed. +1. Do not block finality for "disabled" disputes [#3358](https://github.com/paritytech/polkadot-sdk/pull/3358) + - Emergency fix to not block finality for disputes initiated only by disabled validators +1. Re-enable small offender when approaching BZT (**Runtime**) #TODO + - When BZT limit is reached and there are more offenders to be disabled re-enable the smallest offenders to disable + the biggest ones From a993513c9c54313bc3c7093563d2f4ff6fe42ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=B3nal=20Murray?= Date: Fri, 10 May 2024 20:41:02 +0100 Subject: [PATCH 10/10] Add docs to request_core_count (#4423) The fact that this takes two sessions to come into effect is not obvious. Just added some docs to explain that. Also tidied up uses of "broker chain" -> "coretime chain" --- .../runtime/parachains/src/coretime/mod.rs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/polkadot/runtime/parachains/src/coretime/mod.rs b/polkadot/runtime/parachains/src/coretime/mod.rs index 94bce4c83e6f..33cbcb98fb29 100644 --- a/polkadot/runtime/parachains/src/coretime/mod.rs +++ b/polkadot/runtime/parachains/src/coretime/mod.rs @@ -106,7 +106,7 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// The runtime's definition of a Currency. type Currency: Currency; - /// The ParaId of the broker system parachain. + /// The ParaId of the coretime chain. #[pallet::constant] type BrokerId: Get; /// Something that provides the weight of this pallet. @@ -139,10 +139,16 @@ pub mod pallet { #[pallet::call] impl Pallet { + /// Request the configuration to be updated with the specified number of cores. Warning: + /// Since this only schedules a configuration update, it takes two sessions to come into + /// effect. + /// + /// - `origin`: Root or the Coretime Chain + /// - `count`: total number of cores #[pallet::weight(::WeightInfo::request_core_count())] #[pallet::call_index(1)] pub fn request_core_count(origin: OriginFor, count: u16) -> DispatchResult { - // Ignore requests not coming from the broker parachain or root. + // Ignore requests not coming from the coretime chain or root. Self::ensure_root_or_para(origin, ::BrokerId::get().into())?; configuration::Pallet::::set_coretime_cores_unchecked(u32::from(count)) @@ -155,7 +161,7 @@ pub mod pallet { // origin: OriginFor, // _when: BlockNumberFor, //) -> DispatchResult { - // // Ignore requests not coming from the broker parachain or root. + // // Ignore requests not coming from the coretime chain or root. // Self::ensure_root_or_para(origin, ::BrokerId::get().into())?; // Ok(()) //} @@ -168,7 +174,7 @@ pub mod pallet { // _who: T::AccountId, // _amount: BalanceOf, //) -> DispatchResult { - // // Ignore requests not coming from the broker parachain or root. + // // Ignore requests not coming from the coretime chain or root. // Self::ensure_root_or_para(origin, ::BrokerId::get().into())?; // Ok(()) //} @@ -177,7 +183,7 @@ pub mod pallet { /// to be used. /// /// Parameters: - /// -`origin`: The `ExternalBrokerOrigin`, assumed to be the Broker system parachain. + /// -`origin`: The `ExternalBrokerOrigin`, assumed to be the coretime chain. /// -`core`: The core that should be scheduled. /// -`begin`: The starting blockheight of the instruction. /// -`assignment`: How the blockspace should be utilised. @@ -193,7 +199,7 @@ pub mod pallet { assignment: Vec<(CoreAssignment, PartsOf57600)>, end_hint: Option>, ) -> DispatchResult { - // Ignore requests not coming from the broker parachain or root. + // Ignore requests not coming from the coretime chain or root. Self::ensure_root_or_para(origin, T::BrokerId::get().into())?; let core = u32::from(core).into(); @@ -243,7 +249,7 @@ impl Pallet { } } - // Handle legacy swaps in coretime. Notifies broker parachain that a lease swap has occurred via + // Handle legacy swaps in coretime. Notifies coretime chain that a lease swap has occurred via // XCM message. This function is meant to be used in an implementation of `OnSwap` trait. pub fn on_legacy_lease_swap(one: ParaId, other: ParaId) { let message = Xcm(vec![