Add XCM Origin and converter (#2896)

* Add XCM Origin and converter

* IsMajority filter can be location-prefixed

* Update xcm/pallet-xcm/src/lib.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/src/v0/multi_location.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Introduce UsingComponents to allow reuse of fee payment in XCM

* Use Drop rather than finalize

* Add errors for weight.

* Apply suggestions from code review

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Fixes

* Update xcm/xcm-builder/src/weight.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Various XCM fixes and improvements

* Fixes

* Update xcm/xcm-builder/src/weight.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/weight.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/weight.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Fixes

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Gavin Wood
2021-04-27 14:33:40 +02:00
committed by GitHub
parent 8e0963e533
commit 9194219586
13 changed files with 357 additions and 218 deletions
+2
View File
@@ -16,6 +16,7 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", de
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
# Polkadot dependencies
polkadot-parachain = { path = "../../parachain", default-features = false }
@@ -33,4 +34,5 @@ std = [
"sp-runtime/std",
"frame-support/std",
"polkadot-parachain/std",
"pallet-transaction-payment/std",
]
+1 -1
View File
@@ -46,7 +46,7 @@ mod fungibles_adapter;
pub use fungibles_adapter::FungiblesAdapter;
mod weight;
pub use weight::{FixedRateOfConcreteFungible, FixedWeightBounds};
pub use weight::{FixedRateOfConcreteFungible, FixedWeightBounds, UsingComponents};
mod matches_fungible;
pub use matches_fungible::{IsAbstract, IsConcrete};
+1 -1
View File
@@ -272,6 +272,6 @@ impl Config for TestConfig {
type LocationInverter = LocationInverter<TestAncestry>;
type Barrier = TestBarrier;
type Weigher = FixedWeightBounds<UnitWeightCost, TestCall>;
type Trader = FixedRateOfConcreteFungible<WeightPrice>;
type Trader = FixedRateOfConcreteFungible<WeightPrice, ()>;
type ResponseHandler = TestResponseHandler;
}
+108 -23
View File
@@ -14,19 +14,22 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use sp_std::{result::Result, marker::PhantomData};
use sp_std::{result::Result, marker::PhantomData, convert::TryInto};
use parity_scale_codec::Decode;
use xcm::v0::{Xcm, Order, MultiAsset, MultiLocation};
use frame_support::{traits::Get, weights::{Weight, GetDispatchInfo}};
use xcm::v0::{Xcm, Order, MultiAsset, MultiLocation, Error};
use sp_runtime::traits::{Zero, Saturating, SaturatedConversion};
use frame_support::traits::{Get, OnUnbalanced as OnUnbalancedT, tokens::currency::Currency as CurrencyT};
use frame_support::weights::{Weight, GetDispatchInfo, WeightToFeePolynomial};
use xcm_executor::{Assets, traits::{WeightBounds, WeightTrader}};
pub struct FixedWeightBounds<T, C>(PhantomData<(T, C)>);
impl<T: Get<Weight>, C: Decode + GetDispatchInfo> WeightBounds<C> for FixedWeightBounds<T, C> {
fn shallow(message: &mut Xcm<C>) -> Result<Weight, ()> {
let min = match message {
Ok(match message {
Xcm::Transact { call, .. } => {
call.ensure_decoded()?.get_dispatch_info().weight + T::get()
}
Xcm::RelayedFrom { ref mut message, .. } => T::get() + Self::shallow(message.as_mut())?,
Xcm::WithdrawAsset { effects, .. }
| Xcm::ReserveAssetDeposit { effects, .. }
| Xcm::TeleportAsset { effects, .. } => {
@@ -45,16 +48,16 @@ impl<T: Get<Weight>, C: Decode + GetDispatchInfo> WeightBounds<C> for FixedWeigh
T::get() + inner
}
_ => T::get(),
};
Ok(min)
})
}
fn deep(message: &mut Xcm<C>) -> Result<Weight, ()> {
let mut extra = 0;
match message {
Xcm::Transact { .. } => {}
Ok(match message {
Xcm::RelayedFrom { ref mut message, .. } => Self::deep(message.as_mut())?,
Xcm::WithdrawAsset { effects, .. }
| Xcm::ReserveAssetDeposit { effects, .. }
| Xcm::TeleportAsset { effects, .. } => {
| Xcm::TeleportAsset { effects, .. }
=> {
let mut extra = 0;
for effect in effects.iter_mut() {
match effect {
Order::BuyExecution { xcm, .. } => {
@@ -65,34 +68,116 @@ impl<T: Get<Weight>, C: Decode + GetDispatchInfo> WeightBounds<C> for FixedWeigh
_ => {}
}
}
}
_ => {}
};
Ok(extra)
extra
},
_ => 0,
})
}
}
/// Function trait for handling some revenue. Similar to a negative imbalance (credit) handler, but for a
/// `MultiAsset`. Sensible implementations will deposit the asset in some known treasury or block-author account.
pub trait TakeRevenue {
/// Do something with the given `revenue`, which is a single non-wildcard `MultiAsset`.
fn take_revenue(revenue: MultiAsset);
}
/// Null implementation just burns the revenue.
impl TakeRevenue for () {
fn take_revenue(_revenue: MultiAsset) {}
}
/// Simple fee calculator that requires payment in a single concrete fungible at a fixed rate.
///
/// The constant `Get` type parameter should be the concrete fungible ID and the amount of it required for
/// one second of weight.
pub struct FixedRateOfConcreteFungible<T>(Weight, PhantomData<T>);
impl<T: Get<(MultiLocation, u128)>> WeightTrader for FixedRateOfConcreteFungible<T> {
fn new() -> Self { Self(0, PhantomData) }
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, ()> {
pub struct FixedRateOfConcreteFungible<
T: Get<(MultiLocation, u128)>,
R: TakeRevenue,
>(Weight, u128, PhantomData<(T, R)>);
impl<T: Get<(MultiLocation, u128)>, R: TakeRevenue> WeightTrader for FixedRateOfConcreteFungible<T, R> {
fn new() -> Self { Self(0, 0, PhantomData) }
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, Error> {
let (id, units_per_second) = T::get();
let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128;
use frame_support::weights::constants::WEIGHT_PER_SECOND;
let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128);
let required = MultiAsset::ConcreteFungible { amount, id };
let (used, _) = payment.less(required).map_err(|_| ())?;
let (unused, _) = payment.less(required).map_err(|_| Error::TooExpensive)?;
self.0 = self.0.saturating_add(weight);
Ok(used)
self.1 = self.1.saturating_add(amount);
Ok(unused)
}
fn refund_weight(&mut self, weight: Weight) -> MultiAsset {
let weight = weight.min(self.0);
self.0 -= weight;
let (id, units_per_second) = T::get();
let weight = weight.min(self.0);
let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128;
self.0 -= weight;
self.1 = self.1.saturating_sub(amount);
let result = MultiAsset::ConcreteFungible { amount, id };
result
}
}
impl<T: Get<(MultiLocation, u128)>, R: TakeRevenue> Drop for FixedRateOfConcreteFungible<T, R> {
fn drop(&mut self) {
let revenue = MultiAsset::ConcreteFungible { amount: self.1, id: T::get().0 };
R::take_revenue(revenue);
}
}
/// Weight trader which uses the TransactionPayment pallet to set the right price for weight and then
/// places any weight bought into the right account.
pub struct UsingComponents<
WeightToFee: WeightToFeePolynomial<Balance=Currency::Balance>,
AssetId: Get<MultiLocation>,
AccountId,
Currency: CurrencyT<AccountId>,
OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
>(Weight, Currency::Balance, PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>);
impl<
WeightToFee: WeightToFeePolynomial<Balance=Currency::Balance>,
AssetId: Get<MultiLocation>,
AccountId,
Currency: CurrencyT<AccountId>,
OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
> WeightTrader for UsingComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> {
fn new() -> Self { Self(0, Zero::zero(), PhantomData) }
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, Error> {
let amount = WeightToFee::calc(&weight);
let required = MultiAsset::ConcreteFungible {
amount: amount.try_into().map_err(|_| Error::Overflow)?,
id: AssetId::get(),
};
let (unused, _) = payment.less(required).map_err(|_| Error::TooExpensive)?;
self.0 = self.0.saturating_add(weight);
self.1 = self.1.saturating_add(amount);
Ok(unused)
}
fn refund_weight(&mut self, weight: Weight) -> MultiAsset {
let weight = weight.min(self.0);
let amount = WeightToFee::calc(&weight);
self.0 -= weight;
self.1 = self.1.saturating_sub(amount);
let result = MultiAsset::ConcreteFungible {
amount: amount.saturated_into(),
id: AssetId::get(),
};
result
}
}
impl<
WeightToFee: WeightToFeePolynomial<Balance=Currency::Balance>,
AssetId: Get<MultiLocation>,
AccountId,
Currency: CurrencyT<AccountId>,
OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
> Drop for UsingComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> {
fn drop(&mut self) {
OnUnbalanced::on_unbalanced(Currency::issue(self.1));
}
}