diff --git a/bridges/primitives/chain-rococo/Cargo.toml b/bridges/primitives/chain-rococo/Cargo.toml index b97e8d9d1a..2f806b3632 100644 --- a/bridges/primitives/chain-rococo/Cargo.toml +++ b/bridges/primitives/chain-rococo/Cargo.toml @@ -8,6 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] parity-scale-codec = { version = "2.0.0", default-features = false, features = ["derive"] } +smallvec = "1.6" # Bridge Dependencies bp-header-chain = { path = "../header-chain", default-features = false } @@ -16,6 +17,7 @@ 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 +30,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-rococo/src/lib.rs b/bridges/primitives/chain-rococo/src/lib.rs index 20a5cd6658..7940b40d43 100644 --- a/bridges/primitives/chain-rococo/src/lib.rs +++ b/bridges/primitives/chain-rococo/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; @@ -43,6 +44,23 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { transaction_version: 0, }; +// NOTE: This needs to be kept up to date with the Rococo runtime found in the Polkadot repo. +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + fn polynomial() -> WeightToFeeCoefficients { + const CENTS: Balance = 1_000_000_000_000 / 100; + 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, + }] + } +} + /// Rococo Runtime `Call` enum. /// /// The enum represents a subset of possible `Call`s we can send to Rococo chain. diff --git a/bridges/primitives/chain-wococo/Cargo.toml b/bridges/primitives/chain-wococo/Cargo.toml index ecf783a51e..ffa75b5a45 100644 --- a/bridges/primitives/chain-wococo/Cargo.toml +++ b/bridges/primitives/chain-wococo/Cargo.toml @@ -13,13 +13,13 @@ parity-scale-codec = { version = "2.0.0", default-features = false, features = [ 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-rococo = { path = "../chain-rococo", default-features = false } bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies 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 } -sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [features] default = ["std"] @@ -28,9 +28,9 @@ std = [ "bp-messages/std", "bp-polkadot-core/std", "bp-runtime/std", + "bp-rococo/std", "parity-scale-codec/std", "sp-api/std", "sp-runtime/std", "sp-std/std", - "sp-version/std", ] diff --git a/bridges/primitives/chain-wococo/src/lib.rs b/bridges/primitives/chain-wococo/src/lib.rs index 94e3ecbf2e..3aaa5a730a 100644 --- a/bridges/primitives/chain-wococo/src/lib.rs +++ b/bridges/primitives/chain-wococo/src/lib.rs @@ -23,26 +23,16 @@ use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; use bp_runtime::Chain; use sp_std::prelude::*; -use sp_version::RuntimeVersion; pub use bp_polkadot_core::*; +// Rococo runtime = Wococo runtime +pub use bp_rococo::{WeightToFee, VERSION}; /// Wococo Chain pub type Wococo = PolkadotLike; pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic; -// NOTE: This needs to be kept up to date with the Rococo runtime found in the Polkadot repo. -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: sp_version::create_runtime_str!("rococo"), - impl_name: sp_version::create_runtime_str!("parity-rococo-v1.6"), - authoring_version: 0, - spec_version: 9004, - impl_version: 0, - apis: sp_version::create_apis_vec![[]], - transaction_version: 0, -}; - /// Wococo Runtime `Call` enum. /// /// The enum represents a subset of possible `Call`s we can send to Rococo chain. diff --git a/bridges/primitives/polkadot-core/src/lib.rs b/bridges/primitives/polkadot-core/src/lib.rs index c9858c0820..a1619b27bc 100644 --- a/bridges/primitives/polkadot-core/src/lib.rs +++ b/bridges/primitives/polkadot-core/src/lib.rs @@ -22,7 +22,7 @@ use frame_support::{ dispatch::Dispatchable, parameter_types, weights::{ - constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_PER_SECOND}, + constants::{BlockExecutionWeight, WEIGHT_PER_SECOND}, DispatchClass, Weight, }, Blake2_128Concat, RuntimeDebug, StorageHasher, Twox128, @@ -33,13 +33,13 @@ use sp_core::Hasher as HasherT; use sp_runtime::{ generic, traits::{BlakeTwo256, IdentifyAccount, Verify}, - MultiAddress, MultiSignature, OpaqueExtrinsic, Perbill, + MultiAddress, MultiSignature, OpaqueExtrinsic, }; use sp_std::prelude::Vec; // Re-export's to avoid extra substrate dependencies in chain-specific crates. -pub use frame_support::Parameter; -pub use sp_runtime::traits::Convert; +pub use frame_support::{weights::constants::ExtrinsicBaseWeight, Parameter}; +pub use sp_runtime::{traits::Convert, Perbill}; /// Number of extra bytes (excluding size of storage value itself) of storage proof, built at /// Polkadot-like chain. This mostly depends on number of entries in the storage trie. diff --git a/bridges/relays/bin-substrate/Cargo.toml b/bridges/relays/bin-substrate/Cargo.toml index fc61d138fd..3bbf05f3b1 100644 --- a/bridges/relays/bin-substrate/Cargo.toml +++ b/bridges/relays/bin-substrate/Cargo.toml @@ -59,5 +59,6 @@ sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" } [dev-dependencies] -sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } hex-literal = "0.3" +pallet-bridge-grandpa = { path = "../../modules/grandpa" } +sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs b/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs index 4dfe800b2b..4e641aec74 100644 --- a/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs +++ b/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs @@ -16,12 +16,13 @@ //! Rococo-to-Wococo headers sync entrypoint. +use crate::chains::wococo_headers_to_rococo::MAXIMAL_BALANCE_DECREASE_PER_DAY; use crate::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate}; use bp_header_chain::justification::GrandpaJustification; use codec::Encode; use relay_rococo_client::{Rococo, SyncHeader as RococoSyncHeader}; -use relay_substrate_client::{Chain, Client, TransactionSignScheme}; +use relay_substrate_client::{Chain, TransactionSignScheme}; use relay_utils::metrics::MetricsParams; use relay_wococo_client::{SigningParams as WococoSigningParams, Wococo}; use sp_core::{Bytes, Pair}; @@ -38,11 +39,16 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo { crate::chains::add_polkadot_kusama_price_metrics::(params) } - fn start_relay_guards(target_client: &Client) { + fn start_relay_guards(&self) { relay_substrate_client::guard::abort_on_spec_version_change( - target_client.clone(), + self.target_client.clone(), bp_wococo::VERSION.spec_version, - ) + ); + relay_substrate_client::guard::abort_when_account_balance_decreased( + self.target_client.clone(), + self.transactions_author(), + MAXIMAL_BALANCE_DECREASE_PER_DAY, + ); } fn transactions_author(&self) -> bp_wococo::AccountId { diff --git a/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs b/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs index 3b6a478269..4cae33db31 100644 --- a/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs +++ b/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs @@ -21,11 +21,18 @@ use crate::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityT use bp_header_chain::justification::GrandpaJustification; use codec::Encode; use relay_rococo_client::{Rococo, SigningParams as RococoSigningParams}; -use relay_substrate_client::{Chain, Client, TransactionSignScheme}; +use relay_substrate_client::{Chain, TransactionSignScheme}; use relay_utils::metrics::MetricsParams; use relay_wococo_client::{SyncHeader as WococoSyncHeader, Wococo}; use sp_core::{Bytes, Pair}; +/// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat +/// relay as gone wild. +/// +/// See `maximal_balance_decrease_per_day_is_sane` test for details. +/// Note that this is in plancks, so this corresponds to `1500 UNITS`. +pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_rococo::Balance = 1_500_000_000_000_000; + /// Wococo-to-Rococo finality sync pipeline. pub(crate) type WococoFinalityToRococo = SubstrateFinalityToSubstrate; @@ -38,11 +45,16 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo { crate::chains::add_polkadot_kusama_price_metrics::(params) } - fn start_relay_guards(target_client: &Client) { + fn start_relay_guards(&self) { relay_substrate_client::guard::abort_on_spec_version_change( - target_client.clone(), + self.target_client.clone(), bp_rococo::VERSION.spec_version, - ) + ); + relay_substrate_client::guard::abort_when_account_balance_decreased( + self.target_client.clone(), + self.transactions_author(), + MAXIMAL_BALANCE_DECREASE_PER_DAY, + ); } fn transactions_author(&self) -> bp_rococo::AccountId { @@ -65,3 +77,41 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo { Bytes(transaction.encode()) } } + +#[cfg(test)] +mod tests { + use super::*; + use frame_support::weights::WeightToFeePolynomial; + use pallet_bridge_grandpa::weights::WeightInfo; + + #[test] + fn maximal_balance_decrease_per_day_is_sane() { + // Rococo/Wococo GRANDPA pallet weights. They're now using Rialto weights => using `RialtoWeight` is justified. + // + // Using Rialto runtime this is slightly incorrect, because `DbWeight` of Rococo/Wococo runtime may differ + // from the `DbWeight` of Rialto runtime. But now (and most probably forever) it is the same. + type RococoGrandpaPalletWeights = pallet_bridge_grandpa::weights::RialtoWeight; + + // The following formula shall not be treated as super-accurate - guard is to protect from mad relays, + // not to protect from over-average loses. + // + // Worst case: we're submitting proof for every source header. Since we submit every header, the number of + // headers in ancestry proof is near to 0 (let's round up to 2). And the number of authorities is 1024, + // which is (now) larger than on any existing chain => normally there'll be ~1024*2/3+1 commits. + const AVG_VOTES_ANCESTRIES_LEN: u32 = 2; + const AVG_PRECOMMITS_LEN: u32 = 1024 * 2 / 3 + 1; + let number_of_source_headers_per_day: bp_wococo::Balance = bp_wococo::DAYS as _; + let single_source_header_submit_call_weight = + RococoGrandpaPalletWeights::submit_finality_proof(AVG_VOTES_ANCESTRIES_LEN, AVG_PRECOMMITS_LEN); + // for simplicity - add extra weight for base tx fee + fee that is paid for the tx size + adjusted fee + let single_source_header_submit_tx_weight = single_source_header_submit_call_weight * 3 / 2; + let single_source_header_tx_cost = bp_rococo::WeightToFee::calc(&single_source_header_submit_tx_weight); + let maximal_expected_decrease = single_source_header_tx_cost * number_of_source_headers_per_day; + assert!( + MAXIMAL_BALANCE_DECREASE_PER_DAY >= maximal_expected_decrease, + "Maximal expected loss per day {} is larger than hardcoded {}", + maximal_expected_decrease, + MAXIMAL_BALANCE_DECREASE_PER_DAY, + ); + } +} diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers.rs b/bridges/relays/bin-substrate/src/cli/relay_headers.rs index abce9b3bfe..6806588288 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_headers.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_headers.rs @@ -97,15 +97,10 @@ impl RelayHeaders { let target_client = self.target.to_client::().await?; let target_sign = self.target_sign.to_keypair::()?; let metrics_params = Finality::customize_metrics(self.prometheus_params.into())?; - Finality::start_relay_guards(&target_client); + let finality = Finality::new(target_client.clone(), target_sign); + finality.start_relay_guards(); - crate::finality_pipeline::run( - Finality::new(target_client.clone(), target_sign), - source_client, - target_client, - metrics_params, - ) - .await + crate::finality_pipeline::run(finality, source_client, target_client, metrics_params).await }) } } diff --git a/bridges/relays/bin-substrate/src/finality_pipeline.rs b/bridges/relays/bin-substrate/src/finality_pipeline.rs index fd222f1c29..fe0004bbbd 100644 --- a/bridges/relays/bin-substrate/src/finality_pipeline.rs +++ b/bridges/relays/bin-substrate/src/finality_pipeline.rs @@ -51,7 +51,7 @@ pub trait SubstrateFinalitySyncPipeline: FinalitySyncPipeline { /// Different finality bridges may have different set of guards - e.g. on ephemeral chains we /// don't need version guards, on test chains we don't care that much about relayer account /// balance, ... So the implementation is left to the specific bridges. - fn start_relay_guards(_target_client: &Client) {} + fn start_relay_guards(&self) {} /// Returns id of account that we're using to sign transactions at target chain. fn transactions_author(&self) -> ::AccountId;