diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index 9bbdf3a93724999c8ea92f98d75b13ae98de53e1..1eb77ed75ea63aba70d39907cf0084a5a198f3a3 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -282,7 +282,7 @@ parameter_types! { impl pallet_transaction_payment::Config for Runtime { type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, ()>; type TransactionByteFee = TransactionByteFee; - type WeightToFee = IdentityFee<Balance>; + type WeightToFee = bp_millau::WeightToFee; type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< Runtime, TargetBlockFullness, diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs index 2899d18d140275064bff862a38bfdf05500faa3d..c13c0d78a1b40fde4fcb32ab7ecab1b0a2b181eb 100644 --- a/bridges/bin/rialto/runtime/src/lib.rs +++ b/bridges/bin/rialto/runtime/src/lib.rs @@ -414,7 +414,7 @@ parameter_types! { impl pallet_transaction_payment::Config for Runtime { type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, ()>; type TransactionByteFee = TransactionByteFee; - type WeightToFee = IdentityFee<Balance>; + type WeightToFee = bp_rialto::WeightToFee; type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< Runtime, TargetBlockFullness, diff --git a/bridges/primitives/chain-kusama/Cargo.toml b/bridges/primitives/chain-kusama/Cargo.toml index 70ff3b844df07a295c098541f933c82d226cf542..33102ea4f88715079daa47191b3673d19d2bbb44 100644 --- a/bridges/primitives/chain-kusama/Cargo.toml +++ b/bridges/primitives/chain-kusama/Cargo.toml @@ -7,13 +7,17 @@ edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +smallvec = "1.6" # Bridge Dependencies + bp-messages = { path = "../messages", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -23,6 +27,7 @@ std = [ "bp-messages/std", "bp-polkadot-core/std", "bp-runtime/std", + "frame-support/std", "sp-api/std", "sp-std/std", ] diff --git a/bridges/primitives/chain-kusama/src/lib.rs b/bridges/primitives/chain-kusama/src/lib.rs index e5ab47259e54c2fe31d6d0441fabf49460037f1a..59bb486c90064a1f2d271fbd7aaf2f4074255629 100644 --- a/bridges/primitives/chain-kusama/src/lib.rs +++ b/bridges/primitives/chain-kusama/src/lib.rs @@ -21,6 +21,7 @@ #![allow(clippy::unnecessary_mut_passed)] use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; +use frame_support::weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial}; use sp_std::prelude::*; pub use bp_polkadot_core::*; @@ -28,6 +29,24 @@ pub use bp_polkadot_core::*; /// Kusama Chain pub type Kusama = PolkadotLike; +// NOTE: This needs to be kept up to date with the Kusama runtime found in the Polkadot repo. +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + fn polynomial() -> WeightToFeeCoefficients<Self::Balance> { + const CENTS: Balance = 1_000_000_000_000 / 30_000; + // in Kusama, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT: + let p = CENTS; + let q = 10 * Balance::from(ExtrinsicBaseWeight::get()); + smallvec::smallvec![WeightToFeeCoefficient { + degree: 1, + negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, + }] + } +} + // We use this to get the account on Kusama (target) which is derived from Polkadot's (source) // account. pub fn derive_account_from_polkadot_id(id: bp_runtime::SourceAccount<AccountId>) -> AccountId { diff --git a/bridges/primitives/chain-millau/src/lib.rs b/bridges/primitives/chain-millau/src/lib.rs index 34d59ce2ef9a0ca331917baca68994fce401d7b5..cb732bccfa055a92a6ed67c8f253ecb67ed89afa 100644 --- a/bridges/primitives/chain-millau/src/lib.rs +++ b/bridges/primitives/chain-millau/src/lib.rs @@ -25,7 +25,7 @@ mod millau_hash; use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; use bp_runtime::Chain; use frame_support::{ - weights::{constants::WEIGHT_PER_SECOND, DispatchClass, Weight}, + weights::{constants::WEIGHT_PER_SECOND, DispatchClass, IdentityFee, Weight}, Parameter, RuntimeDebug, }; use frame_system::limits; @@ -149,6 +149,9 @@ pub type AccountSigner = MultiSigner; /// Balance of an account. pub type Balance = u64; +/// Weight-to-Fee type used by Millau. +pub type WeightToFee = IdentityFee<Balance>; + /// Millau chain. #[derive(RuntimeDebug)] pub struct Millau; diff --git a/bridges/primitives/chain-polkadot/Cargo.toml b/bridges/primitives/chain-polkadot/Cargo.toml index 22ded41b9145ca690f423939d7bdc611ecc48c55..4d3be2ae477d77d47bcb019245d05e8c3729da18 100644 --- a/bridges/primitives/chain-polkadot/Cargo.toml +++ b/bridges/primitives/chain-polkadot/Cargo.toml @@ -7,14 +7,17 @@ edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +smallvec = "1.6" # Bridge Dependencies + bp-messages = { path = "../messages", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -24,6 +27,7 @@ std = [ "bp-messages/std", "bp-polkadot-core/std", "bp-runtime/std", + "frame-support/std", "sp-api/std", "sp-std/std", ] diff --git a/bridges/primitives/chain-polkadot/src/lib.rs b/bridges/primitives/chain-polkadot/src/lib.rs index b0ba77c66ffc34cc7dbb9fd5534832e74cce5c23..17c80e82b22c216a367f17aaf20759c23cec792e 100644 --- a/bridges/primitives/chain-polkadot/src/lib.rs +++ b/bridges/primitives/chain-polkadot/src/lib.rs @@ -21,6 +21,7 @@ #![allow(clippy::unnecessary_mut_passed)] use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; +use frame_support::weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial}; use sp_std::prelude::*; pub use bp_polkadot_core::*; @@ -28,6 +29,24 @@ pub use bp_polkadot_core::*; /// Polkadot Chain pub type Polkadot = PolkadotLike; +// NOTE: This needs to be kept up to date with the Polkadot runtime found in the Polkadot repo. +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + fn polynomial() -> WeightToFeeCoefficients<Self::Balance> { + const CENTS: Balance = 10_000_000_000 / 100; + // in Polkadot, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT: + let p = CENTS; + let q = 10 * Balance::from(ExtrinsicBaseWeight::get()); + smallvec::smallvec![WeightToFeeCoefficient { + degree: 1, + negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, + }] + } +} + // We use this to get the account on Polkadot (target) which is derived from Kusama's (source) // account. pub fn derive_account_from_kusama_id(id: bp_runtime::SourceAccount<AccountId>) -> AccountId { diff --git a/bridges/primitives/chain-rialto/src/lib.rs b/bridges/primitives/chain-rialto/src/lib.rs index 57fe5d4bfa54264b61107b018a2a298ec4d767aa..3c57b701b71668ae758f2474ba24b49caea03f3f 100644 --- a/bridges/primitives/chain-rialto/src/lib.rs +++ b/bridges/primitives/chain-rialto/src/lib.rs @@ -23,7 +23,7 @@ use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; use bp_runtime::Chain; use frame_support::{ - weights::{constants::WEIGHT_PER_SECOND, DispatchClass, Weight}, + weights::{constants::WEIGHT_PER_SECOND, DispatchClass, IdentityFee, Weight}, Parameter, RuntimeDebug, }; use frame_system::limits; @@ -148,6 +148,9 @@ pub type Balance = u128; /// An instant or duration in time. pub type Moment = u64; +/// Weight-to-Fee type used by Rialto. +pub type WeightToFee = IdentityFee<Balance>; + /// Rialto chain. #[derive(RuntimeDebug)] pub struct Rialto; diff --git a/bridges/primitives/chain-rococo/src/lib.rs b/bridges/primitives/chain-rococo/src/lib.rs index 2d5769a39c2f3aebc3d04bd773c1a8706191af56..ce58e7ec9ab02d30220707ec366ccfdd6b025cf1 100644 --- a/bridges/primitives/chain-rococo/src/lib.rs +++ b/bridges/primitives/chain-rococo/src/lib.rs @@ -21,7 +21,7 @@ #![allow(clippy::unnecessary_mut_passed)] use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; -use frame_support::weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial}; +use frame_support::weights::{Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial}; use sp_std::prelude::*; use sp_version::RuntimeVersion; @@ -97,6 +97,13 @@ pub const FROM_ROCOCO_LATEST_CONFIRMED_NONCE_METHOD: &str = "FromRococoInboundLa /// Name of the `FromRococoInboundLaneApi::unrewarded_relayers_state` runtime method. pub const FROM_ROCOCO_UNREWARDED_RELAYERS_STATE: &str = "FromRococoInboundLaneApi_unrewarded_relayers_state"; +/// Weight of pay-dispatch-fee operation for inbound messages at Rococo chain. +/// +/// This value corresponds to the result of `pallet_bridge_messages::WeightInfoExt::pay_inbound_dispatch_fee_overhead()` +/// call for your chain. Don't put too much reserve there, because it is used to **decrease** +/// `DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT` cost. So putting large reserve would make delivery transactions cheaper. +pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = 600_000_000; + sp_api::decl_runtime_apis! { /// API for querying information about the finalized Rococo headers. /// diff --git a/bridges/primitives/chain-westend/Cargo.toml b/bridges/primitives/chain-westend/Cargo.toml index 42a9e67a7174fc3b333df5e23a7cc455de25d092..2fb8df091f5571f7fdf79acc2b7839987fa9e4e7 100644 --- a/bridges/primitives/chain-westend/Cargo.toml +++ b/bridges/primitives/chain-westend/Cargo.toml @@ -8,14 +8,18 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] parity-scale-codec = { version = "2.2.0", default-features = false, features = ["derive"] } +smallvec = "1.6" # Bridge Dependencies + bp-header-chain = { path = "../header-chain", default-features = false } bp-messages = { path = "../messages", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -28,6 +32,7 @@ std = [ "bp-messages/std", "bp-polkadot-core/std", "bp-runtime/std", + "frame-support/std", "parity-scale-codec/std", "sp-api/std", "sp-runtime/std", diff --git a/bridges/primitives/chain-westend/src/lib.rs b/bridges/primitives/chain-westend/src/lib.rs index 4f8b9cccf4c75520bdba0dd7aff7c8253ba7ae0a..595c41e443e59e1e275630db450a2d61449d568a 100644 --- a/bridges/primitives/chain-westend/src/lib.rs +++ b/bridges/primitives/chain-westend/src/lib.rs @@ -22,6 +22,7 @@ use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; use bp_runtime::Chain; +use frame_support::weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial}; use sp_std::prelude::*; use sp_version::RuntimeVersion; @@ -30,6 +31,24 @@ pub use bp_polkadot_core::*; /// Westend Chain pub type Westend = PolkadotLike; +// NOTE: This needs to be kept up to date with the Westend runtime found in the Polkadot repo. +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + fn polynomial() -> WeightToFeeCoefficients<Self::Balance> { + const CENTS: Balance = 1_000_000_000_000 / 1_000; + // in Westend, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT: + let p = CENTS; + let q = 10 * Balance::from(ExtrinsicBaseWeight::get()); + smallvec::smallvec![WeightToFeeCoefficient { + degree: 1, + negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, + }] + } +} + pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>; // NOTE: This needs to be kept up to date with the Westend runtime found in the Polkadot repo. diff --git a/bridges/primitives/chain-wococo/src/lib.rs b/bridges/primitives/chain-wococo/src/lib.rs index b846e00321fd01ea031ad09193f01cf70e2a9e8a..f962973d6c1c8f943a49f4f8793cafb008a0a597 100644 --- a/bridges/primitives/chain-wococo/src/lib.rs +++ b/bridges/primitives/chain-wococo/src/lib.rs @@ -25,7 +25,7 @@ use sp_std::prelude::*; pub use bp_polkadot_core::*; // Rococo runtime = Wococo runtime -pub use bp_rococo::{WeightToFee, SESSION_LENGTH, VERSION}; +pub use bp_rococo::{WeightToFee, PAY_INBOUND_DISPATCH_FEE_WEIGHT, SESSION_LENGTH, VERSION}; /// Wococo Chain pub type Wococo = PolkadotLike; diff --git a/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs b/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs index 4195e452d9e0d646d680a2c080ac7364364f52c5..51eb4e961b68a7d9cda24b67ec5ff5eff49a26ec 100644 --- a/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs +++ b/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs @@ -24,6 +24,7 @@ use sp_core::{Bytes, Pair}; use bp_messages::MessageNonce; use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; +use frame_support::weights::Weight; use messages_relay::message_lane::MessageLane; use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams}; use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams}; @@ -64,6 +65,8 @@ impl SubstrateMessageLane for MillauMessagesToRialto { const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_millau::WITH_RIALTO_MESSAGES_PALLET_NAME; const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_rialto::WITH_MILLAU_MESSAGES_PALLET_NAME; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + type SourceChain = Millau; type TargetChain = Rialto; diff --git a/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs b/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs index 6fc7ee5b0828c99ba64e911dad622ea0d77163f0..0ced49a0a31463fd9a3d0ec9725d5c6c799ae742 100644 --- a/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs +++ b/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs @@ -24,6 +24,7 @@ use sp_core::{Bytes, Pair}; use bp_messages::MessageNonce; use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; +use frame_support::weights::Weight; use messages_relay::message_lane::MessageLane; use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams}; use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams}; @@ -64,6 +65,8 @@ impl SubstrateMessageLane for RialtoMessagesToMillau { const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_rialto::WITH_MILLAU_MESSAGES_PALLET_NAME; const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_millau::WITH_RIALTO_MESSAGES_PALLET_NAME; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + type SourceChain = Rialto; type TargetChain = Millau; diff --git a/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs b/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs index 9fa06770523e962411bd740a7749b45863298cfe..51f89c0dbe6dfce22d45f8c3ff0268160d7e5a3d 100644 --- a/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs +++ b/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs @@ -23,6 +23,7 @@ use sp_core::{Bytes, Pair}; use bp_messages::MessageNonce; use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; +use frame_support::weights::Weight; use messages_relay::message_lane::MessageLane; use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams}; use relay_substrate_client::{Chain, Client, TransactionSignScheme}; @@ -63,6 +64,8 @@ impl SubstrateMessageLane for RococoMessagesToWococo { const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_rococo::WITH_WOCOCO_MESSAGES_PALLET_NAME; const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_wococo::WITH_ROCOCO_MESSAGES_PALLET_NAME; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + type SourceChain = Rococo; type TargetChain = Wococo; diff --git a/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs b/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs index 6e03d752f00034734f06492950cf691e745573db..9bc13dec1440d3a71c1a84d6192bc07acca52dd1 100644 --- a/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs +++ b/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs @@ -23,6 +23,7 @@ use sp_core::{Bytes, Pair}; use bp_messages::MessageNonce; use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; +use frame_support::weights::Weight; use messages_relay::message_lane::MessageLane; use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams}; use relay_substrate_client::{Chain, Client, TransactionSignScheme}; @@ -62,6 +63,8 @@ impl SubstrateMessageLane for WococoMessagesToRococo { const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_wococo::WITH_ROCOCO_MESSAGES_PALLET_NAME; const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_rococo::WITH_WOCOCO_MESSAGES_PALLET_NAME; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + type SourceChain = Wococo; type TargetChain = Rococo; diff --git a/bridges/relays/bin-substrate/src/cli/send_message.rs b/bridges/relays/bin-substrate/src/cli/send_message.rs index 75c3118eb49f23c76afc407f0f757ec953746ee8..5ab6b84c32df5c9e1bff336ac5a3c3a154cd795d 100644 --- a/bridges/relays/bin-substrate/src/cli/send_message.rs +++ b/bridges/relays/bin-substrate/src/cli/send_message.rs @@ -190,8 +190,9 @@ impl SendMessage { log::info!( target: "bridge", - "Sending message to {}. Size: {}. Dispatch weight: {}. Fee: {}", + "Sending message to {}. Lane: {:?}. Size: {}. Dispatch weight: {}. Fee: {}", Target::NAME, + lane, signed_source_call.len(), dispatch_weight, fee, diff --git a/bridges/relays/client-kusama/src/lib.rs b/bridges/relays/client-kusama/src/lib.rs index 01869fb7b1c4b0f2d71eb597970e56fbbed93456..0c94e73aecff6ffb807ad5ada9e59202851ff04f 100644 --- a/bridges/relays/client-kusama/src/lib.rs +++ b/bridges/relays/client-kusama/src/lib.rs @@ -44,6 +44,7 @@ impl Chain for Kusama { type SignedBlock = bp_kusama::SignedBlock; type Call = (); type Balance = bp_kusama::Balance; + type WeightToFee = bp_kusama::WeightToFee; } /// Kusama header type used in headers sync. diff --git a/bridges/relays/client-millau/src/lib.rs b/bridges/relays/client-millau/src/lib.rs index ce1ab870141f5f88714f5e826ac7a659700d5ea2..36430dd83dc219a6866b782363fb119a23e0191d 100644 --- a/bridges/relays/client-millau/src/lib.rs +++ b/bridges/relays/client-millau/src/lib.rs @@ -47,6 +47,7 @@ impl Chain for Millau { type SignedBlock = millau_runtime::SignedBlock; type Call = millau_runtime::Call; type Balance = millau_runtime::Balance; + type WeightToFee = bp_millau::WeightToFee; } impl ChainWithBalances for Millau { diff --git a/bridges/relays/client-polkadot/src/lib.rs b/bridges/relays/client-polkadot/src/lib.rs index 04ddce29d091d8db8d43667666cd73757d3c4dc2..dc5564cc17bb5c489366c7f75745106184fb6eea 100644 --- a/bridges/relays/client-polkadot/src/lib.rs +++ b/bridges/relays/client-polkadot/src/lib.rs @@ -44,6 +44,7 @@ impl Chain for Polkadot { type SignedBlock = bp_polkadot::SignedBlock; type Call = (); type Balance = bp_polkadot::Balance; + type WeightToFee = bp_polkadot::WeightToFee; } /// Polkadot header type used in headers sync. diff --git a/bridges/relays/client-rialto/src/lib.rs b/bridges/relays/client-rialto/src/lib.rs index aaa62eea0e7f305ddb5bb84d548a9a035e8ea755..8024ab1fdce565d4bb74d2863518f65d797049c4 100644 --- a/bridges/relays/client-rialto/src/lib.rs +++ b/bridges/relays/client-rialto/src/lib.rs @@ -47,6 +47,7 @@ impl Chain for Rialto { type SignedBlock = rialto_runtime::SignedBlock; type Call = rialto_runtime::Call; type Balance = rialto_runtime::Balance; + type WeightToFee = bp_rialto::WeightToFee; } impl ChainWithBalances for Rialto { diff --git a/bridges/relays/client-rococo/src/lib.rs b/bridges/relays/client-rococo/src/lib.rs index 25c10999c66a56162512ef2af896be13bd3c4f6a..c419610dad05cbb97bca1aa680b962225f2fd389 100644 --- a/bridges/relays/client-rococo/src/lib.rs +++ b/bridges/relays/client-rococo/src/lib.rs @@ -52,6 +52,7 @@ impl Chain for Rococo { type SignedBlock = bp_rococo::SignedBlock; type Call = crate::runtime::Call; type Balance = bp_rococo::Balance; + type WeightToFee = bp_rococo::WeightToFee; } impl ChainWithBalances for Rococo { diff --git a/bridges/relays/client-substrate/src/chain.rs b/bridges/relays/client-substrate/src/chain.rs index 7bc5f711f0605a7708fe4add4d16d5a0bc6f9a55..81397e2c4ae30f67a0128647967a7a92363c846c 100644 --- a/bridges/relays/client-substrate/src/chain.rs +++ b/bridges/relays/client-substrate/src/chain.rs @@ -15,7 +15,7 @@ // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. use bp_runtime::Chain as ChainBase; -use frame_support::Parameter; +use frame_support::{weights::WeightToFeePolynomial, Parameter}; use jsonrpsee_ws_client::{DeserializeOwned, Serialize}; use num_traits::{Bounded, CheckedSub, SaturatingAdd, Zero}; use sp_core::{storage::StorageKey, Pair}; @@ -77,12 +77,17 @@ pub trait Chain: ChainBase + Clone { + SaturatingAdd + Zero + std::convert::TryFrom<sp_core::U256>; + + /// Type that is used by the chain, to convert from weight to fee. + type WeightToFee: WeightToFeePolynomial<Balance = Self::Balance>; } /// Balance type used by the chain pub type BalanceOf<C> = <C as Chain>::Balance; /// Index type used by the chain pub type IndexOf<C> = <C as Chain>::Index; +/// Weight-to-Fee type used by the chain +pub type WeightToFeeOf<C> = <C as Chain>::WeightToFee; /// Substrate-based chain with `frame_system::Config::AccountData` set to /// the `pallet_balances::AccountData<Balance>`. diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index b63e7f30a6cc208febeb3a837e19d387d26ff498..9fb778651b475af5e6d216a94c10add1afb73bb6 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -310,23 +310,24 @@ impl<C: Chain> Client<C> { } /// Estimate fee that will be spent on given extrinsic. - pub async fn estimate_extrinsic_fee(&self, transaction: Bytes) -> Result<C::Balance> { + pub async fn estimate_extrinsic_fee(&self, transaction: Bytes) -> Result<InclusionFee<C::Balance>> { self.jsonrpsee_execute(move |client| async move { let fee_details = Substrate::<C>::payment_query_fee_details(&*client, transaction, None).await?; let inclusion_fee = fee_details .inclusion_fee - .map(|inclusion_fee| { - InclusionFee { - base_fee: C::Balance::try_from(inclusion_fee.base_fee.into_u256()) - .unwrap_or_else(|_| C::Balance::max_value()), - len_fee: C::Balance::try_from(inclusion_fee.len_fee.into_u256()) - .unwrap_or_else(|_| C::Balance::max_value()), - adjusted_weight_fee: C::Balance::try_from(inclusion_fee.adjusted_weight_fee.into_u256()) - .unwrap_or_else(|_| C::Balance::max_value()), - } - .inclusion_fee() + .map(|inclusion_fee| InclusionFee { + base_fee: C::Balance::try_from(inclusion_fee.base_fee.into_u256()) + .unwrap_or_else(|_| C::Balance::max_value()), + len_fee: C::Balance::try_from(inclusion_fee.len_fee.into_u256()) + .unwrap_or_else(|_| C::Balance::max_value()), + adjusted_weight_fee: C::Balance::try_from(inclusion_fee.adjusted_weight_fee.into_u256()) + .unwrap_or_else(|_| C::Balance::max_value()), }) - .unwrap_or_else(Zero::zero); + .unwrap_or_else(|| InclusionFee { + base_fee: Zero::zero(), + len_fee: Zero::zero(), + adjusted_weight_fee: Zero::zero(), + }); Ok(inclusion_fee) }) .await diff --git a/bridges/relays/client-substrate/src/guard.rs b/bridges/relays/client-substrate/src/guard.rs index 37a7c2aa275c5b62e265cae7229566e776475a5b..f7df7dbb05c5b26a0b9d4ac359d88c8e879c35a7 100644 --- a/bridges/relays/client-substrate/src/guard.rs +++ b/bridges/relays/client-substrate/src/guard.rs @@ -194,6 +194,7 @@ mod tests { sp_runtime::generic::SignedBlock<sp_runtime::generic::Block<Self::Header, sp_runtime::OpaqueExtrinsic>>; type Call = (); type Balance = u32; + type WeightToFee = frame_support::weights::IdentityFee<u32>; } impl ChainWithBalances for TestChain { diff --git a/bridges/relays/client-substrate/src/lib.rs b/bridges/relays/client-substrate/src/lib.rs index 6aa319c036773940530682ef1a6613981de31629..be1835df3227dc85d07b77e71365b89fb7238edc 100644 --- a/bridges/relays/client-substrate/src/lib.rs +++ b/bridges/relays/client-substrate/src/lib.rs @@ -31,7 +31,9 @@ pub mod metrics; use std::time::Duration; -pub use crate::chain::{BalanceOf, BlockWithJustification, Chain, ChainWithBalances, IndexOf, TransactionSignScheme}; +pub use crate::chain::{ + BalanceOf, BlockWithJustification, Chain, ChainWithBalances, IndexOf, TransactionSignScheme, WeightToFeeOf, +}; pub use crate::client::{Client, JustificationsSubscription, OpaqueGrandpaAuthoritiesSet}; pub use crate::error::{Error, Result}; pub use crate::sync_header::SyncHeader; diff --git a/bridges/relays/client-westend/src/lib.rs b/bridges/relays/client-westend/src/lib.rs index fefab00c5615f41fe103f3f70a1cc44797ece907..b33be7421cf44fe7e26ed46d561c1157a9f4ae86 100644 --- a/bridges/relays/client-westend/src/lib.rs +++ b/bridges/relays/client-westend/src/lib.rs @@ -50,6 +50,7 @@ impl Chain for Westend { type SignedBlock = bp_westend::SignedBlock; type Call = bp_westend::Call; type Balance = bp_westend::Balance; + type WeightToFee = bp_westend::WeightToFee; } impl ChainWithBalances for Westend { diff --git a/bridges/relays/client-wococo/src/lib.rs b/bridges/relays/client-wococo/src/lib.rs index 4b3bdd7d84d1437a3c5db21dd53884d58582c128..03cb4d71563f7701b34de12c051081f7c57ac0b1 100644 --- a/bridges/relays/client-wococo/src/lib.rs +++ b/bridges/relays/client-wococo/src/lib.rs @@ -52,6 +52,7 @@ impl Chain for Wococo { type SignedBlock = bp_wococo::SignedBlock; type Call = crate::runtime::Call; type Balance = bp_wococo::Balance; + type WeightToFee = bp_wococo::WeightToFee; } impl ChainWithBalances for Wococo { diff --git a/bridges/relays/lib-substrate-relay/Cargo.toml b/bridges/relays/lib-substrate-relay/Cargo.toml index 8a341c45ad62e1d10cfe4b1507935bd5285690ea..7ab81b786b5d033590c7e7c17d39bad9ef6019cc 100644 --- a/bridges/relays/lib-substrate-relay/Cargo.toml +++ b/bridges/relays/lib-substrate-relay/Cargo.toml @@ -40,8 +40,9 @@ sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } [dev-dependencies] -relay-millau-client = { path = "../client-millau" } -relay-rialto-client = { path = "../client-rialto" } -bp-rialto = { path = "../../primitives/chain-rialto" } bp-millau = { path = "../../primitives/chain-millau" } +bp-rococo = { path = "../../primitives/chain-rococo" } +bp-wococo = { path = "../../primitives/chain-wococo" } +relay-rococo-client = { path = "../client-rococo" } +relay-wococo-client = { path = "../client-wococo" } rialto-runtime = { path = "../../bin/rialto/runtime" } diff --git a/bridges/relays/lib-substrate-relay/src/messages_lane.rs b/bridges/relays/lib-substrate-relay/src/messages_lane.rs index 0b648e8cc83291d2216438c8e43d57f6eb4bbc92..4614a50883a020b94db670b223b8d54b9a68490b 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_lane.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_lane.rs @@ -88,6 +88,13 @@ pub trait SubstrateMessageLane: 'static + Clone + Send + Sync { /// Name of the messages pallet as it is declared in the `construct_runtime!()` at target chain. const MESSAGE_PALLET_NAME_AT_TARGET: &'static str; + /// Extra weight of the delivery transaction at the target chain, that is paid to cover + /// dispatch fee payment. + /// + /// If dispatch fee is paid at the source chain, then this weight is refunded by the + /// delivery transaction. + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight; + /// Source chain. type SourceChain: Chain; /// Target chain. diff --git a/bridges/relays/lib-substrate-relay/src/messages_source.rs b/bridges/relays/lib-substrate-relay/src/messages_source.rs index 9a98b9b1d48c3ce390d6b6c19a4b22d490af92ce..d6e92d1e51c4f7c0e8d797781e79ed838baa19fd 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_source.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_source.rs @@ -24,7 +24,6 @@ use crate::on_demand_headers::OnDemandHeadersRelay; use async_trait::async_trait; use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState}; -use bp_runtime::messages::DispatchFeePayment; use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; @@ -43,7 +42,10 @@ use relay_substrate_client::{ }; use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId}; use sp_core::Bytes; -use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; +use sp_runtime::{ + traits::{AtLeast32BitUnsigned, Header as HeaderT}, + DeserializeOwned, +}; use std::ops::RangeInclusive; /// Intermediate message proof returned by the source Substrate node. Includes everything @@ -121,6 +123,7 @@ where >, <P::MessageLane as MessageLane>::TargetHeaderNumber: Decode, <P::MessageLane as MessageLane>::TargetHeaderHash: Decode, + <P::MessageLane as MessageLane>::SourceChainBalance: AtLeast32BitUnsigned, { async fn state(&self) -> Result<SourceClientState<P::MessageLane>, SubstrateError> { // we can't continue to deliver confirmations if source node is out of sync, because @@ -264,6 +267,7 @@ where prepare_dummy_messages_delivery_proof::<P::SourceChain, P::TargetChain>(), )) .await + .map(|fee| fee.inclusion_fee()) .unwrap_or_else(|_| BalanceOf::<P::SourceChain>::max_value()) } } @@ -397,7 +401,7 @@ fn make_message_details_map<C: Chain>( dispatch_weight: details.dispatch_weight, size: details.size as _, reward: details.delivery_and_dispatch_fee, - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, + dispatch_fee_payment: details.dispatch_fee_payment, }, ); expected_nonce = details.nonce + 1; @@ -411,12 +415,12 @@ fn make_message_details_map<C: Chain>( mod tests { use super::*; use bp_runtime::messages::DispatchFeePayment; - use relay_millau_client::Millau; - use relay_rialto_client::Rialto; + use relay_rococo_client::Rococo; + use relay_wococo_client::Wococo; fn message_details_from_rpc( nonces: RangeInclusive<MessageNonce>, - ) -> Vec<bp_messages::MessageDetails<bp_rialto::Balance>> { + ) -> Vec<bp_messages::MessageDetails<bp_wococo::Balance>> { nonces .into_iter() .map(|nonce| bp_messages::MessageDetails { @@ -432,7 +436,7 @@ mod tests { #[test] fn make_message_details_map_succeeds_if_no_messages_are_missing() { assert_eq!( - make_message_details_map::<relay_rialto_client::Rialto>(message_details_from_rpc(1..=3), 1..=3,).unwrap(), + make_message_details_map::<Wococo>(message_details_from_rpc(1..=3), 1..=3,).unwrap(), vec![ ( 1, @@ -470,7 +474,7 @@ mod tests { #[test] fn make_message_details_map_succeeds_if_head_messages_are_missing() { assert_eq!( - make_message_details_map::<relay_rialto_client::Rialto>(message_details_from_rpc(2..=3), 1..=3,).unwrap(), + make_message_details_map::<Wococo>(message_details_from_rpc(2..=3), 1..=3,).unwrap(), vec![ ( 2, @@ -501,7 +505,7 @@ mod tests { let mut message_details_from_rpc = message_details_from_rpc(1..=3); message_details_from_rpc.remove(1); assert!(matches!( - make_message_details_map::<relay_rialto_client::Rialto>(message_details_from_rpc, 1..=3,), + make_message_details_map::<Wococo>(message_details_from_rpc, 1..=3,), Err(SubstrateError::Custom(_)) )); } @@ -509,7 +513,7 @@ mod tests { #[test] fn make_message_details_map_fails_if_tail_messages_are_missing() { assert!(matches!( - make_message_details_map::<relay_rialto_client::Rialto>(message_details_from_rpc(1..=2), 1..=3,), + make_message_details_map::<Wococo>(message_details_from_rpc(1..=2), 1..=3,), Err(SubstrateError::Custom(_)) )); } @@ -517,15 +521,15 @@ mod tests { #[test] fn make_message_details_map_fails_if_all_messages_are_missing() { assert!(matches!( - make_message_details_map::<relay_rialto_client::Rialto>(vec![], 1..=3), + make_message_details_map::<Wococo>(vec![], 1..=3), Err(SubstrateError::Custom(_)) )); } #[test] fn prepare_dummy_messages_delivery_proof_works() { - let expected_minimal_size = Rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE + Millau::STORAGE_PROOF_OVERHEAD; - let dummy_proof = prepare_dummy_messages_delivery_proof::<Rialto, Millau>(); + let expected_minimal_size = Wococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE + Rococo::STORAGE_PROOF_OVERHEAD; + let dummy_proof = prepare_dummy_messages_delivery_proof::<Wococo, Rococo>(); assert!( dummy_proof.1.encode().len() as u32 > expected_minimal_size, "Expected proof size at least {}. Got: {}", diff --git a/bridges/relays/lib-substrate-relay/src/messages_target.rs b/bridges/relays/lib-substrate-relay/src/messages_target.rs index 8e460769156ad5ecfe008eb763245e77d3195fcf..20eb7a54b383dc65bbd9902159029befc3074d0d 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_target.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_target.rs @@ -29,7 +29,7 @@ use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; use codec::{Decode, Encode}; -use frame_support::weights::Weight; +use frame_support::weights::{Weight, WeightToFeePolynomial}; use messages_relay::message_lane::MessageLane; use messages_relay::{ message_lane::{SourceHeaderIdOf, TargetHeaderIdOf}, @@ -37,11 +37,11 @@ use messages_relay::{ }; use num_traits::{Bounded, Zero}; use relay_substrate_client::{ - BalanceOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, HeaderOf, IndexOf, + BalanceOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, HeaderOf, IndexOf, WeightToFeeOf, }; use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId}; use sp_core::Bytes; -use sp_runtime::{DeserializeOwned, FixedPointNumber, FixedU128}; +use sp_runtime::{traits::Saturating, DeserializeOwned, FixedPointNumber, FixedU128}; use std::{convert::TryFrom, ops::RangeInclusive}; /// Message receiving proof returned by the target Substrate node. @@ -118,7 +118,6 @@ where BlockNumberOf<P::TargetChain>: Copy, HeaderOf<P::TargetChain>: DeserializeOwned, BlockNumberOf<P::TargetChain>: BlockNumberBase, - P::MessageLane: MessageLane< MessagesProof = SubstrateMessagesProof<P::SourceChain>, MessagesReceivingProof = SubstrateMessagesReceivingProof<P::TargetChain>, @@ -244,6 +243,7 @@ where async fn estimate_delivery_transaction_in_source_tokens( &self, nonces: RangeInclusive<MessageNonce>, + total_prepaid_nonces: MessageNonce, total_dispatch_weight: Weight, total_size: u32, ) -> Result<<P::MessageLane as MessageLane>::SourceChainBalance, SubstrateError> { @@ -258,27 +258,88 @@ where P::SourceChain::NAME, )) })?; + + // Prepare 'dummy' delivery transaction - we only care about its length and dispatch weight. + let delivery_tx = self.lane.make_messages_delivery_transaction( + Zero::zero(), + HeaderId(Default::default(), Default::default()), + nonces.clone(), + prepare_dummy_messages_proof::<P::SourceChain>(nonces.clone(), total_dispatch_weight, total_size), + ); + let delivery_tx_fee = self.client.estimate_extrinsic_fee(delivery_tx).await?; + let inclusion_fee_in_target_tokens = delivery_tx_fee.inclusion_fee(); + + // The pre-dispatch cost of delivery transaction includes additional fee to cover dispatch fee payment + // (Currency::transfer in regular deployment). But if message dispatch has already been paid + // at the Source chain, the delivery transaction will refund relayer with this additional cost. + // But `estimate_extrinsic_fee` obviously just returns pre-dispatch cost of the transaction. So + // if transaction delivers prepaid message, then it may happen that pre-dispatch cost is larger + // than reward and `Rational` relayer will refuse to deliver this message. + // + // The most obvious solution would be to deduct total weight of dispatch fee payments from the + // `total_dispatch_weight` and use regular `estimate_extrinsic_fee` call. But what if + // `total_dispatch_weight` is less than total dispatch fee payments weight? Weight is strictly + // positive, so we can't use this option. + // + // Instead we'll be directly using `WeightToFee` and `NextFeeMultiplier` of the Target chain. + // This requires more knowledge of the Target chain, but seems there's no better way to solve + // this now. + let expected_refund_in_target_tokens = if total_prepaid_nonces != 0 { + const WEIGHT_DIFFERENCE: Weight = 100; + + let larger_dispatch_weight = total_dispatch_weight.saturating_add(WEIGHT_DIFFERENCE); + let larger_delivery_tx_fee = self + .client + .estimate_extrinsic_fee(self.lane.make_messages_delivery_transaction( + Zero::zero(), + HeaderId(Default::default(), Default::default()), + nonces.clone(), + prepare_dummy_messages_proof::<P::SourceChain>(nonces.clone(), larger_dispatch_weight, total_size), + )) + .await?; + + compute_prepaid_messages_refund::<P>( + total_prepaid_nonces, + compute_fee_multiplier::<P::TargetChain>( + delivery_tx_fee.adjusted_weight_fee, + total_dispatch_weight, + larger_delivery_tx_fee.adjusted_weight_fee, + larger_dispatch_weight, + ), + ) + } else { + Zero::zero() + }; + + let delivery_fee_in_source_tokens = convert_target_tokens_to_source_tokens::<P::SourceChain, P::TargetChain>( + FixedU128::from_float(conversion_rate), + inclusion_fee_in_target_tokens.saturating_sub(expected_refund_in_target_tokens), + ); + log::trace!( target: "bridge", - "Using conversion rate {} when converting from {} tokens to {} tokens", - conversion_rate, - P::TargetChain::NAME, - P::SourceChain::NAME, + "Estimated {} -> {} messages delivery transaction.\n\t\ + Total nonces: {:?}\n\t\ + Prepaid messages: {}\n\t\ + Total messages size: {}\n\t\ + Total messages dispatch weight: {}\n\t\ + Inclusion fee (in {1} tokens): {:?}\n\t\ + Expected refund (in {1} tokens): {:?}\n\t\ + {1} -> {0} conversion rate: {:?}\n\t\ + Expected delivery tx fee (in {0} tokens): {:?}", + P::SourceChain::NAME, + P::TargetChain::NAME, + nonces, + total_prepaid_nonces, + total_size, + total_dispatch_weight, + inclusion_fee_in_target_tokens, + expected_refund_in_target_tokens, + conversion_rate, + delivery_fee_in_source_tokens, ); - Ok( - convert_target_tokens_to_source_tokens::<P::SourceChain, P::TargetChain>( - FixedU128::from_float(conversion_rate), - self.client - .estimate_extrinsic_fee(self.lane.make_messages_delivery_transaction( - Zero::zero(), - HeaderId(Default::default(), Default::default()), - nonces.clone(), - prepare_dummy_messages_proof::<P::SourceChain>(nonces, total_dispatch_weight, total_size), - )) - .await - .unwrap_or_else(|_| <P::TargetChain as Chain>::Balance::max_value()), - ), - ) + + Ok(delivery_fee_in_source_tokens) } } @@ -316,17 +377,110 @@ where .unwrap_or_else(|_| SC::Balance::max_value()) } +/// Compute fee multiplier that is used by the chain, given couple of fees for transactions +/// that are only differ in dispatch weights. +/// +/// This function assumes that standard transaction payment pallet is used by the chain. +/// The only fee component that depends on dispatch weight is the `adjusted_weight_fee`. +/// +/// **WARNING**: this functions will only be accurate if weight-to-fee conversion function +/// is linear. For non-linear polynomials the error will grow with `weight_difference` growth. +/// So better to use smaller differences. +fn compute_fee_multiplier<C: Chain>( + smaller_adjusted_weight_fee: BalanceOf<C>, + smaller_tx_weight: Weight, + larger_adjusted_weight_fee: BalanceOf<C>, + larger_tx_weight: Weight, +) -> FixedU128 { + let adjusted_weight_fee_difference = larger_adjusted_weight_fee.saturating_sub(smaller_adjusted_weight_fee); + let smaller_tx_unadjusted_weight_fee = WeightToFeeOf::<C>::calc(&smaller_tx_weight); + let larger_tx_unadjusted_weight_fee = WeightToFeeOf::<C>::calc(&larger_tx_weight); + FixedU128::saturating_from_rational( + adjusted_weight_fee_difference, + larger_tx_unadjusted_weight_fee.saturating_sub(smaller_tx_unadjusted_weight_fee), + ) +} + +/// Compute fee that will be refunded to the relayer because dispatch of `total_prepaid_nonces` +/// messages has been paid at the source chain. +fn compute_prepaid_messages_refund<P: SubstrateMessageLane>( + total_prepaid_nonces: MessageNonce, + fee_multiplier: FixedU128, +) -> BalanceOf<P::TargetChain> { + fee_multiplier.saturating_mul_int(WeightToFeeOf::<P::TargetChain>::calc( + &P::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN.saturating_mul(total_prepaid_nonces), + )) +} + #[cfg(test)] mod tests { use super::*; - use relay_millau_client::Millau; - use relay_rialto_client::Rialto; + use relay_rococo_client::{Rococo, SigningParams as RococoSigningParams}; + use relay_wococo_client::{SigningParams as WococoSigningParams, Wococo}; + + #[derive(Clone)] + struct TestSubstrateMessageLane; + + impl SubstrateMessageLane for TestSubstrateMessageLane { + type MessageLane = crate::messages_lane::SubstrateMessageLaneToSubstrate< + Rococo, + RococoSigningParams, + Wococo, + WococoSigningParams, + >; + + const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = ""; + const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str = ""; + const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = ""; + + const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = ""; + const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str = ""; + const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = ""; + + const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = ""; + const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = ""; + + const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = ""; + const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = ""; + + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = 100_000; + + type SourceChain = Rococo; + type TargetChain = Wococo; + + fn source_transactions_author(&self) -> bp_rococo::AccountId { + unreachable!() + } + + fn make_messages_receiving_proof_transaction( + &self, + _transaction_nonce: <Rococo as Chain>::Index, + _generated_at_block: TargetHeaderIdOf<Self::MessageLane>, + _proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof, + ) -> Bytes { + unreachable!() + } + + fn target_transactions_author(&self) -> bp_wococo::AccountId { + unreachable!() + } + + fn make_messages_delivery_transaction( + &self, + _transaction_nonce: <Wococo as Chain>::Index, + _generated_at_header: SourceHeaderIdOf<Self::MessageLane>, + _nonces: RangeInclusive<MessageNonce>, + _proof: <Self::MessageLane as MessageLane>::MessagesProof, + ) -> Bytes { + unreachable!() + } + } #[test] fn prepare_dummy_messages_proof_works() { const DISPATCH_WEIGHT: Weight = 1_000_000; const SIZE: u32 = 1_000; - let dummy_proof = prepare_dummy_messages_proof::<Rialto>(1..=10, DISPATCH_WEIGHT, SIZE); + let dummy_proof = prepare_dummy_messages_proof::<Rococo>(1..=10, DISPATCH_WEIGHT, SIZE); assert_eq!(dummy_proof.0, DISPATCH_WEIGHT); assert!( dummy_proof.1.encode().len() as u32 > SIZE, @@ -339,16 +493,47 @@ mod tests { #[test] fn convert_target_tokens_to_source_tokens_works() { assert_eq!( - convert_target_tokens_to_source_tokens::<Rialto, Millau>((150, 100).into(), 1_000), + convert_target_tokens_to_source_tokens::<Rococo, Wococo>((150, 100).into(), 1_000), 1_500 ); assert_eq!( - convert_target_tokens_to_source_tokens::<Rialto, Millau>((50, 100).into(), 1_000), + convert_target_tokens_to_source_tokens::<Rococo, Wococo>((50, 100).into(), 1_000), 500 ); assert_eq!( - convert_target_tokens_to_source_tokens::<Rialto, Millau>((100, 100).into(), 1_000), + convert_target_tokens_to_source_tokens::<Rococo, Wococo>((100, 100).into(), 1_000), 1_000 ); } + + #[test] + fn compute_fee_multiplier_returns_sane_results() { + let multiplier = FixedU128::saturating_from_rational(1, 1000); + + let smaller_weight = 1_000_000; + let smaller_adjusted_weight_fee = multiplier.saturating_mul_int(WeightToFeeOf::<Rococo>::calc(&smaller_weight)); + + let larger_weight = smaller_weight + 200_000; + let larger_adjusted_weight_fee = multiplier.saturating_mul_int(WeightToFeeOf::<Rococo>::calc(&larger_weight)); + + assert_eq!( + compute_fee_multiplier::<Rococo>( + smaller_adjusted_weight_fee, + smaller_weight, + larger_adjusted_weight_fee, + larger_weight, + ), + multiplier, + ); + } + + #[test] + fn compute_prepaid_messages_refund_returns_sane_results() { + assert!( + compute_prepaid_messages_refund::<TestSubstrateMessageLane>( + 10, + FixedU128::saturating_from_rational(110, 100), + ) > (10 * TestSubstrateMessageLane::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN).into() + ); + } } diff --git a/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs b/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs index 2f8441dbfc986ab67edb5ad6b839c1f98935cef6..8973d4fe3ec08a768d9419005124c7f7eb3ce427 100644 --- a/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs +++ b/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs @@ -420,10 +420,10 @@ fn on_demand_headers_relay_name<SourceChain: Chain, TargetChain: Chain>() -> Str mod tests { use super::*; - type TestChain = relay_millau_client::Millau; + type TestChain = relay_rococo_client::Rococo; - const AT_SOURCE: Option<bp_millau::BlockNumber> = Some(10); - const AT_TARGET: Option<bp_millau::BlockNumber> = Some(1); + const AT_SOURCE: Option<bp_rococo::BlockNumber> = Some(10); + const AT_TARGET: Option<bp_rococo::BlockNumber> = Some(1); #[async_std::test] async fn mandatory_headers_scan_range_selects_range_if_too_many_headers_are_missing() { diff --git a/bridges/relays/messages/src/message_lane_loop.rs b/bridges/relays/messages/src/message_lane_loop.rs index 84b0c72408dfa691548088685e52a787e6a7a4f6..049b6e7b5f799581b472ebd2ff22ba3b41ade726 100644 --- a/bridges/relays/messages/src/message_lane_loop.rs +++ b/bridges/relays/messages/src/message_lane_loop.rs @@ -210,6 +210,7 @@ pub trait TargetClient<P: MessageLane>: RelayClient { async fn estimate_delivery_transaction_in_source_tokens( &self, nonces: RangeInclusive<MessageNonce>, + total_prepaid_nonces: MessageNonce, total_dispatch_weight: Weight, total_size: u32, ) -> Result<P::SourceChainBalance, Self::Error>; @@ -773,6 +774,7 @@ pub(crate) mod tests { async fn estimate_delivery_transaction_in_source_tokens( &self, nonces: RangeInclusive<MessageNonce>, + _total_prepaid_nonces: MessageNonce, total_dispatch_weight: Weight, total_size: u32, ) -> Result<TestSourceChainBalance, TestError> { diff --git a/bridges/relays/messages/src/message_race_delivery.rs b/bridges/relays/messages/src/message_race_delivery.rs index 2e94bbee8b1c455daee1d2db02a1446f3277ef75..8a5f8d7df260dc7a2d958ce1f5a01859a302a4a5 100644 --- a/bridges/relays/messages/src/message_race_delivery.rs +++ b/bridges/relays/messages/src/message_race_delivery.rs @@ -561,6 +561,7 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>( let mut selected_weight: Weight = 0; let mut selected_unpaid_weight: Weight = 0; + let mut selected_prepaid_nonces = 0; let mut selected_size: u32 = 0; let mut selected_count: MessageNonce = 0; let mut selected_reward = P::SourceChainBalance::zero(); @@ -570,6 +571,8 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>( let mut total_confirmations_cost = P::SourceChainBalance::zero(); let mut total_cost = P::SourceChainBalance::zero(); + let hard_selected_begin_nonce = nonces_queue[nonces_queue_range.start].1.begin(); + // technically, multiple confirmations will be delivered in a single transaction, // meaning less loses for relayer. But here we don't know the final relayer yet, so // we're adding a separate transaction for every message. Normally, this cost is covered @@ -637,8 +640,12 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>( // dispatch origin account AND reward is not covering this fee. // // So in the latter case we're not adding the dispatch weight to the delivery transaction weight. + let mut new_selected_prepaid_nonces = selected_prepaid_nonces; let new_selected_unpaid_weight = match details.dispatch_fee_payment { - DispatchFeePayment::AtSourceChain => selected_unpaid_weight.saturating_add(details.dispatch_weight), + DispatchFeePayment::AtSourceChain => { + new_selected_prepaid_nonces += 1; + selected_unpaid_weight.saturating_add(details.dispatch_weight) + } DispatchFeePayment::AtTargetChain => selected_unpaid_weight, }; @@ -651,7 +658,8 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>( RelayerMode::Rational => { let delivery_transaction_cost = lane_target_client .estimate_delivery_transaction_in_source_tokens( - 0..=(new_selected_count as MessageNonce - 1), + hard_selected_begin_nonce..=(hard_selected_begin_nonce + index as MessageNonce), + new_selected_prepaid_nonces, new_selected_unpaid_weight, new_selected_size as u32, ) @@ -711,11 +719,11 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>( hard_selected_count = index + 1; selected_weight = new_selected_weight; selected_unpaid_weight = new_selected_unpaid_weight; + selected_prepaid_nonces = new_selected_prepaid_nonces; selected_size = new_selected_size; selected_count = new_selected_count; } - let hard_selected_begin_nonce = nonces_queue[nonces_queue_range.start].1.begin(); if hard_selected_count != soft_selected_count { let hard_selected_end_nonce = hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1; let soft_selected_begin_nonce = hard_selected_begin_nonce;