Introduce bridge relayers pallet (#1513)

* introduce relayers pallet

* add MessageDeliveryAndDispatchPaymentAdapter

* plug in pallet into test runtimes

* tests prototype

* tests for the relayers pallet

* tests for payment adapter

* mint_reward_payment_procedure_actually_mints_tokens

* benchmarks

* remove irrelevant todo

* remove redundant clone
This commit is contained in:
Svyatoslav Nikolsky
2022-07-20 13:10:59 +03:00
committed by Bastian Köcher
parent 1e0c2a6e02
commit 7590abd1a3
17 changed files with 862 additions and 43 deletions
+5
View File
@@ -20,6 +20,7 @@ bp-header-chain = { path = "../../../primitives/header-chain", default-features
bp-messages = { path = "../../../primitives/messages", default-features = false }
bp-millau = { path = "../../../primitives/chain-millau", default-features = false }
bp-polkadot-core = { path = "../../../primitives/polkadot-core", default-features = false }
bp-relayers = { path = "../../../primitives/relayers", default-features = false }
bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false }
bp-rialto-parachain = { path = "../../../primitives/chain-rialto-parachain", default-features = false }
bp-runtime = { path = "../../../primitives/runtime", default-features = false }
@@ -28,6 +29,7 @@ bridge-runtime-common = { path = "../../runtime-common", default-features = fals
pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false }
pallet-bridge-messages = { path = "../../../modules/messages", default-features = false }
pallet-bridge-parachains = { path = "../../../modules/parachains", default-features = false }
pallet-bridge-relayers = { path = "../../../modules/relayers", default-features = false }
pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false }
# Substrate Dependencies
@@ -89,6 +91,7 @@ std = [
"bp-messages/std",
"bp-millau/std",
"bp-polkadot-core/std",
"bp-relayers/std",
"bp-rialto/std",
"bp-rialto-parachain/std",
"bp-runtime/std",
@@ -107,6 +110,7 @@ std = [
"pallet-bridge-grandpa/std",
"pallet-bridge-messages/std",
"pallet-bridge-parachains/std",
"pallet-bridge-relayers/std",
"pallet-grandpa/std",
"pallet-mmr/std",
"pallet-randomness-collective-flip/std",
@@ -145,6 +149,7 @@ runtime-benchmarks = [
"libsecp256k1",
"pallet-bridge-messages/runtime-benchmarks",
"pallet-bridge-parachains/runtime-benchmarks",
"pallet-bridge-relayers/runtime-benchmarks",
"pallet-xcm/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
+22 -2
View File
@@ -388,6 +388,13 @@ parameter_types! {
pub const MaxRequests: u32 = 50;
}
impl pallet_bridge_relayers::Config for Runtime {
type Event = Event;
type Reward = Balance;
type PaymentProcedure = bp_relayers::MintReward<pallet_balances::Pallet<Runtime>, AccountId>;
type WeightInfo = ();
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
/// Number of headers to keep in benchmarks.
@@ -463,7 +470,12 @@ impl pallet_bridge_messages::Config<WithRialtoMessagesInstance> for Runtime {
type TargetHeaderChain = crate::rialto_messages::Rialto;
type LaneMessageVerifier = crate::rialto_messages::ToRialtoMessageVerifier;
type MessageDeliveryAndDispatchPayment = ();
type MessageDeliveryAndDispatchPayment =
pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter<
Runtime,
WithRialtoMessagesInstance,
GetDeliveryConfirmationTransactionFee,
>;
type OnMessageAccepted = ();
type OnDeliveryConfirmed = ();
@@ -494,7 +506,12 @@ impl pallet_bridge_messages::Config<WithRialtoParachainMessagesInstance> for Run
type TargetHeaderChain = crate::rialto_parachain_messages::RialtoParachain;
type LaneMessageVerifier = crate::rialto_parachain_messages::ToRialtoParachainMessageVerifier;
type MessageDeliveryAndDispatchPayment = ();
type MessageDeliveryAndDispatchPayment =
pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter<
Runtime,
WithRialtoParachainMessagesInstance,
GetDeliveryConfirmationTransactionFee,
>;
type OnMessageAccepted = ();
type OnDeliveryConfirmed = ();
@@ -558,6 +575,7 @@ construct_runtime!(
MmrLeaf: pallet_beefy_mmr::{Pallet, Storage},
// Rialto bridge modules.
BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event<T>},
BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage},
BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>},
@@ -937,6 +955,7 @@ impl_runtime_apis! {
list_benchmark!(list, extra, pallet_bridge_messages, MessagesBench::<Runtime, WithRialtoMessagesInstance>);
list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa);
list_benchmark!(list, extra, pallet_bridge_parachains, ParachainsBench::<Runtime, WithRialtoMessagesInstance>);
list_benchmark!(list, extra, pallet_bridge_relayers, BridgeRelayers);
let storage_info = AllPalletsWithSystem::storage_info();
@@ -1058,6 +1077,7 @@ impl_runtime_apis! {
pallet_bridge_parachains,
ParachainsBench::<Runtime, WithRialtoParachainsInstance>
);
add_benchmark!(params, batches, pallet_bridge_relayers, BridgeRelayers);
Ok(batches)
}
@@ -19,11 +19,13 @@ serde = { version = '1.0', optional = true, features = ['derive'] }
bp-messages = { path = "../../../primitives/messages", default-features = false }
bp-millau = { path = "../../../primitives/chain-millau", default-features = false }
bp-relayers = { path = "../../../primitives/relayers", default-features = false }
bp-runtime = { path = "../../../primitives/runtime", default-features = false }
bp-rialto-parachain = { path = "../../../primitives/chain-rialto-parachain", default-features = false }
bridge-runtime-common = { path = "../../runtime-common", default-features = false }
pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false }
pallet-bridge-messages = { path = "../../../modules/messages", default-features = false }
pallet-bridge-relayers = { path = "../../../modules/relayers", default-features = false }
# Substrate Dependencies
## Substrate Primitive Dependencies
@@ -89,6 +91,7 @@ runtime-benchmarks = [
std = [
"bp-messages/std",
"bp-millau/std",
"bp-relayers/std",
"bp-runtime/std",
"bp-rialto-parachain/std",
"bridge-runtime-common/std",
@@ -113,6 +116,7 @@ std = [
"pallet-balances/std",
"pallet-bridge-grandpa/std",
"pallet-bridge-messages/std",
"pallet-bridge-relayers/std",
"pallet-randomness-collective-flip/std",
"pallet-timestamp/std",
"pallet-sudo/std",
@@ -474,6 +474,13 @@ impl pallet_aura::Config for Runtime {
type MaxAuthorities = MaxAuthorities;
}
impl pallet_bridge_relayers::Config for Runtime {
type Event = Event;
type Reward = Balance;
type PaymentProcedure = bp_relayers::MintReward<pallet_balances::Pallet<Runtime>, AccountId>;
type WeightInfo = ();
}
parameter_types! {
/// This is a pretty unscientific cap.
///
@@ -530,7 +537,12 @@ impl pallet_bridge_messages::Config<WithMillauMessagesInstance> for Runtime {
type TargetHeaderChain = crate::millau_messages::Millau;
type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier;
type MessageDeliveryAndDispatchPayment = ();
type MessageDeliveryAndDispatchPayment =
pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter<
Runtime,
WithMillauMessagesInstance,
GetDeliveryConfirmationTransactionFee,
>;
type OnMessageAccepted = ();
type OnDeliveryConfirmed = ();
@@ -567,6 +579,7 @@ construct_runtime!(
DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event<T>} = 53,
// Millau bridge modules.
BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event<T>},
BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage},
BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>},
}
+4
View File
@@ -19,11 +19,13 @@ serde = { version = "1.0", optional = true, features = ["derive"] }
bp-header-chain = { path = "../../../primitives/header-chain", default-features = false }
bp-messages = { path = "../../../primitives/messages", default-features = false }
bp-millau = { path = "../../../primitives/chain-millau", default-features = false }
bp-relayers = { path = "../../../primitives/relayers", default-features = false }
bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false }
bp-runtime = { path = "../../../primitives/runtime", default-features = false }
bridge-runtime-common = { path = "../../runtime-common", default-features = false }
pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false }
pallet-bridge-messages = { path = "../../../modules/messages", default-features = false }
pallet-bridge-relayers = { path = "../../../modules/relayers", default-features = false }
pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false }
# Substrate Dependencies
@@ -89,6 +91,7 @@ std = [
"bp-header-chain/std",
"bp-messages/std",
"bp-millau/std",
"bp-relayers/std",
"bp-rialto/std",
"bp-runtime/std",
"bridge-runtime-common/std",
@@ -106,6 +109,7 @@ std = [
"pallet-beefy-mmr/std",
"pallet-bridge-grandpa/std",
"pallet-bridge-messages/std",
"pallet-bridge-relayers/std",
"pallet-grandpa/std",
"pallet-mmr/std",
"pallet-xcm/std",
+14 -1
View File
@@ -389,6 +389,13 @@ impl pallet_authority_discovery::Config for Runtime {
type MaxAuthorities = MaxAuthorities;
}
impl pallet_bridge_relayers::Config for Runtime {
type Event = Event;
type Reward = Balance;
type PaymentProcedure = bp_relayers::MintReward<pallet_balances::Pallet<Runtime>, AccountId>;
type WeightInfo = ();
}
parameter_types! {
/// This is a pretty unscientific cap.
///
@@ -447,7 +454,12 @@ impl pallet_bridge_messages::Config<WithMillauMessagesInstance> for Runtime {
type TargetHeaderChain = crate::millau_messages::Millau;
type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier;
type MessageDeliveryAndDispatchPayment = ();
type MessageDeliveryAndDispatchPayment =
pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter<
Runtime,
WithMillauMessagesInstance,
GetDeliveryConfirmationTransactionFee,
>;
type OnMessageAccepted = ();
type OnDeliveryConfirmed = ();
@@ -484,6 +496,7 @@ construct_runtime!(
MmrLeaf: pallet_beefy_mmr::{Pallet, Storage},
// Millau bridge modules.
BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event<T>},
BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage},
BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>},
+1
View File
@@ -40,6 +40,7 @@
#![allow(clippy::all)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use frame_support::{
traits::Get,
+40 -39
View File
@@ -17,7 +17,7 @@
//! Autogenerated weights for `pallet_bridge_messages`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-07-07, STEPS: 50, REPEAT: 20
//! DATE: 2022-07-20, STEPS: 50, REPEAT: 20
//! LOW RANGE: [], HIGH RANGE: []
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
//! CHAIN: Some("dev"), DB CACHE: 1024
@@ -40,6 +40,7 @@
#![allow(clippy::all)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use frame_support::{
traits::Get,
@@ -69,22 +70,22 @@ pub trait WeightInfo {
pub struct MillauWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for MillauWeight<T> {
fn send_minimal_message_worst_case() -> Weight {
(37_948_000 as Weight)
(38_822_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(10 as Weight))
}
fn send_1_kb_message_worst_case() -> Weight {
(39_158_000 as Weight)
(39_799_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(10 as Weight))
}
fn send_16_kb_message_worst_case() -> Weight {
(48_698_000 as Weight)
(47_772_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(10 as Weight))
}
fn maximal_increase_message_fee() -> Weight {
(2_919_864_000 as Weight)
(3_081_804_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
@@ -95,71 +96,71 @@ impl<T: frame_system::Config> WeightInfo for MillauWeight<T> {
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
fn receive_single_message_proof() -> Weight {
(25_992_000 as Weight)
(26_523_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
fn receive_two_messages_proof() -> Weight {
(37_016_000 as Weight)
(39_278_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
(31_589_000 as Weight)
(32_416_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
fn receive_single_message_proof_1_kb() -> Weight {
(25_962_000 as Weight)
(27_078_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
fn receive_single_message_proof_16_kb() -> Weight {
(74_385_000 as Weight)
(78_235_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
fn receive_single_prepaid_message_proof() -> Weight {
(26_159_000 as Weight)
(27_635_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
fn receive_delivery_proof_for_single_message() -> Weight {
(27_590_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
(34_576_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
(27_354_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
(37_318_000 as Weight)
.saturating_add(T::DbWeight::get().reads(6 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
(27_652_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
(41_245_000 as Weight)
.saturating_add(T::DbWeight::get().reads(7 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
fn send_minimal_message_worst_case() -> Weight {
(37_948_000 as Weight)
(38_822_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(10 as Weight))
}
fn send_1_kb_message_worst_case() -> Weight {
(39_158_000 as Weight)
(39_799_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(10 as Weight))
}
fn send_16_kb_message_worst_case() -> Weight {
(48_698_000 as Weight)
(47_772_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(10 as Weight))
}
fn maximal_increase_message_fee() -> Weight {
(2_919_864_000 as Weight)
(3_081_804_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
@@ -170,48 +171,48 @@ impl WeightInfo for () {
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
fn receive_single_message_proof() -> Weight {
(25_992_000 as Weight)
(26_523_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(4 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
fn receive_two_messages_proof() -> Weight {
(37_016_000 as Weight)
(39_278_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(4 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
(31_589_000 as Weight)
(32_416_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(4 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
fn receive_single_message_proof_1_kb() -> Weight {
(25_962_000 as Weight)
(27_078_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
fn receive_single_message_proof_16_kb() -> Weight {
(74_385_000 as Weight)
(78_235_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
fn receive_single_prepaid_message_proof() -> Weight {
(26_159_000 as Weight)
(27_635_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(4 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
fn receive_delivery_proof_for_single_message() -> Weight {
(27_590_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
(34_576_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
(27_354_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
(37_318_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(6 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
(27_652_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
(41_245_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(7 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
}
@@ -40,6 +40,7 @@
#![allow(clippy::all)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use frame_support::{
traits::Get,
+55
View File
@@ -0,0 +1,55 @@
[package]
name = "pallet-bridge-relayers"
description = "Module used to store relayer rewards and coordinate relayers set."
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false }
log = { version = "0.4.14", default-features = false }
num-traits = { version = "0.2", default-features = false }
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
# Bridge dependencies
bp-messages = { path = "../../primitives/messages", default-features = false }
bp-relayers = { path = "../../primitives/relayers", default-features = false }
pallet-bridge-messages = { path = "../messages", default-features = false }
# Substrate Dependencies
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
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 }
sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
[dev-dependencies]
bp-runtime = { path = "../../primitives/runtime" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
[features]
default = ["std"]
std = [
"bp-messages/std",
"bp-relayers/std",
"codec/std",
"frame-support/std",
"frame-system/std",
"log/std",
"num-traits/std",
"pallet-bridge-messages/std",
"scale-info/std",
"serde",
"sp-arithmetic/std",
"sp-std/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
]
@@ -0,0 +1,40 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common 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.
// Parity Bridges Common 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 Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Benchmarks for the relayers Pallet.
#![cfg(feature = "runtime-benchmarks")]
use crate::*;
use frame_benchmarking::{benchmarks, whitelisted_caller};
use frame_system::RawOrigin;
/// Reward amount that is (hopefully) is larger than existential deposit across all chains.
const REWARD_AMOUNT: u32 = u32::MAX;
benchmarks! {
// Benchmark `claim_rewards` call.
claim_rewards {
let relayer: T::AccountId = whitelisted_caller();
RelayerRewards::<T>::insert(&relayer, T::Reward::from(REWARD_AMOUNT));
}: _(RawOrigin::Signed(relayer))
verify {
// we can't check anything here, because `PaymentProcedure` is responsible for
// payment logic, so we assume that if call has succeeded, the procedure has
// also completed successfully
}
}
+170
View File
@@ -0,0 +1,170 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common 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.
// Parity Bridges Common 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 Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Runtime module that is used to store relayer rewards and (in the future) to
//! coordinate relations between relayers.
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
use bp_relayers::PaymentProcedure;
use sp_arithmetic::traits::AtLeast32BitUnsigned;
use sp_std::marker::PhantomData;
use weights::WeightInfo;
pub use pallet::*;
pub use payment_adapter::MessageDeliveryAndDispatchPaymentAdapter;
mod benchmarking;
mod mock;
mod payment_adapter;
pub mod weights;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// Type of relayer reward.
type Reward: AtLeast32BitUnsigned + Copy + Parameter + MaxEncodedLen;
/// Pay rewards adapter.
type PaymentProcedure: PaymentProcedure<Self::AccountId, Self::Reward>;
/// Pallet call weights.
type WeightInfo: WeightInfo;
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Claim accumulated rewards.
#[pallet::weight(T::WeightInfo::claim_rewards())]
pub fn claim_rewards(origin: OriginFor<T>) -> DispatchResult {
let relayer = ensure_signed(origin)?;
RelayerRewards::<T>::try_mutate_exists(&relayer, |maybe_reward| -> DispatchResult {
let reward = maybe_reward.take().ok_or(Error::<T>::NoRewardForRelayer)?;
T::PaymentProcedure::pay_reward(&relayer, reward).map_err(|e| {
log::trace!(
target: "runtime::bridge-relayers",
"Failed to pay rewards to {:?}: {:?}",
relayer,
e,
);
Error::<T>::FailedToPayReward
})?;
Self::deposit_event(Event::<T>::RewardPaid { relayer: relayer.clone(), reward });
Ok(())
})
}
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Reward has been paid to the relayer.
RewardPaid {
/// Relayer account that has been rewarded.
relayer: T::AccountId,
/// Reward amount.
reward: T::Reward,
},
}
#[pallet::error]
pub enum Error<T> {
/// No reward can be claimed by given relayer.
NoRewardForRelayer,
/// Reward payment procedure has failed.
FailedToPayReward,
}
/// Map of the relayer => accumulated reward.
#[pallet::storage]
pub type RelayerRewards<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, T::Reward, OptionQuery>;
}
#[cfg(test)]
mod tests {
use super::*;
use mock::*;
use frame_support::{assert_noop, assert_ok, traits::fungible::Inspect};
use sp_runtime::DispatchError;
#[test]
fn root_cant_claim_anything() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime>::claim_rewards(Origin::root()),
DispatchError::BadOrigin,
);
});
}
#[test]
fn relayer_cant_claim_if_no_reward_exists() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime>::claim_rewards(Origin::signed(REGULAR_RELAYER)),
Error::<TestRuntime>::NoRewardForRelayer,
);
});
}
#[test]
fn relayer_cant_claim_if_payment_procedure_fails() {
run_test(|| {
RelayerRewards::<TestRuntime>::insert(FAILING_RELAYER, 100);
assert_noop!(
Pallet::<TestRuntime>::claim_rewards(Origin::signed(FAILING_RELAYER)),
Error::<TestRuntime>::FailedToPayReward,
);
});
}
#[test]
fn relayer_can_claim_reward() {
run_test(|| {
RelayerRewards::<TestRuntime>::insert(REGULAR_RELAYER, 100);
assert_ok!(Pallet::<TestRuntime>::claim_rewards(Origin::signed(REGULAR_RELAYER)));
assert_eq!(RelayerRewards::<TestRuntime>::get(REGULAR_RELAYER), None);
});
}
#[test]
fn mint_reward_payment_procedure_actually_mints_tokens() {
type Balances = pallet_balances::Pallet<TestRuntime>;
run_test(|| {
assert_eq!(Balances::balance(&1), 0);
assert_eq!(Balances::total_issuance(), 0);
bp_relayers::MintReward::<Balances, AccountId>::pay_reward(&1, 100).unwrap();
assert_eq!(Balances::balance(&1), 100);
assert_eq!(Balances::total_issuance(), 100);
});
}
}
+156
View File
@@ -0,0 +1,156 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common 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.
// Parity Bridges Common 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 Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
#![cfg(test)]
use crate as pallet_bridge_relayers;
use bp_messages::{source_chain::ForbidOutboundMessages, target_chain::ForbidInboundMessages};
use bp_relayers::PaymentProcedure;
use frame_support::{parameter_types, weights::RuntimeDbWeight};
use sp_core::H256;
use sp_runtime::{
testing::Header as SubstrateHeader,
traits::{BlakeTwo256, IdentityLookup},
};
pub type AccountId = u64;
pub type Balance = u64;
type Block = frame_system::mocking::MockBlock<TestRuntime>;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<TestRuntime>;
frame_support::construct_runtime! {
pub enum TestRuntime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
Balances: pallet_balances::{Pallet, Event<T>},
Messages: pallet_bridge_messages::{Pallet, Event<T>},
Relayers: pallet_bridge_relayers::{Pallet, Call, Event<T>},
}
}
parameter_types! {
pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 };
}
impl frame_system::Config for TestRuntime {
type Origin = Origin;
type Index = u64;
type Call = Call;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = SubstrateHeader;
type Event = Event;
type BlockHashCount = frame_support::traits::ConstU64<250>;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type BaseCallFilter = frame_support::traits::Everything;
type SystemWeightInfo = ();
type BlockWeights = ();
type BlockLength = ();
type DbWeight = DbWeight;
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_balances::Config for TestRuntime {
type MaxLocks = ();
type Balance = Balance;
type DustRemoval = ();
type Event = Event;
type ExistentialDeposit = frame_support::traits::ConstU64<1>;
type AccountStore = frame_system::Pallet<TestRuntime>;
type WeightInfo = ();
type MaxReserves = ();
type ReserveIdentifier = ();
}
parameter_types! {
pub const TestBridgedChainId: bp_runtime::ChainId = *b"test";
}
// we're not testing messages pallet here, so values in this config might be crazy
impl pallet_bridge_messages::Config for TestRuntime {
type Event = Event;
type WeightInfo = ();
type Parameter = ();
type MaxMessagesToPruneAtOnce = frame_support::traits::ConstU64<0>;
type MaxUnrewardedRelayerEntriesAtInboundLane = frame_support::traits::ConstU64<8>;
type MaxUnconfirmedMessagesAtInboundLane = frame_support::traits::ConstU64<8>;
type MaximalOutboundPayloadSize = frame_support::traits::ConstU32<1024>;
type OutboundPayload = ();
type OutboundMessageFee = Balance;
type InboundPayload = ();
type InboundMessageFee = Balance;
type InboundRelayer = AccountId;
type TargetHeaderChain = ForbidOutboundMessages;
type LaneMessageVerifier = ForbidOutboundMessages;
type MessageDeliveryAndDispatchPayment = ();
type OnMessageAccepted = ();
type OnDeliveryConfirmed = ();
type SourceHeaderChain = ForbidInboundMessages;
type MessageDispatch = ForbidInboundMessages;
type BridgedChainId = TestBridgedChainId;
}
impl pallet_bridge_relayers::Config for TestRuntime {
type Event = Event;
type Reward = Balance;
type PaymentProcedure = TestPaymentProcedure;
type WeightInfo = ();
}
/// Regular relayer that may receive rewards.
pub const REGULAR_RELAYER: AccountId = 1;
/// Relayer that can't receive rewards.
pub const FAILING_RELAYER: AccountId = 2;
/// Payment procedure that rejects payments to the `FAILING_RELAYER`.
pub struct TestPaymentProcedure;
impl PaymentProcedure<AccountId, Balance> for TestPaymentProcedure {
type Error = ();
fn pay_reward(relayer: &AccountId, _reward: Balance) -> Result<(), Self::Error> {
match *relayer {
FAILING_RELAYER => Err(()),
_ => Ok(()),
}
}
}
/// Run pallet test.
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
let t = frame_system::GenesisConfig::default().build_storage::<TestRuntime>().unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(test)
}
@@ -0,0 +1,180 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common 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.
// Parity Bridges Common 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 Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Code that allows relayers pallet to be used as a delivery+dispatch payment mechanism
//! for the messages pallet.
use crate::{Config, RelayerRewards};
use bp_messages::source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards};
use frame_support::traits::Get;
use sp_arithmetic::traits::{Bounded, Saturating, Zero};
use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive};
/// Adapter that allows relayers pallet to be used as a delivery+dispatch payment mechanism
/// for the messages pallet.
pub struct MessageDeliveryAndDispatchPaymentAdapter<T, MessagesInstance, GetConfirmationFee>(
PhantomData<(T, MessagesInstance, GetConfirmationFee)>,
);
impl<T, MessagesInstance, GetConfirmationFee>
MessageDeliveryAndDispatchPayment<T::Origin, T::AccountId, T::Reward>
for MessageDeliveryAndDispatchPaymentAdapter<T, MessagesInstance, GetConfirmationFee>
where
T: Config + pallet_bridge_messages::Config<MessagesInstance, OutboundMessageFee = T::Reward>,
MessagesInstance: 'static,
GetConfirmationFee: Get<T::Reward>,
{
type Error = &'static str;
fn pay_delivery_and_dispatch_fee(
_submitter: &T::Origin,
_fee: &T::Reward,
) -> Result<(), Self::Error> {
// nothing shall happen here, because XCM deals with fee payment (planned to be burnt?
// or transferred to the treasury?)
Ok(())
}
fn pay_relayers_rewards(
lane_id: bp_messages::LaneId,
messages_relayers: VecDeque<bp_messages::UnrewardedRelayer<T::AccountId>>,
confirmation_relayer: &T::AccountId,
received_range: &RangeInclusive<bp_messages::MessageNonce>,
) {
let relayers_rewards = pallet_bridge_messages::calc_relayers_rewards::<T, MessagesInstance>(
lane_id,
messages_relayers,
received_range,
);
if !relayers_rewards.is_empty() {
schedule_relayers_rewards::<T>(
confirmation_relayer,
relayers_rewards,
GetConfirmationFee::get(),
);
}
}
}
// Update rewards to given relayers, optionally rewarding confirmation relayer.
fn schedule_relayers_rewards<T: Config>(
confirmation_relayer: &T::AccountId,
relayers_rewards: RelayersRewards<T::AccountId, T::Reward>,
confirmation_fee: T::Reward,
) {
// reward every relayer except `confirmation_relayer`
let mut confirmation_relayer_reward = T::Reward::zero();
for (relayer, reward) in relayers_rewards {
let mut relayer_reward = reward.reward;
if relayer != *confirmation_relayer {
// If delivery confirmation is submitted by other relayer, let's deduct confirmation fee
// from relayer reward.
//
// If confirmation fee has been increased (or if it was the only component of message
// fee), then messages relayer may receive zero reward.
let mut confirmation_reward = T::Reward::try_from(reward.messages)
.unwrap_or_else(|_| Bounded::max_value())
.saturating_mul(confirmation_fee);
if confirmation_reward > relayer_reward {
confirmation_reward = relayer_reward;
}
relayer_reward = relayer_reward.saturating_sub(confirmation_reward);
confirmation_relayer_reward =
confirmation_relayer_reward.saturating_add(confirmation_reward);
} else {
// If delivery confirmation is submitted by this relayer, let's add confirmation fee
// from other relayers to this relayer reward.
confirmation_relayer_reward = confirmation_relayer_reward.saturating_add(reward.reward);
continue
}
schedule_relayer_reward::<T>(&relayer, relayer_reward);
}
// finally - pay reward to confirmation relayer
schedule_relayer_reward::<T>(confirmation_relayer, confirmation_relayer_reward);
}
/// Remember that the reward shall be paid to the relayer.
fn schedule_relayer_reward<T: Config>(relayer: &T::AccountId, reward: T::Reward) {
if reward.is_zero() {
return
}
RelayerRewards::<T>::mutate(relayer, |old_reward: &mut Option<T::Reward>| {
let new_reward = old_reward.unwrap_or_else(Zero::zero).saturating_add(reward);
log::trace!(
target: "T::bridge-relayers",
"Relayer {:?} can now claim reward: {:?}",
relayer,
new_reward,
);
*old_reward = Some(new_reward);
});
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::*;
const RELAYER_1: AccountId = 1;
const RELAYER_2: AccountId = 2;
const RELAYER_3: AccountId = 3;
fn relayers_rewards() -> RelayersRewards<AccountId, Balance> {
vec![
(RELAYER_1, bp_messages::source_chain::RelayerRewards { reward: 100, messages: 2 }),
(RELAYER_2, bp_messages::source_chain::RelayerRewards { reward: 100, messages: 3 }),
]
.into_iter()
.collect()
}
#[test]
fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() {
run_test(|| {
schedule_relayers_rewards::<TestRuntime>(&RELAYER_2, relayers_rewards(), 10);
assert_eq!(RelayerRewards::<TestRuntime>::get(&RELAYER_1), Some(80));
assert_eq!(RelayerRewards::<TestRuntime>::get(&RELAYER_2), Some(120));
});
}
#[test]
fn confirmation_relayer_is_rewarded_if_it_has_not_delivered_any_delivered_messages() {
run_test(|| {
schedule_relayers_rewards::<TestRuntime>(&RELAYER_3, relayers_rewards(), 10);
assert_eq!(RelayerRewards::<TestRuntime>::get(&RELAYER_1), Some(80));
assert_eq!(RelayerRewards::<TestRuntime>::get(&RELAYER_2), Some(70));
assert_eq!(RelayerRewards::<TestRuntime>::get(&RELAYER_3), Some(50));
});
}
#[test]
fn only_confirmation_relayer_is_rewarded_if_confirmation_fee_has_significantly_increased() {
run_test(|| {
schedule_relayers_rewards::<TestRuntime>(&RELAYER_3, relayers_rewards(), 1000);
assert_eq!(RelayerRewards::<TestRuntime>::get(&RELAYER_1), None);
assert_eq!(RelayerRewards::<TestRuntime>::get(&RELAYER_2), None);
assert_eq!(RelayerRewards::<TestRuntime>::get(&RELAYER_3), Some(200));
});
}
}
+73
View File
@@ -0,0 +1,73 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common 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.
// Parity Bridges Common 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 Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Autogenerated weights for `pallet_bridge_relayers`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-07-20, STEPS: 50, REPEAT: 20
//! LOW RANGE: [], HIGH RANGE: []
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
//! CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// target/release/millau-bridge-node
// benchmark
// pallet
// --chain=dev
// --steps=50
// --repeat=20
// --pallet=pallet_bridge_relayers
// --extrinsic=*
// --execution=wasm
// --wasm-execution=Compiled
// --heap-pages=4096
// --output=./modules/relayers/src/weights.rs
// --template=./.maintain/millau-weight-template.hbs
#![allow(clippy::all)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use frame_support::{
traits::Get,
weights::{constants::RocksDbWeight, Weight},
};
use sp_std::marker::PhantomData;
/// Weight functions needed for `pallet_bridge_relayers`.
pub trait WeightInfo {
fn claim_rewards() -> Weight;
}
/// Weights for `pallet_bridge_relayers` using the Millau node and recommended hardware.
pub struct MillauWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for MillauWeight<T> {
fn claim_rewards() -> Weight {
(38_435_000 as Weight)
.saturating_add(T::DbWeight::get().reads(2 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
fn claim_rewards() -> Weight {
(38_435_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
}
+38
View File
@@ -0,0 +1,38 @@
[package]
name = "bp-relayers"
description = "Primitives of relayers module."
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
# Bridge dependencies
#bp-runtime = { path = "../runtime", default-features = false }
# Substrate Dependencies
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 }
#sp-core = { 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 }
[dev-dependencies]
hex = "0.4"
hex-literal = "0.3"
[features]
default = ["std"]
std = [
# "bp-runtime/std",
"frame-support/std",
# "frame-system/std",
# "scale-info/std",
# "serde",
# "sp-core/std",
"sp-runtime/std",
"sp-std/std",
]
+45
View File
@@ -0,0 +1,45 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common 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.
// Parity Bridges Common 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 Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Primitives of messages module.
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
use sp_std::{fmt::Debug, marker::PhantomData};
/// Reward payment procedure.
pub trait PaymentProcedure<Relayer, Reward> {
/// Error that may be returned by the procedure.
type Error: Debug;
/// Pay reward to the relayer.
fn pay_reward(relayer: &Relayer, reward: Reward) -> Result<(), Self::Error>;
}
/// Reward payment procedure that is simply minting given amount of tokens.
pub struct MintReward<T, Relayer>(PhantomData<(T, Relayer)>);
impl<T, Relayer> PaymentProcedure<Relayer, T::Balance> for MintReward<T, Relayer>
where
T: frame_support::traits::fungible::Mutate<Relayer>,
{
type Error = sp_runtime::DispatchError;
fn pay_reward(relayer: &Relayer, reward: T::Balance) -> Result<(), Self::Error> {
T::mint_into(relayer, reward)
}
}