Skip to content
// Copyright (C) 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/>.
//! Helpers for generating message storage proofs, that are used by tests and by benchmarks.
use crate::messages::{AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain};
use bp_messages::{
storage_keys, InboundLaneData, LaneId, MessageKey, MessageNonce, MessagePayload,
OutboundLaneData,
};
use bp_runtime::{record_all_trie_keys, RawStorageProof, StorageProofSize};
use codec::Encode;
use sp_std::{ops::RangeInclusive, prelude::*};
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};
/// Simple and correct message data encode function.
pub fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option<Vec<u8>> {
Some(m.encode())
}
/// Simple and correct outbound lane data encode function.
pub fn encode_lane_data(d: &OutboundLaneData) -> Vec<u8> {
d.encode()
}
/// Prepare storage proof of given messages.
///
/// Returns state trie root and nodes with prepared messages.
pub fn prepare_messages_storage_proof<B>(
lane: LaneId,
message_nonces: RangeInclusive<MessageNonce>,
outbound_lane_data: Option<OutboundLaneData>,
size: StorageProofSize,
message_payload: MessagePayload,
encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option<Vec<u8>>,
encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec<u8>,
) -> (HashOf<BridgedChain<B>>, RawStorageProof)
where
B: MessageBridge,
HashOf<BridgedChain<B>>: Copy + Default,
{
// prepare Bridged chain storage with messages and (optionally) outbound lane state
let message_count = message_nonces.end().saturating_sub(*message_nonces.start()) + 1;
let mut storage_keys = Vec::with_capacity(message_count as usize + 1);
let mut root = Default::default();
let mut mdb = MemoryDB::default();
{
let mut trie =
TrieDBMutBuilderV1::<HasherOf<BridgedChain<B>>>::new(&mut mdb, &mut root).build();
// insert messages
for (i, nonce) in message_nonces.into_iter().enumerate() {
let message_key = MessageKey { lane_id: lane, nonce };
let message_payload = match encode_message(nonce, &message_payload) {
Some(message_payload) =>
if i == 0 {
grow_trie_leaf_value(message_payload, size)
} else {
message_payload
},
None => continue,
};
let storage_key = storage_keys::message_key(
B::BRIDGED_MESSAGES_PALLET_NAME,
&message_key.lane_id,
message_key.nonce,
)
.0;
trie.insert(&storage_key, &message_payload)
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
storage_keys.push(storage_key);
}
// insert outbound lane state
if let Some(outbound_lane_data) = outbound_lane_data.as_ref().map(encode_outbound_lane_data)
{
let storage_key =
storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0;
trie.insert(&storage_key, &outbound_lane_data)
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
storage_keys.push(storage_key);
}
}
// generate storage proof to be delivered to This chain
let storage_proof = record_all_trie_keys::<LayoutV1<HasherOf<BridgedChain<B>>>, _>(&mdb, &root)
.map_err(|_| "record_all_trie_keys has failed")
.expect("record_all_trie_keys should not fail in benchmarks");
(root, storage_proof)
}
/// Prepare storage proof of given messages delivery.
///
/// Returns state trie root and nodes with prepared messages.
pub fn prepare_message_delivery_storage_proof<B>(
lane: LaneId,
inbound_lane_data: InboundLaneData<AccountIdOf<ThisChain<B>>>,
size: StorageProofSize,
) -> (HashOf<BridgedChain<B>>, RawStorageProof)
where
B: MessageBridge,
{
// prepare Bridged chain storage with inbound lane state
let storage_key = storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0;
let mut root = Default::default();
let mut mdb = MemoryDB::default();
{
let mut trie =
TrieDBMutBuilderV1::<HasherOf<BridgedChain<B>>>::new(&mut mdb, &mut root).build();
let inbound_lane_data = grow_trie_leaf_value(inbound_lane_data.encode(), size);
trie.insert(&storage_key, &inbound_lane_data)
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
}
// generate storage proof to be delivered to This chain
let storage_proof = record_all_trie_keys::<LayoutV1<HasherOf<BridgedChain<B>>>, _>(&mdb, &root)
.map_err(|_| "record_all_trie_keys has failed")
.expect("record_all_trie_keys should not fail in benchmarks");
(root, storage_proof)
}
/// Add extra data to the trie leaf value so that it'll be of given size.
pub fn grow_trie_leaf_value(mut value: Vec<u8>, size: StorageProofSize) -> Vec<u8> {
match size {
StorageProofSize::Minimal(_) => (),
StorageProofSize::HasLargeLeaf(size) if size as usize > value.len() => {
value.extend(sp_std::iter::repeat(42u8).take(size as usize - value.len()));
},
StorageProofSize::HasLargeLeaf(_) => (),
}
value
}
// Copyright (C) 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/>.
//! Module provides utilities for easier XCM handling, e.g:
//! `XcmExecutor` -> `MessageSender` -> `OutboundMessageQueue`
//! |
//! `Relayer`
//! |
//! `XcmRouter` <- `MessageDispatch` <- `InboundMessageQueue`
use bp_messages::{
source_chain::OnMessagesDelivered,
target_chain::{DispatchMessage, MessageDispatch},
LaneId, MessageNonce,
};
use bp_runtime::messages::MessageDispatchResult;
pub use bp_xcm_bridge_hub::XcmAsPlainPayload;
use bp_xcm_bridge_hub_router::XcmChannelStatusProvider;
use codec::{Decode, Encode};
use frame_support::{traits::Get, weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound};
use pallet_bridge_messages::{
Config as MessagesConfig, OutboundLanesCongestedSignals, WeightInfoExt as MessagesPalletWeights,
};
use scale_info::TypeInfo;
use sp_runtime::SaturatedConversion;
use sp_std::{fmt::Debug, marker::PhantomData};
use xcm::prelude::*;
use xcm_builder::{DispatchBlob, DispatchBlobError};
/// Message dispatch result type for single message.
#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)]
pub enum XcmBlobMessageDispatchResult {
/// We've been unable to decode message payload.
InvalidPayload,
/// Message has been dispatched.
Dispatched,
/// Message has **NOT** been dispatched because of given error.
NotDispatched(#[codec(skip)] Option<DispatchBlobError>),
}
/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages
///
/// It needs to be used at the target bridge hub.
pub struct XcmBlobMessageDispatch<DispatchBlob, Weights, Channel> {
_marker: sp_std::marker::PhantomData<(DispatchBlob, Weights, Channel)>,
}
impl<
BlobDispatcher: DispatchBlob,
Weights: MessagesPalletWeights,
Channel: XcmChannelStatusProvider,
> MessageDispatch for XcmBlobMessageDispatch<BlobDispatcher, Weights, Channel>
{
type DispatchPayload = XcmAsPlainPayload;
type DispatchLevelResult = XcmBlobMessageDispatchResult;
fn is_active() -> bool {
!Channel::is_congested()
}
fn dispatch_weight(message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight {
match message.data.payload {
Ok(ref payload) => {
let payload_size = payload.encoded_size().saturated_into();
Weights::message_dispatch_weight(payload_size)
},
Err(_) => Weight::zero(),
}
}
fn dispatch(
message: DispatchMessage<Self::DispatchPayload>,
) -> MessageDispatchResult<Self::DispatchLevelResult> {
let payload = match message.data.payload {
Ok(payload) => payload,
Err(e) => {
log::error!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}",
e,
message.key.nonce
);
return MessageDispatchResult {
unspent_weight: Weight::zero(),
dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
}
},
};
let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) {
Ok(_) => {
log::debug!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}",
message.key.nonce
);
XcmBlobMessageDispatchResult::Dispatched
},
Err(e) => {
log::error!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}",
e, message.key.nonce
);
XcmBlobMessageDispatchResult::NotDispatched(Some(e))
},
};
MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result }
}
}
/// A pair of sending chain location and message lane, used by this chain to send messages
/// over the bridge.
#[cfg_attr(feature = "std", derive(Debug, Eq, PartialEq))]
pub struct SenderAndLane {
/// Sending chain relative location.
pub location: Location,
/// Message lane, used by the sending chain.
pub lane: LaneId,
}
impl SenderAndLane {
/// Create new object using provided location and lane.
pub fn new(location: Location, lane: LaneId) -> Self {
SenderAndLane { location, lane }
}
}
/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from
/// one side, where on the other it can be dispatched by [`XcmBlobMessageDispatch`].
pub trait XcmBlobHauler {
/// Runtime that has messages pallet deployed.
type Runtime: MessagesConfig<Self::MessagesInstance>;
/// Instance of the messages pallet that is used to send messages.
type MessagesInstance: 'static;
/// Actual XCM message sender (`HRMP` or `UMP`) to the source chain
/// location (`Self::SenderAndLane::get().location`).
type ToSourceChainSender: SendXcm;
/// An XCM message that is sent to the sending chain when the bridge queue becomes congested.
type CongestedMessage: Get<Option<Xcm<()>>>;
/// An XCM message that is sent to the sending chain when the bridge queue becomes not
/// congested.
type UncongestedMessage: Get<Option<Xcm<()>>>;
/// Returns `true` if we want to handle congestion.
fn supports_congestion_detection() -> bool {
Self::CongestedMessage::get().is_some() || Self::UncongestedMessage::get().is_some()
}
}
/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`pallet_bridge_messages`] and
/// makes sure that XCM blob is sent to the outbound lane to be relayed.
///
/// It needs to be used at the source bridge hub.
pub struct XcmBlobHaulerAdapter<XcmBlobHauler, Lanes>(
sp_std::marker::PhantomData<(XcmBlobHauler, Lanes)>,
);
impl<
H: XcmBlobHauler,
Lanes: Get<sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))>>,
> OnMessagesDelivered for XcmBlobHaulerAdapter<H, Lanes>
{
fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce) {
if let Some(sender_and_lane) =
Lanes::get().iter().find(|link| link.0.lane == lane).map(|link| &link.0)
{
// notify XCM queue manager about updated lane state
LocalXcmQueueManager::<H>::on_bridge_messages_delivered(
sender_and_lane,
enqueued_messages,
);
}
}
}
/// Manager of local XCM queues (and indirectly - underlying transport channels) that
/// controls the queue state.
///
/// It needs to be used at the source bridge hub.
pub struct LocalXcmQueueManager<H>(PhantomData<H>);
/// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we
/// send a "congestion" XCM message to the sending chain.
const OUTBOUND_LANE_CONGESTED_THRESHOLD: MessageNonce = 8_192;
/// After we have sent "congestion" XCM message to the sending chain, we wait until number
/// of messages in the outbound bridge queue drops to this count, before sending `uncongestion`
/// XCM message.
const OUTBOUND_LANE_UNCONGESTED_THRESHOLD: MessageNonce = 1_024;
impl<H: XcmBlobHauler> LocalXcmQueueManager<H> {
/// Must be called whenever we push a message to the bridge lane.
pub fn on_bridge_message_enqueued(
sender_and_lane: &SenderAndLane,
enqueued_messages: MessageNonce,
) {
// skip if we dont want to handle congestion
if !H::supports_congestion_detection() {
return
}
// if we have already sent the congestion signal, we don't want to do anything
if Self::is_congested_signal_sent(sender_and_lane.lane) {
return
}
// if the bridge queue is not congested, we don't want to do anything
let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD;
if !is_congested {
return
}
log::info!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"Sending 'congested' XCM message to {:?} to avoid overloading lane {:?}: there are\
{} messages queued at the bridge queue",
sender_and_lane.location,
sender_and_lane.lane,
enqueued_messages,
);
if let Err(e) = Self::send_congested_signal(sender_and_lane) {
log::info!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"Failed to send the 'congested' XCM message to {:?}: {:?}",
sender_and_lane.location,
e,
);
}
}
/// Must be called whenever we receive a message delivery confirmation.
pub fn on_bridge_messages_delivered(
sender_and_lane: &SenderAndLane,
enqueued_messages: MessageNonce,
) {
// skip if we dont want to handle congestion
if !H::supports_congestion_detection() {
return
}
// if we have not sent the congestion signal before, we don't want to do anything
if !Self::is_congested_signal_sent(sender_and_lane.lane) {
return
}
// if the bridge queue is still congested, we don't want to do anything
let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD;
if is_congested {
return
}
log::info!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"Sending 'uncongested' XCM message to {:?}. Lane {:?}: there are\
{} messages queued at the bridge queue",
sender_and_lane.location,
sender_and_lane.lane,
enqueued_messages,
);
if let Err(e) = Self::send_uncongested_signal(sender_and_lane) {
log::info!(
target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"Failed to send the 'uncongested' XCM message to {:?}: {:?}",
sender_and_lane.location,
e,
);
}
}
/// Returns true if we have sent "congested" signal to the `sending_chain_location`.
fn is_congested_signal_sent(lane: LaneId) -> bool {
OutboundLanesCongestedSignals::<H::Runtime, H::MessagesInstance>::get(lane)
}
/// Send congested signal to the `sending_chain_location`.
fn send_congested_signal(sender_and_lane: &SenderAndLane) -> Result<(), SendError> {
if let Some(msg) = H::CongestedMessage::get() {
send_xcm::<H::ToSourceChainSender>(sender_and_lane.location.clone(), msg)?;
OutboundLanesCongestedSignals::<H::Runtime, H::MessagesInstance>::insert(
sender_and_lane.lane,
true,
);
}
Ok(())
}
/// Send `uncongested` signal to the `sending_chain_location`.
fn send_uncongested_signal(sender_and_lane: &SenderAndLane) -> Result<(), SendError> {
if let Some(msg) = H::UncongestedMessage::get() {
send_xcm::<H::ToSourceChainSender>(sender_and_lane.location.clone(), msg)?;
OutboundLanesCongestedSignals::<H::Runtime, H::MessagesInstance>::remove(
sender_and_lane.lane,
);
}
Ok(())
}
}
/// Adapter for the implementation of `GetVersion`, which attempts to find the minimal
/// configured XCM version between the destination `dest` and the bridge hub location provided as
/// `Get<Location>`.
pub struct XcmVersionOfDestAndRemoteBridge<Version, RemoteBridge>(
sp_std::marker::PhantomData<(Version, RemoteBridge)>,
);
impl<Version: GetVersion, RemoteBridge: Get<Location>> GetVersion
for XcmVersionOfDestAndRemoteBridge<Version, RemoteBridge>
{
fn get_version_for(dest: &Location) -> Option<XcmVersion> {
let dest_version = Version::get_version_for(dest);
let bridge_hub_version = Version::get_version_for(&RemoteBridge::get());
match (dest_version, bridge_hub_version) {
(Some(dv), Some(bhv)) => Some(sp_std::cmp::min(dv, bhv)),
(Some(dv), None) => Some(dv),
(None, Some(bhv)) => Some(bhv),
(None, None) => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::*;
use bp_messages::OutboundLaneData;
use frame_support::parameter_types;
use pallet_bridge_messages::OutboundLanes;
parameter_types! {
pub TestSenderAndLane: SenderAndLane = SenderAndLane {
location: Location::new(1, [Parachain(1000)]),
lane: TEST_LANE_ID,
};
pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![
(TestSenderAndLane::get(), (NetworkId::ByGenesis([0; 32]), InteriorLocation::Here))
];
pub DummyXcmMessage: Xcm<()> = Xcm::new();
}
struct DummySendXcm;
impl DummySendXcm {
fn messages_sent() -> u32 {
frame_support::storage::unhashed::get(b"DummySendXcm").unwrap_or(0)
}
}
impl SendXcm for DummySendXcm {
type Ticket = ();
fn validate(
_destination: &mut Option<Location>,
_message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
Ok(((), Default::default()))
}
fn deliver(_ticket: Self::Ticket) -> Result<XcmHash, SendError> {
let messages_sent: u32 = Self::messages_sent();
frame_support::storage::unhashed::put(b"DummySendXcm", &(messages_sent + 1));
Ok(XcmHash::default())
}
}
struct TestBlobHauler;
impl XcmBlobHauler for TestBlobHauler {
type Runtime = TestRuntime;
type MessagesInstance = ();
type ToSourceChainSender = DummySendXcm;
type CongestedMessage = DummyXcmMessage;
type UncongestedMessage = DummyXcmMessage;
}
type TestBlobHaulerAdapter = XcmBlobHaulerAdapter<TestBlobHauler, TestLanes>;
fn fill_up_lane_to_congestion() -> MessageNonce {
let latest_generated_nonce = OUTBOUND_LANE_CONGESTED_THRESHOLD;
OutboundLanes::<TestRuntime, ()>::insert(
TEST_LANE_ID,
OutboundLaneData {
oldest_unpruned_nonce: 0,
latest_received_nonce: 0,
latest_generated_nonce,
},
);
latest_generated_nonce
}
#[test]
fn congested_signal_is_not_sent_twice() {
run_test(|| {
let enqueued = fill_up_lane_to_congestion();
// next sent message leads to congested signal
LocalXcmQueueManager::<TestBlobHauler>::on_bridge_message_enqueued(
&TestSenderAndLane::get(),
enqueued + 1,
);
assert_eq!(DummySendXcm::messages_sent(), 1);
// next sent message => we don't sent another congested signal
LocalXcmQueueManager::<TestBlobHauler>::on_bridge_message_enqueued(
&TestSenderAndLane::get(),
enqueued,
);
assert_eq!(DummySendXcm::messages_sent(), 1);
});
}
#[test]
fn congested_signal_is_not_sent_when_outbound_lane_is_not_congested() {
run_test(|| {
LocalXcmQueueManager::<TestBlobHauler>::on_bridge_message_enqueued(
&TestSenderAndLane::get(),
1,
);
assert_eq!(DummySendXcm::messages_sent(), 0);
});
}
#[test]
fn congested_signal_is_sent_when_outbound_lane_is_congested() {
run_test(|| {
let enqueued = fill_up_lane_to_congestion();
// next sent message leads to congested signal
LocalXcmQueueManager::<TestBlobHauler>::on_bridge_message_enqueued(
&TestSenderAndLane::get(),
enqueued + 1,
);
assert_eq!(DummySendXcm::messages_sent(), 1);
assert!(LocalXcmQueueManager::<TestBlobHauler>::is_congested_signal_sent(TEST_LANE_ID));
});
}
#[test]
fn uncongested_signal_is_not_sent_when_messages_are_delivered_at_other_lane() {
run_test(|| {
LocalXcmQueueManager::<TestBlobHauler>::send_congested_signal(&TestSenderAndLane::get()).unwrap();
assert_eq!(DummySendXcm::messages_sent(), 1);
// when we receive a delivery report for other lane, we don't send an uncongested signal
TestBlobHaulerAdapter::on_messages_delivered(LaneId([42, 42, 42, 42]), 0);
assert_eq!(DummySendXcm::messages_sent(), 1);
});
}
#[test]
fn uncongested_signal_is_not_sent_when_we_havent_send_congested_signal_before() {
run_test(|| {
TestBlobHaulerAdapter::on_messages_delivered(TEST_LANE_ID, 0);
assert_eq!(DummySendXcm::messages_sent(), 0);
});
}
#[test]
fn uncongested_signal_is_not_sent_if_outbound_lane_is_still_congested() {
run_test(|| {
LocalXcmQueueManager::<TestBlobHauler>::send_congested_signal(&TestSenderAndLane::get()).unwrap();
assert_eq!(DummySendXcm::messages_sent(), 1);
TestBlobHaulerAdapter::on_messages_delivered(
TEST_LANE_ID,
OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1,
);
assert_eq!(DummySendXcm::messages_sent(), 1);
});
}
#[test]
fn uncongested_signal_is_sent_if_outbound_lane_is_uncongested() {
run_test(|| {
LocalXcmQueueManager::<TestBlobHauler>::send_congested_signal(&TestSenderAndLane::get()).unwrap();
assert_eq!(DummySendXcm::messages_sent(), 1);
TestBlobHaulerAdapter::on_messages_delivered(
TEST_LANE_ID,
OUTBOUND_LANE_UNCONGESTED_THRESHOLD,
);
assert_eq!(DummySendXcm::messages_sent(), 2);
});
}
}
// Copyright (C) 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/>.
//! A mock runtime for testing different stuff in the crate.
#![cfg(test)]
use crate::messages::{
source::{
FromThisChainMaximalOutboundPayloadSize, FromThisChainMessagePayload,
TargetHeaderChainAdapter,
},
target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter},
BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages,
};
use bp_header_chain::{ChainWithGrandpa, HeaderChain};
use bp_messages::{
target_chain::{DispatchMessage, MessageDispatch},
LaneId, MessageNonce,
};
use bp_parachains::SingleParaStoredHeaderDataBuilder;
use bp_relayers::PayRewardFromAccount;
use bp_runtime::{
messages::MessageDispatchResult, Chain, ChainId, Parachain, UnderlyingChainProvider,
};
use codec::{Decode, Encode};
use frame_support::{
derive_impl, parameter_types,
weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight},
};
use pallet_transaction_payment::Multiplier;
use sp_runtime::{
testing::H256,
traits::{BlakeTwo256, ConstU32, ConstU64, ConstU8},
FixedPointNumber, Perquintill,
};
/// Account identifier at `ThisChain`.
pub type ThisChainAccountId = u64;
/// Balance at `ThisChain`.
pub type ThisChainBalance = u64;
/// Block number at `ThisChain`.
pub type ThisChainBlockNumber = u32;
/// Hash at `ThisChain`.
pub type ThisChainHash = H256;
/// Hasher at `ThisChain`.
pub type ThisChainHasher = BlakeTwo256;
/// Runtime call at `ThisChain`.
pub type ThisChainRuntimeCall = RuntimeCall;
/// Runtime call origin at `ThisChain`.
pub type ThisChainCallOrigin = RuntimeOrigin;
/// Header of `ThisChain`.
pub type ThisChainHeader = sp_runtime::generic::Header<ThisChainBlockNumber, ThisChainHasher>;
/// Block of `ThisChain`.
pub type ThisChainBlock = frame_system::mocking::MockBlockU32<TestRuntime>;
/// Account identifier at the `BridgedChain`.
pub type BridgedChainAccountId = u128;
/// Balance at the `BridgedChain`.
pub type BridgedChainBalance = u128;
/// Block number at the `BridgedChain`.
pub type BridgedChainBlockNumber = u32;
/// Hash at the `BridgedChain`.
pub type BridgedChainHash = H256;
/// Hasher at the `BridgedChain`.
pub type BridgedChainHasher = BlakeTwo256;
/// Header of the `BridgedChain`.
pub type BridgedChainHeader =
sp_runtime::generic::Header<BridgedChainBlockNumber, BridgedChainHasher>;
/// Rewards payment procedure.
pub type TestPaymentProcedure = PayRewardFromAccount<Balances, ThisChainAccountId>;
/// Stake that we are using in tests.
pub type TestStake = ConstU64<5_000>;
/// Stake and slash mechanism to use in tests.
pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed<
ThisChainAccountId,
ThisChainBlockNumber,
Balances,
ReserveId,
TestStake,
ConstU32<8>,
>;
/// Message lane used in tests.
pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]);
/// Bridged chain id used in tests.
pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg";
/// Maximal extrinsic weight at the `BridgedChain`.
pub const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048;
/// Maximal extrinsic size at the `BridgedChain`.
pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024;
frame_support::construct_runtime! {
pub enum TestRuntime
{
System: frame_system::{Pallet, Call, Config<T>, Storage, Event<T>},
Utility: pallet_utility,
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event<T>},
BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event<T>},
BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event<T>},
BridgeParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event<T>},
BridgeMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>},
}
}
crate::generate_bridge_reject_obsolete_headers_and_messages! {
ThisChainRuntimeCall, ThisChainAccountId,
BridgeGrandpa, BridgeParachains, BridgeMessages
}
parameter_types! {
pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID];
pub const BridgedChainId: ChainId = TEST_BRIDGED_CHAIN_ID;
pub const BridgedParasPalletName: &'static str = "Paras";
pub const ExistentialDeposit: ThisChainBalance = 500;
pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 };
pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25);
pub const TransactionBaseFee: ThisChainBalance = 0;
pub const TransactionByteFee: ThisChainBalance = 1;
pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000);
pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128);
pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value();
pub const MaxUnrewardedRelayerEntriesAtInboundLane: MessageNonce = 16;
pub const MaxUnconfirmedMessagesAtInboundLane: MessageNonce = 1_000;
pub const ReserveId: [u8; 8] = *b"brdgrlrs";
}
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for TestRuntime {
type Hash = ThisChainHash;
type Hashing = ThisChainHasher;
type AccountId = ThisChainAccountId;
type Block = ThisChainBlock;
type AccountData = pallet_balances::AccountData<ThisChainBalance>;
type BlockHashCount = ConstU32<250>;
}
impl pallet_utility::Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
type PalletsOrigin = OriginCaller;
type WeightInfo = ();
}
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)]
impl pallet_balances::Config for TestRuntime {
type ReserveIdentifier = [u8; 8];
type AccountStore = System;
}
#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig as pallet_transaction_payment::DefaultConfig)]
impl pallet_transaction_payment::Config for TestRuntime {
type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, ()>;
type OperationalFeeMultiplier = ConstU8<5>;
type WeightToFee = IdentityFee<ThisChainBalance>;
type LengthToFee = ConstantMultiplier<ThisChainBalance, TransactionByteFee>;
type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment<
TestRuntime,
TargetBlockFullness,
AdjustmentVariable,
MinimumMultiplier,
MaximumMultiplier,
>;
}
impl pallet_bridge_grandpa::Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type BridgedChain = BridgedUnderlyingChain;
type MaxFreeHeadersPerBlock = ConstU32<4>;
type FreeHeadersInterval = ConstU32<1_024>;
type HeadersToKeep = ConstU32<8>;
type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight<TestRuntime>;
}
impl pallet_bridge_parachains::Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type BridgesGrandpaPalletInstance = ();
type ParasPalletName = BridgedParasPalletName;
type ParaStoredHeaderDataBuilder =
SingleParaStoredHeaderDataBuilder<BridgedUnderlyingParachain>;
type HeadsToKeep = ConstU32<8>;
type MaxParaHeadDataSize = ConstU32<1024>;
type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight<TestRuntime>;
}
impl pallet_bridge_messages::Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_bridge_messages::weights::BridgeWeight<TestRuntime>;
type ActiveOutboundLanes = ActiveOutboundLanes;
type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
type MaximalOutboundPayloadSize = FromThisChainMaximalOutboundPayloadSize<OnThisChainBridge>;
type OutboundPayload = FromThisChainMessagePayload;
type InboundPayload = FromBridgedChainMessagePayload;
type InboundRelayer = BridgedChainAccountId;
type DeliveryPayments = ();
type TargetHeaderChain = TargetHeaderChainAdapter<OnThisChainBridge>;
type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter<
TestRuntime,
(),
ConstU64<100_000>,
>;
type OnMessagesDelivered = ();
type SourceHeaderChain = SourceHeaderChainAdapter<OnThisChainBridge>;
type MessageDispatch = DummyMessageDispatch;
type BridgedChainId = BridgedChainId;
}
impl pallet_bridge_relayers::Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type Reward = ThisChainBalance;
type PaymentProcedure = TestPaymentProcedure;
type StakeAndSlash = TestStakeAndSlash;
type WeightInfo = ();
}
/// Dummy message dispatcher.
pub struct DummyMessageDispatch;
impl DummyMessageDispatch {
pub fn deactivate() {
frame_support::storage::unhashed::put(&b"inactive"[..], &false);
}
}
impl MessageDispatch for DummyMessageDispatch {
type DispatchPayload = Vec<u8>;
type DispatchLevelResult = ();
fn is_active() -> bool {
frame_support::storage::unhashed::take::<bool>(&b"inactive"[..]) != Some(false)
}
fn dispatch_weight(_message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight {
Weight::zero()
}
fn dispatch(
_: DispatchMessage<Self::DispatchPayload>,
) -> MessageDispatchResult<Self::DispatchLevelResult> {
MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () }
}
}
/// Bridge that is deployed on `ThisChain` and allows sending/receiving messages to/from
/// `BridgedChain`.
#[derive(Debug, PartialEq, Eq)]
pub struct OnThisChainBridge;
impl MessageBridge for OnThisChainBridge {
const BRIDGED_MESSAGES_PALLET_NAME: &'static str = "";
type ThisChain = ThisChain;
type BridgedChain = BridgedChain;
type BridgedHeaderChain = pallet_bridge_grandpa::GrandpaChainHeaders<TestRuntime, ()>;
}
/// Bridge that is deployed on `BridgedChain` and allows sending/receiving messages to/from
/// `ThisChain`.
#[derive(Debug, PartialEq, Eq)]
pub struct OnBridgedChainBridge;
impl MessageBridge for OnBridgedChainBridge {
const BRIDGED_MESSAGES_PALLET_NAME: &'static str = "";
type ThisChain = BridgedChain;
type BridgedChain = ThisChain;
type BridgedHeaderChain = ThisHeaderChain;
}
/// Dummy implementation of `HeaderChain` for `ThisChain` at the `BridgedChain`.
pub struct ThisHeaderChain;
impl HeaderChain<ThisUnderlyingChain> for ThisHeaderChain {
fn finalized_header_state_root(_hash: HashOf<ThisChain>) -> Option<HashOf<ThisChain>> {
unreachable!()
}
}
/// Call origin at `BridgedChain`.
#[derive(Clone, Debug)]
pub struct BridgedChainOrigin;
impl From<BridgedChainOrigin>
for Result<frame_system::RawOrigin<BridgedChainAccountId>, BridgedChainOrigin>
{
fn from(
_origin: BridgedChainOrigin,
) -> Result<frame_system::RawOrigin<BridgedChainAccountId>, BridgedChainOrigin> {
unreachable!()
}
}
/// Underlying chain of `ThisChain`.
pub struct ThisUnderlyingChain;
impl Chain for ThisUnderlyingChain {
const ID: ChainId = *b"tuch";
type BlockNumber = ThisChainBlockNumber;
type Hash = ThisChainHash;
type Hasher = ThisChainHasher;
type Header = ThisChainHeader;
type AccountId = ThisChainAccountId;
type Balance = ThisChainBalance;
type Nonce = u32;
type Signature = sp_runtime::MultiSignature;
fn max_extrinsic_size() -> u32 {
BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
}
fn max_extrinsic_weight() -> Weight {
Weight::zero()
}
}
/// The chain where we are in tests.
pub struct ThisChain;
impl UnderlyingChainProvider for ThisChain {
type Chain = ThisUnderlyingChain;
}
impl ThisChainWithMessages for ThisChain {
type RuntimeOrigin = ThisChainCallOrigin;
}
impl BridgedChainWithMessages for ThisChain {}
/// Underlying chain of `BridgedChain`.
pub struct BridgedUnderlyingChain;
/// Some parachain under `BridgedChain` consensus.
pub struct BridgedUnderlyingParachain;
/// Runtime call of the `BridgedChain`.
#[derive(Decode, Encode)]
pub struct BridgedChainCall;
impl Chain for BridgedUnderlyingChain {
const ID: ChainId = *b"buch";
type BlockNumber = BridgedChainBlockNumber;
type Hash = BridgedChainHash;
type Hasher = BridgedChainHasher;
type Header = BridgedChainHeader;
type AccountId = BridgedChainAccountId;
type Balance = BridgedChainBalance;
type Nonce = u32;
type Signature = sp_runtime::MultiSignature;
fn max_extrinsic_size() -> u32 {
BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
}
fn max_extrinsic_weight() -> Weight {
Weight::zero()
}
}
impl ChainWithGrandpa for BridgedUnderlyingChain {
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
const MAX_AUTHORITIES_COUNT: u32 = 16;
const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8;
const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
const AVERAGE_HEADER_SIZE: u32 = 64;
}
impl Chain for BridgedUnderlyingParachain {
const ID: ChainId = *b"bupc";
type BlockNumber = BridgedChainBlockNumber;
type Hash = BridgedChainHash;
type Hasher = BridgedChainHasher;
type Header = BridgedChainHeader;
type AccountId = BridgedChainAccountId;
type Balance = BridgedChainBalance;
type Nonce = u32;
type Signature = sp_runtime::MultiSignature;
fn max_extrinsic_size() -> u32 {
BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
}
fn max_extrinsic_weight() -> Weight {
Weight::zero()
}
}
impl Parachain for BridgedUnderlyingParachain {
const PARACHAIN_ID: u32 = 42;
const MAX_HEADER_SIZE: u32 = 1_024;
}
/// The other, bridged chain, used in tests.
pub struct BridgedChain;
impl UnderlyingChainProvider for BridgedChain {
type Chain = BridgedUnderlyingChain;
}
impl ThisChainWithMessages for BridgedChain {
type RuntimeOrigin = BridgedChainOrigin;
}
impl BridgedChainWithMessages for BridgedChain {}
/// Run test within test externalities.
pub fn run_test(test: impl FnOnce()) {
sp_io::TestExternalities::new(Default::default()).execute_with(test)
}
// Copyright (C) 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/>.
//! Everything required to run benchmarks of parachains finality module.
#![cfg(feature = "runtime-benchmarks")]
use crate::{
messages_benchmarking::insert_header_to_grandpa_pallet,
messages_generation::grow_trie_leaf_value,
};
use bp_parachains::parachain_head_storage_key_at_source;
use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
use bp_runtime::{record_all_trie_keys, StorageProofSize};
use codec::Encode;
use frame_support::traits::Get;
use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use sp_std::prelude::*;
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};
/// Prepare proof of messages for the `receive_messages_proof` call.
///
/// In addition to returning valid messages proof, environment is prepared to verify this message
/// proof.
pub fn prepare_parachain_heads_proof<R, PI>(
parachains: &[ParaId],
parachain_head_size: u32,
size: StorageProofSize,
) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>)
where
R: pallet_bridge_parachains::Config<PI>
+ pallet_bridge_grandpa::Config<R::BridgesGrandpaPalletInstance>,
PI: 'static,
<R as pallet_bridge_grandpa::Config<R::BridgesGrandpaPalletInstance>>::BridgedChain:
bp_runtime::Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash>,
{
let parachain_head = ParaHead(vec![0u8; parachain_head_size as usize]);
// insert all heads to the trie
let mut parachain_heads = Vec::with_capacity(parachains.len());
let mut storage_keys = Vec::with_capacity(parachains.len());
let mut state_root = Default::default();
let mut mdb = MemoryDB::default();
{
let mut trie =
TrieDBMutBuilderV1::<RelayBlockHasher>::new(&mut mdb, &mut state_root).build();
// insert parachain heads
for (i, parachain) in parachains.into_iter().enumerate() {
let storage_key =
parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain);
let leaf_data = if i == 0 {
grow_trie_leaf_value(parachain_head.encode(), size)
} else {
parachain_head.encode()
};
trie.insert(&storage_key.0, &leaf_data)
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
storage_keys.push(storage_key);
parachain_heads.push((*parachain, parachain_head.hash()))
}
}
// generate heads storage proof
let proof = record_all_trie_keys::<LayoutV1<RelayBlockHasher>, _>(&mdb, &state_root)
.map_err(|_| "record_all_trie_keys has failed")
.expect("record_all_trie_keys should not fail in benchmarks");
let (relay_block_number, relay_block_hash) =
insert_header_to_grandpa_pallet::<R, R::BridgesGrandpaPalletInstance>(state_root);
(relay_block_number, relay_block_hash, ParaHeadsProof { storage_proof: proof }, parachain_heads)
}
# Kusama Bridge Hub <> Polkadot Bridge Hub deployments
This folder contains some information and useful stuff from our other test deployment - between Kusama and Polkadot
bridge hubs. The code and other helpful information can be found in
[this document](https://github.com/paritytech/polkadot-sdk/blob/master/bridges/docs/polkadot-kusama-bridge-overview.md)
and in the [code](https://github.com/polkadot-fellows/runtimes/tree/main/system-parachains/bridge-hubs).
## Grafana Alerts and Dashboards
JSON model for Grafana alerts and dashobards that we use, may be found in the [dasboard/grafana](./dashboard/grafana/)
folder.
**Dashboards:**
- kusama-polkadot-maintenance-dashboard.json
- relay-kusama-to-polkadot-messages-dashboard.json
- relay-polkadot-to-kusama-messages-dashboard.json
(exported JSON directly from https://grafana.teleport.parity.io/dashboards/f/eblDiw17z/Bridges)
**Alerts:**
- bridge-kusama-polkadot-alerts.json https://grafana.teleport.parity.io/alerting/list
_Note: All json files are formatted with `jq . file.json`._
{
"apiVersion": 1,
"groups": [
{
"orgId": 1,
"name": "Bridge Kusama <> Polkadot",
"folder": "bridges",
"interval": "1m",
"rules": [
{
"uid": "adizmaavld2psc",
"title": "Polkadot -> KusamaBridgeHub finality sync lags (00000001)",
"condition": "D",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "max(increase(Polkadot_to_BridgeHubKusama_Sync_best_source_at_target_block_number{domain=\"parity-chains\"}[24h]))",
"instant": false,
"interval": "",
"intervalMs": 30000,
"legendFormat": "At Polkadot",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "C",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"C"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "A",
"intervalMs": 1000,
"maxDataPoints": 43200,
"reducer": "max",
"refId": "C",
"type": "reduce"
}
},
{
"refId": "D",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
5000
],
"type": "lt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"D"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "C",
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "D",
"type": "threshold"
}
}
],
"dasboardUid": "zqjpkXxnk",
"panelId": 2,
"noDataState": "OK",
"execErrState": "OK",
"for": "5m",
"annotations": {
"__dashboardUid__": "zqjpkXxnk",
"__panelId__": "2",
"summary": "Less than 5000 Polkadot headers (~1/2 era) have been synced to KusamaBridgeHub in last 25 hours. Relay is not running?"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "cdiznm0i2tslca",
"title": "PolkadotBridgeHub -> KusamaBridgeHub delivery lags (00000001)",
"condition": "B",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "((vector(0) and ((BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_generated\"} > on () BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}))) or vector(1)) + on () increase(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[10m]) * on () ((vector(1) and ((BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_generated\"} > on () BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}))) or vector(0))",
"instant": false,
"interval": "",
"intervalMs": 30000,
"legendFormat": "Undelivered messages",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 600,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
1,
0
],
"type": "lt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"A"
]
},
"reducer": {
"params": [],
"type": "max"
},
"type": "query"
}
],
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
"expression": "",
"hide": false,
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "B",
"type": "classic_conditions"
}
}
],
"dasboardUid": "zqjpkXxnk",
"panelId": 14,
"noDataState": "OK",
"execErrState": "OK",
"for": "10m",
"annotations": {
"__dashboardUid__": "zqjpkXxnk",
"__panelId__": "14",
"summary": "Messages from PolkadotBridgeHub to KusamaBridgeHub (00000001) are either not delivered, or are delivered with lags"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "adizouqsgd62od",
"title": "PolkadotBridgeHub -> KusamaBridgeHub confirmation lags (00000001)",
"condition": "B",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))",
"instant": false,
"interval": "",
"intervalMs": 30000,
"legendFormat": "Unconfirmed messages",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
50,
0
],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"A"
]
},
"reducer": {
"params": [],
"type": "max"
},
"type": "query"
}
],
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
"expression": "",
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "B",
"type": "classic_conditions"
}
}
],
"dasboardUid": "zqjpkXxnk",
"panelId": 16,
"noDataState": "OK",
"execErrState": "OK",
"for": "10m",
"annotations": {
"__dashboardUid__": "zqjpkXxnk",
"__panelId__": "16",
"summary": "Messages from PolkadotBridgeHub to KusamaBridgeHub (00000001) are either not confirmed, or are confirmed with lags"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "fdizp9l7o5rswf",
"title": "PolkadotBridgeHub -> KusamaBridgeHub reward lags (00000002)",
"condition": "B",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))",
"instant": false,
"interval": "",
"intervalMs": 30000,
"legendFormat": "Unconfirmed rewards",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
10,
0
],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"A"
]
},
"reducer": {
"params": [],
"type": "min"
},
"type": "query"
}
],
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
"expression": "",
"hide": false,
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "B",
"type": "classic_conditions"
}
}
],
"dasboardUid": "zqjpkXxnk",
"panelId": 18,
"noDataState": "OK",
"execErrState": "OK",
"for": "10m",
"annotations": {
"__dashboardUid__": "zqjpkXxnk",
"__panelId__": "18",
"summary": "Rewards for messages from PolkadotBridgeHub to KusamaBridgeHub (00000001) are either not confirmed, or are confirmed with lags"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "bdizqaq47emf4f",
"title": "Kusama -> PolkadotBridgeHub finality sync lags (00000001)",
"condition": "D",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "max(increase(Kusama_to_BridgeHubPolkadot_Sync_best_source_at_target_block_number{domain=\"parity-chains\"}[24h]))",
"instant": false,
"interval": "",
"intervalMs": 30000,
"legendFormat": "At Kusama",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "C",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"C"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "A",
"intervalMs": 1000,
"maxDataPoints": 43200,
"reducer": "max",
"refId": "C",
"type": "reduce"
}
},
{
"refId": "D",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
5000
],
"type": "lt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"D"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "C",
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "D",
"type": "threshold"
}
}
],
"dasboardUid": "tkpc6_bnk",
"panelId": 6,
"noDataState": "OK",
"execErrState": "OK",
"for": "5m",
"annotations": {
"__dashboardUid__": "tkpc6_bnk",
"__panelId__": "6",
"summary": "Less than 5000 Kusama headers (~1/2 era) have been synced to PolkadotBridgeHub in last 25 hours. Relay is not running?"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "adizvdppi4cu8b",
"title": "KusamaBridgeHub -> PolkadotBridgeHub delivery lags (00000001)",
"condition": "A",
"data": [
{
"refId": "B",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "((vector(0) and ((BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_generated\"} > on () BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}))) or vector(1)) + on () increase(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[10m]) * on () ((vector(1) and ((BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_generated\"} > on () BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}))) or vector(0))",
"instant": false,
"interval": "",
"intervalMs": 30000,
"legendFormat": "1 if all messages are delivered. Otherwise - number of delivered messages in last 10m",
"maxDataPoints": 43200,
"range": true,
"refId": "B"
}
},
{
"refId": "A",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
1,
0
],
"type": "lt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"B"
]
},
"reducer": {
"params": [],
"type": "max"
},
"type": "query"
}
],
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
"expression": "",
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "A",
"type": "classic_conditions"
}
}
],
"dasboardUid": "tkpc6_bnk",
"panelId": 12,
"noDataState": "OK",
"execErrState": "OK",
"for": "10m",
"annotations": {
"__dashboardUid__": "tkpc6_bnk",
"__panelId__": "12",
"summary": "Messages from KusamaBridgeHub to PolkadotBridgeHub (00000001) are either not delivered, or are delivered with lags"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "ddizvjxnpwa2ob",
"title": "KusamaBridgeHub -> PolkadotBridgeHub confirmation lags (00000001)",
"condition": "B",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "scalar(max_over_time(KusamaBridgeHub_to_PolkadotBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(KusamaBridgeHub_to_PolkadotBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))",
"instant": false,
"interval": "",
"intervalMs": 30000,
"legendFormat": "Unconfirmed messages",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
50,
0
],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"A"
]
},
"reducer": {
"params": [],
"type": "min"
},
"type": "query"
}
],
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
"expression": "",
"hide": false,
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "B",
"type": "classic_conditions"
}
}
],
"dasboardUid": "tkpc6_bnk",
"panelId": 14,
"noDataState": "OK",
"execErrState": "OK",
"for": "10m",
"annotations": {
"__dashboardUid__": "tkpc6_bnk",
"__panelId__": "14"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "fdizvp3bz6oe8c",
"title": "KusamaBridgeHub -> PolkadotBridgeHub reward lags (00000002)",
"condition": "B",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "scalar(max_over_time(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))",
"interval": "",
"intervalMs": 30000,
"legendFormat": "Unconfirmed rewards",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
10,
0
],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"A"
]
},
"reducer": {
"params": [],
"type": "min"
},
"type": "query"
}
],
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
"expression": "",
"hide": false,
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "B",
"type": "classic_conditions"
}
}
],
"dasboardUid": "tkpc6_bnk",
"panelId": 15,
"noDataState": "OK",
"execErrState": "OK",
"for": "10m",
"annotations": {
"__dashboardUid__": "tkpc6_bnk",
"__panelId__": "15",
"summary": "Rewards for messages from KusamaBridgeHub to PolkadotBridgeHub (00000001) are either not confirmed, or are confirmed with lags"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "edizwf9kbhhxcc",
"title": "KusamaBridgeHub <> PolkadotBridgeHub relay (00000001) node is down",
"condition": "C",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"exemplar": false,
"expr": "up{domain=\"parity-chains\",container=\"bridges-common-relay\"}",
"instant": false,
"interval": "",
"intervalMs": 30000,
"legendFormat": "Is relay running",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"B"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "A",
"intervalMs": 1000,
"maxDataPoints": 43200,
"reducer": "min",
"refId": "B",
"type": "reduce"
}
},
{
"refId": "C",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
1
],
"type": "lt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"C"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "B",
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "C",
"type": "threshold"
}
}
],
"dasboardUid": "UFsgpJtVz",
"panelId": 16,
"noDataState": "OK",
"execErrState": "OK",
"for": "5m",
"annotations": {
"__dashboardUid__": "UFsgpJtVz",
"__panelId__": "16",
"summary": "KusamaBridgeHub <> PolkadotBridgeHub relay (00000001) node is down"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "adizwlq6yk83kc",
"title": "Version guard has aborted KusamaBridgeHub <> PolkadotBridgeHub relay (00000001)",
"condition": "C",
"data": [
{
"refId": "A",
"queryType": "range",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "P7028671862427D8D",
"model": {
"datasource": {
"type": "loki",
"uid": "P7028671862427D8D"
},
"editorMode": "code",
"expr": "count_over_time({container=\"bridges-common-relay\"} |= `Aborting relay` [1m])",
"intervalMs": 1000,
"legendFormat": "Errors per minute",
"maxDataPoints": 43200,
"queryType": "range",
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"B"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "A",
"intervalMs": 1000,
"maxDataPoints": 43200,
"reducer": "max",
"refId": "B",
"type": "reduce"
}
},
{
"refId": "C",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
0
],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"C"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "B",
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "C",
"type": "threshold"
}
}
],
"dasboardUid": "UFsgpJtVz",
"panelId": 11,
"noDataState": "OK",
"execErrState": "OK",
"for": "0s",
"annotations": {
"__dashboardUid__": "UFsgpJtVz",
"__panelId__": "11",
"summary": "The KusamaBridgeHub <> PolkadotBridgeHub relay (00000001) has been aborted by version guard - i.e. one of chains has been upgraded and relay wasn't redeployed"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "fdizwsne5dz40b",
"title": "Kusama headers mismatch",
"condition": "C",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Kusama_to_BridgeHubPolkadot_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-chains\"}",
"interval": "",
"intervalMs": 30000,
"legendFormat": "Best BridgeHubKusama header at BridgeHubPolkadot doesn't match the same header of BridgeHubKusama",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"B"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "A",
"intervalMs": 1000,
"maxDataPoints": 43200,
"reducer": "last",
"refId": "B",
"type": "reduce"
}
},
{
"refId": "C",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
0
],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"C"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "B",
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "C",
"type": "threshold"
}
}
],
"dasboardUid": "UFsgpJtVz",
"panelId": 12,
"noDataState": "OK",
"execErrState": "OK",
"for": "10m",
"annotations": {
"__dashboardUid__": "UFsgpJtVz",
"__panelId__": "12",
"summary": "Best Kusama header at BridgeHubPolkadot (00000001) doesn't match the same header at Kusama"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "ddizwvw3dlzi8e",
"title": "Polkadot headers mismatch",
"condition": "C",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Polkadot_to_BridgeHubKusama_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-chains\"}",
"interval": "",
"intervalMs": 30000,
"legendFormat": "Best BridgeHubKusama header at BridgeHubPolkadot doesn't match the same header of BridgeHubKusama",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"B"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "A",
"intervalMs": 1000,
"maxDataPoints": 43200,
"reducer": "last",
"refId": "B",
"type": "reduce"
}
},
{
"refId": "C",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
0
],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"C"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "__expr__"
},
"expression": "B",
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "C",
"type": "threshold"
}
}
],
"dasboardUid": "UFsgpJtVz",
"panelId": 13,
"noDataState": "NoData",
"execErrState": "Error",
"for": "10m",
"annotations": {
"__dashboardUid__": "UFsgpJtVz",
"__panelId__": "13",
"summary": "Best Polkadot header at BridgeHubKusama (00000001) doesn't match the same header at Polkadot"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "bdizx0xdiomwwc",
"title": "BridgeHubKusama headers mismatch",
"condition": "B",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-chains\"}",
"interval": "",
"intervalMs": 30000,
"legendFormat": "Best BridgeHubKusama header at BridgeHubPolkadot doesn't match the same header of BridgeHubKusama",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
0,
0
],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"A"
]
},
"reducer": {
"params": [],
"type": "max"
},
"type": "query"
}
],
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
"expression": "",
"hide": false,
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "B",
"type": "classic_conditions"
}
}
],
"dasboardUid": "UFsgpJtVz",
"panelId": 2,
"noDataState": "OK",
"execErrState": "OK",
"for": "10m",
"annotations": {
"__dashboardUid__": "UFsgpJtVz",
"__panelId__": "2",
"summary": "Best BridgeHubKusama header at BridgeHubPolkadot (00000001) doesn't match the same header at BridgeHubKusama"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "fdizx4hrhg2yod",
"title": "BridgeHubPolkadot headers mismatch",
"condition": "B",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-chains\"}",
"interval": "",
"intervalMs": 30000,
"legendFormat": "Best BridgeHubKusama header at BridgeHubPolkadot doesn't match the same header of BridgeHubKusama",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
0,
0
],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"A"
]
},
"reducer": {
"params": [],
"type": "max"
},
"type": "query"
}
],
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
"expression": "",
"hide": false,
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "B",
"type": "classic_conditions"
}
}
],
"dasboardUid": "UFsgpJtVz",
"panelId": 3,
"noDataState": "OK",
"execErrState": "OK",
"for": "10m",
"annotations": {
"__dashboardUid__": "UFsgpJtVz",
"__panelId__": "3",
"summary": "Best BridgeHubPolkadot header at BridgeHubKusama (00000001) doesn't match the same header at BridgeHubPolkadot"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "cdizxaawyvldsb",
"title": "Relay balances at KusamaBridgeHub",
"condition": "B",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "last_over_time(at_BridgeHubKusama_relay_BridgeHubPolkadotMessages_balance{domain=\"parity-chains\"}[1h])",
"instant": false,
"interval": "",
"intervalMs": 30000,
"legendFormat": "Messages Relay Balance",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
2,
0
],
"type": "lt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"A"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
"expression": "",
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "B",
"type": "classic_conditions"
}
}
],
"dasboardUid": "UFsgpJtVz",
"panelId": 5,
"noDataState": "NoData",
"execErrState": "Error",
"for": "10m",
"annotations": {
"__dashboardUid__": "UFsgpJtVz",
"__panelId__": "5",
"summary": "With-PolkadotBridgeHub messages relay balance at KusamaBridgeHub (00000001) is too low"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
},
{
"uid": "fdizxtuxuza4gd",
"title": "Relay balances at PolkadotBridgeHub",
"condition": "B",
"data": [
{
"refId": "A",
"relativeTimeRange": {
"from": 21600,
"to": 0
},
"datasourceUid": "PC96415006F908B67",
"model": {
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "last_over_time(at_BridgeHubPolkadot_relay_BridgeHubKusamaMessages_balance{domain=\"parity-chains\"}[1h])",
"instant": false,
"interval": "",
"intervalMs": 30000,
"legendFormat": "Messages Relay Balance",
"maxDataPoints": 43200,
"range": true,
"refId": "A"
}
},
{
"refId": "B",
"relativeTimeRange": {
"from": 0,
"to": 0
},
"datasourceUid": "__expr__",
"model": {
"conditions": [
{
"evaluator": {
"params": [
10,
0
],
"type": "lt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"A"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
"expression": "",
"hide": false,
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "B",
"type": "classic_conditions"
}
}
],
"dasboardUid": "UFsgpJtVz",
"panelId": 6,
"noDataState": "OK",
"execErrState": "OK",
"for": "10m",
"annotations": {
"__dashboardUid__": "UFsgpJtVz",
"__panelId__": "6",
"summary": "With-KusamaBridgeHub messages relay balance at PolkadotBridgeHub (00000001) is too low"
},
"labels": {
"matrix_room": "FqmgUhjOliBGoncGwm"
},
"isPaused": false
}
]
}
]
}
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 4107,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 5,
"x": 0,
"y": 0
},
"id": 8,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"text": {},
"textMode": "name",
"wideLayout": true
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"exemplar": false,
"expr": "substrate_relay_build_info{domain=\"parity-chains\"}",
"instant": true,
"legendFormat": "{{commit}}",
"range": false,
"refId": "A"
}
],
"title": "Relay build commit",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 4,
"x": 5,
"y": 0
},
"id": 9,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"text": {},
"textMode": "name",
"wideLayout": true
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"exemplar": false,
"expr": "substrate_relay_build_info{domain=\"parity-chains\"}",
"instant": true,
"legendFormat": "{{version}}",
"range": false,
"refId": "A"
}
],
"title": "Relay build version",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [
{
"options": {
"0": {
"color": "red",
"index": 1,
"text": "No"
},
"1": {
"color": "green",
"index": 0,
"text": "Yes"
}
},
"type": "value"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 4,
"x": 9,
"y": 0
},
"id": 15,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"exemplar": false,
"expr": "up{domain=\"parity-chains\",container=\"bridges-common-relay\"}",
"instant": false,
"legendFormat": "Is relay running",
"range": true,
"refId": "A"
}
],
"title": "Is relay running?",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 5,
"x": 13,
"y": 0
},
"id": 16,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "9.3.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"exemplar": false,
"expr": "up{domain=\"parity-chains\",container=\"bridges-common-relay\"}",
"instant": false,
"legendFormat": "Is relay running",
"range": true,
"refId": "A"
}
],
"title": "Is relay running? (for alert)",
"type": "timeseries"
},
{
"datasource": {
"type": "loki",
"uid": "P7028671862427D8D"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 18,
"x": 0,
"y": 5
},
"id": 11,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "loki",
"uid": "P7028671862427D8D"
},
"editorMode": "code",
"expr": "count_over_time({container=\"bridges-common-relay\"} |~ `(?i)(warn|error|fail)` [1m])",
"legendFormat": "Errors per minute",
"queryType": "range",
"refId": "A"
}
],
"title": "Relay errors/warnings per minute",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 9,
"x": 0,
"y": 14
},
"id": 12,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Kusama_to_BridgeHubPolkadot_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-chains\"}",
"legendFormat": "Best BridgeHubKusama header at BridgeHubPolkadot doesn't match the same header of BridgeHubKusama",
"range": true,
"refId": "A"
}
],
"title": "Kusama headers mismatch",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 9,
"x": 9,
"y": 14
},
"id": 13,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Polkadot_to_BridgeHubKusama_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-chains\"}",
"legendFormat": "Best BridgeHubKusama header at BridgeHubPolkadot doesn't match the same header of BridgeHubKusama",
"range": true,
"refId": "A"
}
],
"title": "Polkadot headers mismatch",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 9,
"x": 0,
"y": 21
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-chains\"}",
"legendFormat": "Best BridgeHubKusama header at BridgeHubPolkadot doesn't match the same header of BridgeHubKusama",
"range": true,
"refId": "A"
}
],
"title": "BridgeHubKusama headers mismatch",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 9,
"x": 9,
"y": 21
},
"id": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-chains\"}",
"legendFormat": "Best BridgeHubKusama header at BridgeHubPolkadot doesn't match the same header of BridgeHubKusama",
"range": true,
"refId": "A"
}
],
"title": "BridgeHubPolkadot headers mismatch",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 9,
"x": 0,
"y": 28
},
"id": 5,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "at_BridgeHubKusama_relay_BridgeHubPolkadotMessages_balance{domain=\"parity-chains\"}",
"legendFormat": "Messages Relay Balance",
"range": true,
"refId": "A"
}
],
"title": "Relay balances at KusamaBridgeHub",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 9,
"x": 9,
"y": 28
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "at_BridgeHubPolkadot_relay_BridgeHubKusamaMessages_balance{domain=\"parity-chains\"}",
"legendFormat": "Messages Relay Balance",
"range": true,
"refId": "A"
}
],
"title": "Relay balances at PolkadotBridgeHub",
"type": "timeseries"
}
],
"refresh": "5s",
"schemaVersion": 39,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "BridgeHubKusama <> BridgeHubPolkadot maintenance (00000001)",
"uid": "UFsgpJtVz",
"version": 6,
"weekStart": ""
}
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 4105,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 0,
"y": 0
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Kusama_to_BridgeHubPolkadot_Sync_best_source_block_number{domain=\"parity-chains\"}",
"legendFormat": "At Kusama",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Kusama_to_BridgeHubPolkadot_Sync_best_source_at_target_block_number{domain=\"parity-chains\"}",
"hide": false,
"legendFormat": "At BridgeHubPolkadot",
"range": true,
"refId": "B"
}
],
"title": "Best finalized Kusama headers",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 6,
"y": 0
},
"id": 7,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Polkadot_to_BridgeHubKusama_Sync_best_source_block_number{domain=\"parity-chains\"}",
"legendFormat": "At Polkadot",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Polkadot_to_BridgeHubKusama_Sync_best_source_at_target_block_number{domain=\"parity-chains\"}",
"hide": false,
"legendFormat": "At BridgeHubKusama",
"range": true,
"refId": "B"
}
],
"title": "Best finalized Polkadot headers",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"exemplar": true,
"expr": "BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_best_source_block_number{domain=\"parity-chains\"}",
"interval": "",
"legendFormat": "At KusamaBridgeHub",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_best_source_at_target_block_number{domain=\"parity-chains\"}",
"hide": false,
"legendFormat": "At PolkadotBridgeHub",
"range": true,
"refId": "B"
}
],
"title": "Best finalized KusamaBridgeHub headers",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 18,
"y": 0
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"exemplar": true,
"expr": "BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_best_target_block_number{domain=\"parity-chains\"}",
"interval": "",
"legendFormat": "At PolkadotBridgeHub",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_best_target_at_source_block_number{domain=\"parity-chains\"}",
"hide": false,
"legendFormat": "At KusamaBridgeHub",
"range": true,
"refId": "B"
}
],
"title": "Best finalized PolkadotBridgeHub headers",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 9,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "label_replace(label_replace(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\", type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from BridgeHubKusama\", \"type\", \"source_latest_generated\"), \"type\", \"Latest BridgeHubKusama message received by BridgeHubPolkadot\", \"type\", \"target_latest_received\")",
"legendFormat": "{{type}}",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "increase(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\", type=~\"source_latest_generated\"}[24h])",
"hide": true,
"legendFormat": "Messages generated in last 24h",
"range": true,
"refId": "B"
}
],
"title": "Delivery race (00000001)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 10,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "label_replace(label_replace(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest delivery confirmation from BridgeHubPolkadot to BridgeHubKusama\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest BridgeHubKusama message received by BridgeHubPolkadot\", \"type\", \"target_latest_received\")",
"legendFormat": "{{type}}",
"range": true,
"refId": "A"
}
],
"title": "Confirmations race (00000001)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 0,
"y": 16
},
"id": 12,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "scalar(max_over_time(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[2m]) OR on() vector(0))",
"legendFormat": "Undelivered messages",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "((vector(0) and ((BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_generated\"} > on () BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}))) or vector(1)) + on () increase(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[10m]) * on () ((vector(1) and ((BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_generated\"} > on () BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}))) or vector(0))",
"hide": true,
"legendFormat": "1 if all messages are delivered. Otherwise - number of delivered messages in last 10m",
"range": true,
"refId": "B"
}
],
"title": "Delivery race lags (00000001)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 8,
"y": 16
},
"id": 14,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "scalar(max_over_time(KusamaBridgeHub_to_PolkadotBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(KusamaBridgeHub_to_PolkadotBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))",
"legendFormat": "Unconfirmed messages",
"range": true,
"refId": "A"
}
],
"title": "Confirmations race lags (00000001)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 16,
"y": 16
},
"id": 15,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "scalar(max_over_time(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))",
"legendFormat": "Unconfirmed rewards",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "(scalar(max_over_time(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubKusama_to_BridgeHubPolkadot_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[2m]) OR on() vector(0))",
"hide": true,
"legendFormat": "__auto",
"range": true,
"refId": "B"
}
],
"title": "Reward lags (00000001)",
"type": "timeseries"
}
],
"refresh": "5s",
"schemaVersion": 39,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "BridgeHubKusama to BridgeHubPolkadot (00000001)",
"uid": "tkpc6_bnk",
"version": 2,
"weekStart": ""
}
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 4106,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 0,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Polkadot_to_BridgeHubKusama_Sync_best_source_block_number{domain=\"parity-chains\"}",
"legendFormat": "At Polkadot",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Polkadot_to_BridgeHubKusama_Sync_best_source_at_target_block_number{domain=\"parity-chains\"}",
"hide": false,
"legendFormat": "At BridgeHubKusama",
"range": true,
"refId": "B"
}
],
"title": "Best finalized Polkadot headers",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 6,
"y": 0
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Kusama_to_BridgeHubPolkadot_Sync_best_source_block_number{domain=\"parity-chains\"}",
"legendFormat": "At Kusama",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "Kusama_to_BridgeHubPolkadot_Sync_best_source_at_target_block_number{domain=\"parity-chains\"}",
"hide": false,
"legendFormat": "At PolkadotBridgeHub",
"range": true,
"refId": "B"
}
],
"title": "Best finalized Kusama headers",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 12,
"y": 0
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"exemplar": true,
"expr": "BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_best_source_block_number{domain=\"parity-chains\"}",
"interval": "",
"legendFormat": "At PolkadotBridgeHub",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_best_source_at_target_block_number{domain=\"parity-chains\"}",
"hide": false,
"legendFormat": "At KusamaBridgeHub",
"range": true,
"refId": "B"
}
],
"title": "Best finalized PolkadotBridgeHub headers",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 18,
"y": 0
},
"id": 8,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"exemplar": true,
"expr": "BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_best_target_block_number{domain=\"parity-chains\"}",
"interval": "",
"legendFormat": "At KusamaBridgeHub",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_best_target_at_source_block_number{domain=\"parity-chains\"}",
"hide": false,
"legendFormat": "At PolkadotBridgeHub",
"range": true,
"refId": "B"
}
],
"title": "Best finalized KusamaBridgeHub headers",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 10,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "label_replace(label_replace(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from BridgeHubPolkadot\", \"type\", \"source_latest_generated\"), \"type\", \"Latest BridgeHubPolkadot message received by BridgeHubKusama\", \"type\", \"target_latest_received\")",
"legendFormat": "{{type}}",
"range": true,
"refId": "A"
}
],
"title": "Delivery race (00000001)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 12,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "label_replace(label_replace(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest delivery confirmation from BridgeHubKusama to BridgeHubPolkadot\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest BridgeHubPolkadot message received by BridgeHubKusama\", \"type\", \"target_latest_received\")",
"legendFormat": "{{type}}",
"range": true,
"refId": "A"
}
],
"title": "Confirmations race (00000001)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 0,
"y": 16
},
"id": 14,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[2m]) OR on() vector(0))",
"legendFormat": "Undelivered messages",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "((vector(0) and ((BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_generated\"} > on () BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}))) or vector(1)) + on () increase(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[10m]) * on () ((vector(1) and ((BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_generated\"} > on () BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}))) or vector(0))",
"hide": true,
"legendFormat": "1 if all messages are delivered. Otherwise - number of delivered messages in last 10m",
"range": true,
"refId": "B"
}
],
"title": "Delivery race lags (00000001)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 8,
"y": 16
},
"id": 16,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))",
"legendFormat": "Unconfirmed messages",
"range": true,
"refId": "A"
}
],
"title": "Confirmations race lags (00000001)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 16,
"y": 16
},
"id": 18,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))",
"legendFormat": "Unconfirmed rewards",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "PC96415006F908B67"
},
"editorMode": "code",
"expr": "(scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubPolkadot_to_BridgeHubKusama_MessageLane_00000001_lane_state_nonces{domain=\"parity-chains\",type=\"target_latest_received\"}[2m]) OR on() vector(0))",
"hide": true,
"legendFormat": "__auto",
"range": true,
"refId": "B"
}
],
"title": "Reward lags (00000001)",
"type": "timeseries"
}
],
"refresh": "5s",
"schemaVersion": 39,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "BridgeHubPolkadot to BridgeHubKusama (00000001)",
"uid": "zqjpkXxnk",
"version": 2,
"weekStart": ""
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Complex Relay</title>
</head>
<body>
<h1>Complex Relay</h1>
<p>
Both Source Chain and Target Chains have Bridge Messages pallets deployed. They also have required
finality pallets deployed - we don't care about finality type here - they can be either Bridge GRANDPA,
or Bridge Parachains finality pallets, or any combination of those.<br/>
</p>
<p>
There are 4-6 relayer subprocesses inside the Complex Relayer. They include two message relayers,
serving the lane in both directions and 2-4 Complex Relayers (depending on the finality type of Source
and Target Chains).<br/>
</p>
<p>
The following diagram shows the way the complex relayer serves the lane in single direction. Everything
below may be applied to the opposite direction if you'll swap the Source and Target Chains.
</p>
<div class="mermaid">
sequenceDiagram
participant Source Chain
participant Complex Relayer
participant Target Chain
Note right of Source Chain: Finalized: 480, Target Finalized: 50, Sent Messages: 42, Confirmed Messages: 42
Note left of Target Chain: Finalized: 60, Source Finalized: 420, Received Messages: 42
Source Chain ->> Source Chain: someone Sends Message 43
Source Chain ->> Source Chain: Import and Finalize Block 481
Source Chain ->> Complex Relayer: notes new outbound message 43 at Source Chain Block 481
Note right of Complex Relayer: can't deliver message 43, Source Chain Block 481 is not relayed
Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Source Chain Block 481
Source Chain ->> Complex Relayer: Read Finality Proof of Block 481
Complex Relayer ->> Target Chain: Submit Finality Proof of Block 481
Target Chain ->> Target Chain: Import and Finalize Block 61
Note left of Target Chain: Finalized: 61, Source Finalized: 481, Received Messages: 42
Source Chain ->> Complex Relayer: Read Proof of Message 43 at Block 481
Complex Relayer ->> Target Chain: Submit Proof of Message 43 at Block 481
Target Chain ->> Target Chain: Import and Finalize Block 62
Note left of Target Chain: Finalized: 62, Source Finalized: 481, Received Messages: { rewarded: 42, messages-relayer-account: [43] }
Target Chain ->> Complex Relayer: notes new unrewarded relayer at Target Chain Block 62
Note right of Complex Relayer: can't relay delivery confirmations because Target Chain Block 62 is not relayed
Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Target Chain Block 62
Target Chain ->> Complex Relayer: Read Finality Proof of Block 62
Complex Relayer ->> Source Chain: Submit Finality Proof of Block 62
Source Chain ->> Source Chain: Import and Finalize Block 482
Note right of Source Chain: Finalized: 482, Target Finalized: 62, Confirmed Messages: 42
Target Chain ->> Complex Relayer: Read Proof of Message 43 Delivery at Block 62
Complex Relayer ->> Source Chain: Submit Proof of Message 43 Delivery at Block 612
Source Chain ->> Source Chain: rewards messages-relayer-account for delivering message [43]
Source Chain ->> Source Chain: prune delivered message 43 from runtime storage
Note right of Source Chain: Finalized: 482, Target Finalized: 61, Confirmed Messages: 43
Source Chain ->> Source Chain: someone Sends Message 44
Source Chain ->> Source Chain: Import and Finalize Block 483
Source Chain ->> Complex Relayer: notes new outbound message 44 at Source Chain Block 483 and new confirmed message 43
Note right of Complex Relayer: can't deliver message 44, Source Chain Block 483 is not relayed
Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Source Chain Block 483
Source Chain ->> Complex Relayer: Read Finality Proof of Block 483
Complex Relayer ->> Target Chain: Submit Finality Proof of Block 483
Target Chain ->> Target Chain: Import and Finalize Block 63
Note left of Target Chain: Finalized: 63, Source Finalized: 483, Received Messages: { rewarded: 42, messages-relayer-account: [43] }
Source Chain ->> Complex Relayer: Read Proof of Message 44 and Proof of Message 43 reward at Block 483
Complex Relayer ->> Target Chain: Submit Proof of Message 44 and Proof of Message 43 reward at Block 483
Target Chain ->> Target Chain: Import and Finalize Block 64
Note left of Target Chain: Finalized: 64, Source Finalized: 483, Received Messages: { rewarded: 43, messages-relayer-account: [44] }-->
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/mermaid.min.js"></script>
<script>mermaid.initialize({startOnLoad: true})</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>GRANDPA Finality Relay</title>
</head>
<body>
<h1>GRANDPA Finality Relay</h1>
<p>
Source Chain is running GRANDPA Finality Gadget. Bridge GRANDPA finality pallet is deployed at
Target Chain runtime. Relayer is configured to relay Source Chain finality to Target Chain.
</p>
<div class="mermaid">
sequenceDiagram
participant Source Chain
participant Relayer
participant Target Chain
Note left of Source Chain: Best: 500, Finalized: 480, Authorities Set Index: 42
Note right of Target Chain: Uninitialized
Source Chain ->> Relayer: Read Initialization Data
Relayer ->> Target Chain: Initialize Bridge GRANDPA Finality Pallet
Note right of Target Chain: Finalized: 480, Authorities Set Index: 42
Source Chain ->> Source Chain: Import Block 501
Source Chain ->> Source Chain: Import Block 502
Source Chain ->> Source Chain: Finalize Block 495
Source Chain ->> Relayer: Read Finality Proof of Block 495
Relayer ->> Target Chain: Finality Proof of Block 495
Note right of Target Chain: Finalized: 495, Authorities Set Index: 42
Source Chain ->> Source Chain: Import Block 503 that changes Authorities Set to 43
Source Chain ->> Source Chain: Finalize Block 500
Note left of Relayer: Relayer Misses Finality Notification for Block 500
Source Chain ->> Source Chain: Import Block 504
Source Chain ->> Source Chain: Finalize Mandatory Block 503
Source Chain ->> Source Chain: Finalize Block 504
Source Chain ->> Relayer: Read Finality Proof of Mandatory Block 503
Relayer ->> Target Chain: Finality Proof of Block 503
Note right of Target Chain: Finalized: 503, Authorities Set Index: 43
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/mermaid.min.js"></script>
<script>mermaid.initialize({startOnLoad: true})</script>
</body>
</html>
# High-Level Bridge Documentation
This document gives a brief, abstract description of main components that may be found in this repository. If you want
to see how we're using them to build Rococo <> Westend (Kusama <> Polkadot) bridge, please refer to the [Polkadot <>
Kusama Bridge](./polkadot-kusama-bridge-overview.md).
## Purpose
This repo contains all components required to build a trustless connection between standalone Substrate chains, that are
using GRANDPA finality, their parachains or any combination of those. On top of this connection, we offer a messaging
pallet that provides means to organize messages exchange.
On top of that layered infrastructure, anyone may build their own bridge applications - e.g. [XCM
messaging](./polkadot-kusama-bridge-overview.md), [encoded calls
messaging](https://github.com/paritytech/parity-bridges-common/releases/tag/encoded-calls-messaging) and so on.
## Terminology
Even though we support (and require) two-way bridging, the documentation will generally talk about a one-sided
interaction. That's to say, we will only talk about syncing finality proofs and messages from a _source_ chain to a
_target_ chain. This is because the two-sided interaction is really just the one-sided interaction with the source and
target chains switched.
The bridge has both on-chain (pallets) and offchain (relayers) components.
## On-chain components
On-chain bridge components are pallets that are deployed at the chain runtime. Finality pallets require deployment at
the target chain, while messages pallet needs to be deployed at both, source and target chains.
### Bridge GRANDPA Finality Pallet
A GRANDPA light client of the source chain built into the target chain's runtime. It provides a "source of truth" about
the source chain headers which have been finalized. This is useful for higher level applications.
The pallet tracks current GRANDPA authorities set and only accepts finality proofs (GRANDPA justifications), generated
by the current authorities set. The GRANDPA protocol itself requires current authorities set to generate explicit
justification for the header that enacts next authorities set. Such headers and their finality proofs are called
mandatory in the pallet and relayer pays no fee for such headers submission.
The pallet does not require all headers to be imported or provided. The relayer itself chooses which headers he wants to
submit (with the exception of mandatory headers).
More: [pallet level documentation and code](../modules/grandpa/).
### Bridge Parachains Finality Pallet
Parachains are not supposed to have their own finality, so we can't use bridge GRANDPA pallet to verify their finality
proofs. Instead, they rely on their relay chain finality. The parachain header is considered final, when it is accepted
by the [`paras`
pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras)
at its relay chain. Obviously, the relay chain block, where it is accepted, must also be finalized by the relay chain
GRANDPA gadget.
That said, the bridge parachains pallet accepts storage proof of one or several parachain heads, inserted to the
[`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642)
map of the [`paras`
pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras).
To verify this storage proof, the pallet uses relay chain header, imported earlier by the bridge GRANDPA pallet.
The pallet may track multiple parachains at once and those parachains may use different primitives. So the parachain
header decoding never happens at the pallet level. For maintaining the headers order, the pallet uses relay chain header
number.
More: [pallet level documentation and code](../modules/parachains/).
### Bridge Messages Pallet
The pallet is responsible for queuing messages at the source chain and receiving the messages proofs at the target
chain. The messages are sent to the particular _lane_, where they are guaranteed to be received in the same order they
are sent. The pallet supports many lanes.
The lane has two ends. Outbound lane end is storing number of messages that have been sent and the number of messages
that have been received. Inbound lane end stores the number of messages that have been received and also a map that maps
messages to relayers that have delivered those messages to the target chain.
The pallet has three main entrypoints:
- the `send_message` may be used by the other runtime pallets to send the messages;
- the `receive_messages_proof` is responsible for parsing the messages proof and handing messages over to the dispatch
code;
- the `receive_messages_delivery_proof` is responsible for parsing the messages delivery proof and rewarding relayers
that have delivered the message.
Many things are abstracted by the pallet:
- the message itself may mean anything, the pallet doesn't care about its content;
- the message dispatch happens during delivery, but it is decoupled from the pallet code;
- the messages proof and messages delivery proof are verified outside of the pallet;
- the relayers incentivization scheme is defined outside of the pallet.
Outside of the messaging pallet, we have a set of adapters, where messages and delivery proofs are regular storage
proofs. The proofs are generated at the bridged chain and require bridged chain finality. So messages pallet, in this
case, depends on one of the finality pallets. The messages are XCM messages and we are using XCM executor to dispatch
them on receival. You may find more info in [Polkadot <> Kusama Bridge](./polkadot-kusama-bridge-overview.md) document.
More: [pallet level documentation and code](../modules/messages/).
### Bridge Relayers Pallet
The pallet is quite simple. It just registers relayer rewards and has an entrypoint to collect them. When the rewards
are registered and the reward amount is configured outside of the pallet.
More: [pallet level documentation and code](../modules/relayers/).
## Offchain Components
Offchain bridge components are separate processes, called relayers. Relayers are connected both to the source chain and
target chain nodes. Relayers are reading state of the source chain, compare it to the state of the target chain and, if
state at target chain needs to be updated, submits target chain transaction.
### GRANDPA Finality Relay
The task of relay is to submit source chain GRANDPA justifications and their corresponding headers to the Bridge GRANDPA
Finality Pallet, deployed at the target chain. For that, the relay subscribes to the source chain GRANDPA justifications
stream and submits every new justification it sees to the target chain GRANDPA light client. In addition, relay is
searching for mandatory headers and submits their justifications - without that the pallet will be unable to move
forward.
More: [GRANDPA Finality Relay Sequence Diagram](./grandpa-finality-relay.html), [pallet level documentation and
code](../relays/finality/).
### Parachains Finality Relay
The relay connects to the source _relay_ chain and the target chain nodes. It doesn't need to connect to the tracked
parachain nodes. The relay looks at the
[`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642)
map of the [`paras`
pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras)
in source chain, and compares the value with the best parachain head, stored in the bridge parachains pallet at the
target chain. If new parachain head appears at the relay chain block `B`, the relay process **waits** until header `B`
or one of its ancestors appears at the target chain. Once it is available, the storage proof of the map entry is
generated and is submitted to the target chain.
As its on-chain component (which requires bridge GRANDPA pallet to be deployed nearby), the parachains finality relay
requires GRANDPA finality relay to be running in parallel. Without it, the header `B` or any of its children's finality
at source won't be relayed at target, and target chain won't be able to verify generated storage proof.
More: [Parachains Finality Relay Sequence Diagram](./parachains-finality-relay.html), [code](../relays/parachains/).
### Messages Relay
Messages relay is actually two relays that are running in a single process: messages delivery relay and delivery
confirmation relay. Even though they are more complex and have many caveats, the overall algorithm is the same as in
other relays.
Message delivery relay connects to the source chain and looks at the outbound lane end, waiting until new messages are
queued there. Once they appear at the source block `B`, the relay start waiting for the block `B` or its descendant
appear at the target chain. Then the messages storage proof is generated and submitted to the bridge messages pallet at
the target chain. In addition, the transaction may include the storage proof of the outbound lane state - that proves
that relayer rewards have been paid and this data (map of relay accounts to the delivered messages) may be pruned from
the inbound lane state at the target chain.
Delivery confirmation relay connects to the target chain and starts watching the inbound lane end. When new messages are
delivered to the target chain, the corresponding _source chain account_ is inserted to the map in the inbound lane data.
Relay detects that, say, at the target chain block `B` and waits until that block or its descendant appears at the
source chain. Once that happens, the relay crafts a storage proof of that data and sends it to the messages pallet,
deployed at the source chain.
As you can see, the messages relay also requires finality relay to be operating in parallel. Since messages relay
submits transactions to both source and target chains, it requires both _source-to-target_ and _target-to-source_
finality relays. They can be GRANDPA finality relays or GRANDPA+parachains finality relays, depending on the type of
connected chain.
More: [Messages Relay Sequence Diagram](./messages-relay.html), [pallet level documentation and
code](../relays/messages/).
### Complex Relay
Every relay transaction has its cost. The only transaction, that is "free" to relayer is when the mandatory GRANDPA
header is submitted. The relay that feeds the bridge with every relay chain and/or parachain head it sees, will have to
pay a (quite large) cost. And if no messages are sent through the bridge, that is just waste of money.
We have a special relay mode, called _complex relay_, where relay mostly sleeps and only submits transactions that are
required for the messages/confirmations delivery. This mode starts two message relays (in both directions). All required
finality relays are also started in a special _on-demand_ mode. In this mode they do not submit any headers without
special request. As always, the only exception is when GRANDPA finality relay sees the mandatory header - it is
submitted without such request.
The message relays are watching their lanes and when, at some block `B`, they see new messages/confirmations to be
delivered, they are asking on-demand relays to relay this block `B`. On-demand relays does that and then message relay
may perform its job. If on-demand relay is a parachain finality relay, it also runs its own on-demand GRANDPA relay,
which is used to relay required relay chain headers.
More: [Complex Relay Sequence Diagram](./complex-relay.html),
[code](../relays/bin-substrate/src/cli/relay_headers_and_messages/).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Messages Relay</title>
</head>
<body>
<h1>Messages Relay</h1>
<p>
Both Source Chain and Target Chains have Bridge Messages pallets deployed. They also have required
finality pallets deployed - we don't care about finality type here - they can be either Bridge GRANDPA,
or Bridge Parachains finality pallets, or any combination of those.
</p>
<p>
Finality Relayer represents two actual relayers - one relays Source Chain Finality to Target Chain.
And another one relays Target Chain Finality to Source Chain.
</p>
<div class="mermaid">
sequenceDiagram
participant Source Chain
participant Finality Relayer
participant Messages Relayer
participant Target Chain
Note right of Source Chain: Finalized: 480, Target Finalized: 50, Sent Messages: 42, Confirmed Messages: 42
Note left of Target Chain: Finalized: 60, Source Finalized: 420, Received Messages: 42
Source Chain ->> Source Chain: someone Sends Message 43
Source Chain ->> Source Chain: Import and Finalize Block 481
Source Chain ->> Messages Relayer: notes new outbound message 43 at Source Chain Block 481
Note right of Messages Relayer: can't deliver message 43, Source Chain Block 481 is not relayed
Source Chain ->> Finality Relayer: Read Finality Proof of Block 481
Finality Relayer ->> Target Chain: Submit Finality Proof of Block 481
Target Chain ->> Target Chain: Import and Finalize Block 61
Note left of Target Chain: Finalized: 61, Source Finalized: 481, Received Messages: 42
Source Chain ->> Messages Relayer: Read Proof of Message 43 at Block 481
Messages Relayer ->> Target Chain: Submit Proof of Message 43 at Block 481
Target Chain ->> Target Chain: Import and Finalize Block 62
Note left of Target Chain: Finalized: 62, Source Finalized: 481, Received Messages: { rewarded: 42, messages-relayer-account: [43] }
Target Chain ->> Messages Relayer: notes new unrewarded relayer at Target Chain Block 62
Note right of Messages Relayer: can't relay delivery confirmations because Target Chain Block 62 is not relayed
Target Chain ->> Finality Relayer: Read Finality Proof of Block 62
Finality Relayer ->> Source Chain: Submit Finality Proof of Block 62
Source Chain ->> Source Chain: Import and Finalize Block 482
Note right of Source Chain: Finalized: 482, Target Finalized: 62, Confirmed Messages: 42
Target Chain ->> Messages Relayer: Read Proof of Message 43 Delivery at Block 62
Messages Relayer ->> Source Chain: Submit Proof of Message 43 Delivery at Block 612
Source Chain ->> Source Chain: rewards messages-relayer-account for delivering message [43]
Source Chain ->> Source Chain: prune delivered message 43 from runtime storage
Note right of Source Chain: Finalized: 482, Target Finalized: 61, Confirmed Messages: 43
Source Chain ->> Source Chain: someone Sends Message 44
Source Chain ->> Source Chain: Import and Finalize Block 483
Source Chain ->> Messages Relayer: notes new outbound message 44 at Source Chain Block 483 and new confirmed message 43
Note right of Messages Relayer: can't deliver message 44, Source Chain Block 483 is not relayed
Source Chain ->> Finality Relayer: Read Finality Proof of Block 483
Finality Relayer ->> Target Chain: Submit Finality Proof of Block 483
Target Chain ->> Target Chain: Import and Finalize Block 63
Note left of Target Chain: Finalized: 63, Source Finalized: 483, Received Messages: { rewarded: 42, messages-relayer-account: [43] }
Source Chain ->> Messages Relayer: Read Proof of Message 44 and Proof of Message 43 reward at Block 483
Messages Relayer ->> Target Chain: Submit Proof of Message 44 and Proof of Message 43 reward at Block 483
Target Chain ->> Target Chain: Import and Finalize Block 64
Note left of Target Chain: Finalized: 64, Source Finalized: 483, Received Messages: { rewarded: 43, messages-relayer-account: [44] }
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/mermaid.min.js"></script>
<script>mermaid.initialize({startOnLoad: true})</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Parachains Finality Relay</title>
</head>
<body>
<h1>Parachains Finality Relay</h1>
<p>
Source Relay Chain is running GRANDPA Finality Gadget. Source Parachain is a parachain of the Source
Relay Chain. Bridge GRANDPA finality pallet is deployed at Target Chain runtime and is "connected"
to the Source Relay Chain. Bridge Parachains finality pallet is deployed at Target Chain and is
configured to track the Source Parachain. GRANDPA Relayer is configured to relay Source Relay Chain
finality to Target Chain. Parachains Relayer is configured to relay Source Parachain headers finality
to Target Chain.
</p>
<div class="mermaid">
sequenceDiagram
participant Source Parachain
participant Source Relay Chain
participant GRANDPA Relayer
participant Parachains Relayer
participant Target Chain
Note left of Source Parachain: Best: 125
Note left of Source Relay Chain: Finalized: 500, Best Parachain at Finalized: 120
Note right of Target Chain: Best Relay: 480, Best Parachain: 110
Source Parachain ->> Source Parachain: Import Block 126
Source Parachain ->> Source Relay Chain: Receives the Parachain block 126
Source Relay Chain ->> Source Relay Chain: Import block 501
Source Relay Chain ->> Source Relay Chain: Finalize block 501
Note left of Source Relay Chain: Finalized: 501, Best Parachain at Finalized: 126
Source Relay Chain ->> Parachains Relayer: notes new Source Parachain Block 126
Note left of Parachains Relayer: can't relay Source Parachain Block 126, because it requires at least Source Relay Block 501 at Target Chain
Source Relay Chain ->> Source Relay Chain: Import block 502
Source Relay Chain ->> Source Relay Chain: Finalize block 502
Source Relay Chain ->> GRANDPA Relayer: read GRANDPA Finality Proof of Block 502
GRANDPA Relayer ->> Target Chain: submit GRANDPA Finality Proof of Block 502
Note right of Target Chain: Best Relay: 502, Best Parachain: 110
Target Chain ->> Parachains Relayer: notes finalized Source Relay Block 502 at Target Chain
Source Relay Chain ->> Parachains Relayer: read Parachain Finality Proof at Relay Block 502
Parachains Relayer ->> Target Chain: submit Parachain Finality Proof at Relay Block 502
Note right of Target Chain: Best Relay: 502, Best Parachain: 126
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/mermaid.min.js"></script>
<script>mermaid.initialize({startOnLoad: true})</script>
</body>
</html>
# Polkadot <> Kusama Bridge Overview
This document describes how we use all components, described in the [High-Level Bridge
Documentation](./high-level-overview.md), to build the XCM bridge between Kusama and Polkadot. In this case, our
components merely work as a XCM transport (like XCMP/UMP/HRMP), between chains that are not a part of the same consensus
system.
The overall architecture may be seen in [this diagram](./polkadot-kusama-bridge.html).
## Bridge Hubs
All operations at relay chain are expensive. Ideally all non-mandatory transactions must happen on parachains. That's
why we are planning to have two parachains - Polkadot Bridge Hub under Polkadot consensus and Kusama Bridge Hub under
Kusama consensus.
The Bridge Hub will have all required bridge pallets in its runtime. We hope that later, other teams will be able to use
our bridge hubs too and have their pallets there.
The Bridge Hub will use the base token of the ecosystem - KSM at Kusama Bridge Hub and DOT at Polkadot Bridge Hub. The
runtime will have minimal set of non-bridge pallets, so there's not much you can do directly on bridge hubs.
## Connecting Parachains
You won't be able to directly use bridge hub transactions to send XCM messages over the bridge. Instead, you'll need to
use other parachains transactions, which will use HRMP to deliver messages to the Bridge Hub. The Bridge Hub will just
queue these messages in its outbound lane, which is dedicated to deliver messages between two parachains.
Our first planned bridge will connect the Polkadot and Kusama Asset Hubs. A bridge between those two parachains would
allow Asset Hub Polkadot accounts to hold wrapped KSM tokens and Asset Hub Kusama accounts to hold wrapped DOT tokens.
For that bridge (pair of parachains under different consensus systems) we'll be using the lane 00000000. Later, when
other parachains will join the bridge, they will be using other lanes for their messages.
## Running Relayers
We are planning to run our own complex relayer for the lane 00000000. The relayer will relay Kusama/Polkadot GRANDPA
justifications to the bridge hubs at the other side. It'll also relay finalized Kusama Bridge Hub and Polkadot Bridge
Hub heads. This will only happen when messages will be queued at hubs. So most of time relayer will be idle.
There's no any active relayer sets, or something like that. Anyone may start its own relayer and relay queued messages.
We are not against that and, as always, appreciate any community efforts. Of course, running relayer has the cost. Apart
from paying for the CPU and network, the relayer pays for transactions at both sides of the bridge. We have a mechanism
for rewarding relayers.
### Compensating the Cost of Message Delivery Transactions
One part of our rewarding scheme is that the cost of message delivery, for honest relayer, is zero. The honest relayer
is the relayer, which is following our rules:
- we do not reward relayers for submitting GRANDPA finality transactions. The only exception is submitting mandatory
headers (headers which are changing the GRANDPA authorities set) - the cost of such transaction is zero. The relayer
will pay the full cost for submitting all other headers;
- we do not reward relayers for submitting parachain finality transactions. The relayer will pay the full cost for
submitting parachain finality transactions;
- we compensate the cost of message delivery transactions that have actually delivered the messages. So if your
transaction has claimed to deliver messages `[42, 43, 44]`, but, because of some reasons, has actually delivered
messages `[42, 43]`, the transaction will be free for relayer. If it has not delivered any messages, then the relayer
pays the full cost of the transaction;
- we compensate the cost of message delivery and all required finality calls, if they are part of the same
[`frame_utility::batch_all`](https://github.com/paritytech/substrate/blob/891d6a5c870ab88521183facafc811a203bb6541/frame/utility/src/lib.rs#L326)
transaction. Of course, the calls inside the batch must be linked - e.g. the submitted parachain head must be used to
prove messages. Relay header must be used to prove parachain head finality. If one of calls fails, or if they are not
linked together, the relayer pays the full transaction cost.
Please keep in mind that the fee of "zero-cost" transactions is still withdrawn from the relayer account. But the
compensation is registered in the `pallet_bridge_relayers::RelayerRewards` map at the target bridge hub. The relayer may
later claim all its rewards later, using the `pallet_bridge_relayers::claim_rewards` call.
*A side note*: why we don't simply set the cost of useful transactions to zero? That's because the bridge has its cost.
If we won't take any fees, it would mean that the sender is not obliged to pay for its messages. And Bridge Hub
collators (and, maybe, "treasury") are not receiving any payment for including transactions. More about this later, in
the [Who is Rewarding Relayers](#who-is-rewarding-relayers) section.
### Message Delivery Confirmation Rewards
In addition to the "zero-cost" message delivery transactions, the relayer is also rewarded for:
- delivering every message. The reward is registered during delivery confirmation transaction at the Source Bridge Hub.;
- submitting delivery confirmation transaction. The relayer may submit delivery confirmation that e.g. confirms delivery
of four messages, of which the only one (or zero) messages is actually delivered by this relayer. It receives some fee
for confirming messages, delivered by other relayers.
Both rewards may be claimed using the `pallet_bridge_relayers::claim_rewards` call at the Source Bridge Hub.
### Who is Rewarding Relayers
Obviously, there should be someone who is paying relayer rewards. We want bridge transactions to have a cost, so we
can't use fees for rewards. Instead, the parachains using the bridge, use sovereign accounts on both sides of the bridge
to cover relayer rewards.
Bridged Parachains will have sovereign accounts at bridge hubs. For example, the Kusama Asset Hub will have an account
at the Polkadot Bridge Hub. The Polkadot Asset Hub will have an account at the Kusama Bridge Hub. The sovereign accounts
are used as a source of funds when the relayer is calling the `pallet_bridge_relayers::claim_rewards`.
Since messages lane is only used by the pair of parachains, there's no collision between different bridges. E.g. Kusama
Asset Hub will only reward relayers that are delivering messages from Kusama Asset Hub. The Kusama Asset Hub sovereign
account is not used to cover rewards of bridging with some other Polkadot Parachain.
### Multiple Relayers and Rewards
Our goal is to incentivize running honest relayers. But we have no relayers sets, so at any time anyone may submit
message delivery transaction, hoping that the cost of this transaction will be compensated. So what if some message is
currently queued and two relayers are submitting two identical message delivery transactions at once? Without any
special means, the cost of first included transaction will be compensated and the cost of the other one won't. A honest,
but unlucky relayer will lose some money. In addition, we'll waste some portion of block size and weight, which may be
used by other useful transactions.
To solve the problem, we have two signed extensions ([generate_bridge_reject_obsolete_headers_and_messages!
{}](../bin/runtime-common/src/lib.rs) and
[RefundRelayerForMessagesFromParachain](../bin/runtime-common/src/refund_relayer_extension.rs)), that are preventing
bridge transactions with obsolete data from including into the block. We are rejecting following transactions:
- transactions, that are submitting the GRANDPA justification for the best finalized header, or one of its ancestors;
- transactions, that are submitting the proof of the current best parachain head, or one of its ancestors;
- transactions, that are delivering already delivered messages. If at least one of messages is not yet delivered, the
transaction is not rejected;
- transactions, that are confirming delivery of already confirmed messages. If at least one of confirmations is new, the
transaction is not rejected;
- [`frame_utility::batch_all`](https://github.com/paritytech/substrate/blob/891d6a5c870ab88521183facafc811a203bb6541/frame/utility/src/lib.rs#L326)
transactions, that have both finality and message delivery calls. All restrictions from the [Compensating the Cost of
Message Delivery Transactions](#compensating-the-cost-of-message-delivery-transactions) are applied.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Polkadot &lt;&gt; Kusama Bridge</title>
</head>
<body>
<h1>Polkadot &lt;&gt; Kusama Bridge</h1>
<p>
Our bridge connects two parachains - Kusama Bridge Hub and Polkadot Bridge Hub. Messages that
are sent over bridge have XCM format and we are using existing architecture to dispatch them.
Since both Polkadot, Kusama and their parachains already have means to exchange XCM messages
within the same consensus system (HRMP, VMP, ...), it means that we are able to connect all those
chains with our bridge.
</p>
<p>
In our architecture, the lane that is used to relay messages over the bridge is determined by
the XCM source and destinations. So e.g. bridge between Asset Hubs Polkadot and Kusama (and opposite direction)
will use the lane 00000000, bridge between some other Polkadot Parachain and some other Kusama Parachain
will use the lane 00000001 and so on.
</p>
<div class="mermaid">
flowchart LR
subgraph Polkadot Consensus
polkadot(((Polkadot)))
asset_hub_polkadot(((Polkadot Asset Hub)))
polkadot_bh(((Polkadot Bridge Hub)))
polkadot---asset_hub_polkadot
polkadot---polkadot_bh
asset_hub_polkadot-->|Send Message Using HRMP|polkadot_bh
polkadot_bh-->|Send Message Using HRMP|asset_hub_polkadot
asset_hub_polkadot-->|Dispatch the Message|asset_hub_polkadot
end
subgraph Kusama Consensus
kusama_bh(((Kusama Bridge Hub)))
asset_hub_kusama(((Kusama Asset Hub)))
kusama(((Kusama)))
kusama---asset_hub_kusama
kusama---kusama_bh
kusama_bh-->|Send Message Using HRMP|asset_hub_kusama
asset_hub_kusama-->|Dispatch the Message|asset_hub_kusama
asset_hub_kusama-->|Send Message Using HRMP|kusama_bh
end
polkadot_bh&lt;===&gt;|Message is relayed to the Bridged Chain using lane 00000000|kusama_bh
linkStyle 2 stroke:red
linkStyle 7 stroke:red
linkStyle 8 stroke:red
linkStyle 3 stroke:green
linkStyle 4 stroke:green
linkStyle 9 stroke:green
</div>
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>
</body>
</html>
\ No newline at end of file
# Running your own bridge relayer
:warning: :construction: Please read the [Disclaimer](#disclaimer) section first :construction: :warning:
## Disclaimer
There are several things you should know before running your own relayer:
- initial bridge version (we call it bridges v1) supports any number of relayers, but **there's no guaranteed
compensation** for running a relayer and/or submitting valid bridge transactions. Most probably you'll end up
spending more funds than getting from rewards - please accept this fact;
- even if your relayer has managed to submit a valid bridge transaction that has been included into the bridge
hub block, there's no guarantee that you will be able to claim your compensation for that transaction. That's
because compensations are paid from the account, controlled by relay chain governance and it could have no funds
to compensate your useful actions. We'll be working on a proper process to resupply it on-time, but we can't
provide any guarantee until that process is well established.
## A Brief Introduction into Relayers and our Compensations Scheme
Omitting details, relayer is an offchain process that is connected to both bridged chains. It looks at the
outbound bridge messages queue and submits message delivery transactions to the target chain. There's a lot
of details behind that simple phrase - you could find more info in the
[High-Level Bridge Overview](./high-level-overview.md) document.
Reward that is paid to relayer has two parts. The first part static and is controlled by the governance.
It is rather small initially - e.g. you need to deliver `10_000` Kusama -> Polkadot messages to gain single
KSM token.
The other reward part is dynamic. So to deliver an XCM message from one BridgeHub to another, we'll need to
submit two transactions on different chains. Every transaction has its cost, which is:
- dynamic, because e.g. message size can change and/or fee factor of the target chain may change;
- quite large, because those transactions are quite heavy (mostly in terms of size, not weight).
We are compensating the cost of **valid**, **minimal** and **useful** bridge-related transactions to
relayer, that has submitted such transaction. Valid here means that the transaction doesn't fail. Minimal
means that all data within transaction call is actually required for the transaction to succeed. Useful
means that all supplied data in transaction is new and yet unknown to the target chain.
We have implemented a relayer that is able to craft such transactions. The rest of document contains a detailed
information on how to deploy this software on your own node.
## Relayers Concurrency
As it has been said above, we are not compensating cost of transactions that are not **useful**. For
example, if message `100` has already been delivered from Kusama Bridge Hub to Polkadot Bridge Hub, then another
transaction that delivers the same message `100` won't be **useful**. Hence, no compensation to relayer that
has submitted that second transaction.
But what if there are several relayers running? They are noticing the same queued message `100` and
simultaneously submit identical message delivery transactions. You may expect that there'll be one lucky
relayer, whose transaction would win the "race" and which will receive the compensation and reward. And
there'll be several other relayers, losing some funds on their unuseful transactions.
But actually, we have a solution that invalidates transactions of "unlucky" relayers before they are
included into the block. So at least you may be sure that you won't waste your funds on duplicate transactions.
<details>
<summary>Some details?</summary>
All **unuseful** transactions are rejected by our
[transaction extension](https://github.com/paritytech/polkadot-sdk/blob/master/bridges/bin/runtime-common/src/refund_relayer_extension.rs),
which also handles transaction fee compensations. You may find more info on unuseful (aka obsolete) transactions
by lurking in the code.
We also have the WiP prototype of relayers coordination protocol, where relayers will get some guarantee
that their transactions will be prioritized over other relayers transactions at their assigned slots.
That is planned for the future version of bridge and the progress is
[tracked here](https://github.com/paritytech/parity-bridges-common/issues/2486).
</details>
## Prerequisites
Let's focus on the bridge between Polkadot and Kusama Bridge Hubs. Let's also assume that we want to start
a relayer that "serves" an initial lane [`0x00000001`](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs#L54).
<details>
<summary>Lane?</summary>
Think of lane as a queue of messages that need to be delivered to the other/bridged chain. The lane is
bidirectional, meaning that there are four "endpoints". Two "outbound" endpoints (one at every chain), contain
messages that need to be delivered to the bridged chain. Two "inbound" are accepting messages from the bridged
chain and also remember the relayer, who has delivered message(s) to reward it later.
</details>
The same steps may be performed for other lanes and bridges as well - you'll just need to change several parameters.
So to start your relayer instance, you'll need to prepare:
- an address of ws/wss RPC endpoint of the Kusama relay chain;
- an address of ws/wss RPC endpoint of the Polkadot relay chain;
- an address of ws/wss RPC endpoint of the Kusama Bridge Hub chain;
- an address of ws/wss RPC endpoint of the Polkadot Bridge Hub chain;
- an account on Kusama Bridge Hub;
- an account on Polkadot Bridge Hub.
For RPC endpoints, you could start your own nodes, or use some public community nodes. Nodes are not meant to be
archive or provide access to insecure RPC calls.
To create an account on Bridge Hubs, you could use XCM teleport functionality. E.g. if you have an account on
the relay chain, you could use the `teleportAssets` call of `xcmPallet` and send asset
`V3 { id: Concrete(0, Here), Fungible: <your-amount> }` to beneficiary `V3(0, X1(AccountId32(<your-account>)))`
on destination `V3(0, X1(Parachain(1002)))`. To estimate amounts you need, please refer to the [Costs](#costs)
section of the document.
## Registering your Relayer Account (Optional, But Please Read)
Bridge transactions are quite heavy and expensive. We want to minimize block space that can be occupied by
invalid bridge transactions and prioritize valid transactions over invalid. That is achieved by **optional**
relayer registration. Transactions, signed by relayers with active registration, gain huge priority boost.
In exchange, such relayers may be slashed if they submit **invalid** or **non-minimal** transaction.
Transactions, signed by relayers **without** active registration, on the other hand, receive no priority
boost. It means that if there is active registered relayer, most likely all transactions from unregistered
will be counted as **unuseful**, not included into the block and unregistered relayer won't get any reward
for his operations.
Before registering, you should know several things about your funds:
- to register, you need to hold significant amount of funds on your relayer account. As of now, it is
[100 KSM](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs#L71C14-L71C43)
for registration on Kusama Bridge Hub and
[500 DOT](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_kusama_config.rs#L71C14-L71C43)
for registration on Polkadot Bridge Hub;
- when you are registered, those funds are reserved on relayer account and you can't transfer them.
The registration itself, has three states: active, inactive or expired. Initially, it is active, meaning that all
your transactions that are **validated** on top of block, where it is active get priority boost. Registration
becomes expired when the block with the number you have specified during registration is "mined". It is the
`validTill` parameter of the `register` call (see below). After that `validTill` block, you may unregister and get
your reserved funds back. There's also an intermediate point between those blocks - it is the `validTill - LEASE`,
where `LEASE` is the the chain constant, controlled by the governance. Initially it is set to `300` blocks.
All your transactions, **validated** between the `validTill - LEASE` and `validTill` blocks do not get the
priority boost. Also, it is forbidden to specify `validTill` such that the `validTill - currentBlock` is less
than the `LEASE`.
<details>
<summary>Example?</summary>
| Bridge Hub Block | Registration State | Comment |
| ----------------- | ------------------ | ------------------------------------------------------ |
| 100 | Active | You have submitted a tx with the `register(1000)` call |
| 101 | Active | Your message delivery transactions are boosted |
| 102 | Active | Your message delivery transactions are boosted |
| ... | Active | Your message delivery transactions are boosted |
| 700 | Inactive | Your message delivery transactions are not boosted |
| 701 | Inactive | Your message delivery transactions are not boosted |
| ... | Inactive | Your message delivery transactions are not boosted |
| 1000 | Expired | Your may submit a tx with the `deregister` call |
</details>
So once you have enough funds on your account and have selected the `validTill` parameter value, you
could use the Polkadot JS apps to submit an extrinsic. If you want priority boost for your transactions
on the Kusama Bridge Hub, open the
[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/extrinsics)
and submit the `register` extrinsic from the `bridgeRelayers` pallet:
![Register Extrinsic](./bridge-relayers-register.png)
To deregister, submit the simple `deregister` extrinsic when registration is expired:
![Deregister Extrinsic](./bridge-relayers-deregister.png)
At any time, you can prolong your registration by calling the `register` with the larger `validTill`.
## Costs
Your relayer account (on both Bridge Hubs) must hold enough funds to be able to pay costs of bridge
transactions. If your relayer behaves correctly, those costs will be compensated and you will be
able to claim it later.
**IMPORTANT**: you may add tip to your bridge transactions to boost their priority. But our
compensation mechanism never refunds transaction tip, so all tip tokens will be lost.
<details>
<summary>Types of bridge transactions</summary>
There are two types of bridge transactions:
- message delivery transaction brings queued message(s) from one Bridge Hub to another. We record
the fact that this specific (your) relayer has delivered those messages;
- message confirmation transaction confirms that some message have been delivered and also brings
back information on how many messages (your) relayer has delivered. We use this information later
to register delivery rewards on the source chain.
Several messages/confirmations may be included in a single bridge transaction. Apart from this
data, bridge transaction may include finality and storage proofs, required to prove authenticity of
this data.
</details>
To deliver and get reward for a single message, the relayer needs to submit two transactions. One
at the source Bridge Hub and one at the target Bridge Hub. Below are costs for Polkadot <> Kusama
messages (as of today):
- to deliver a single Polkadot -> Kusama message, you would need to pay around `0.06 KSM` at Kusama
Bridge Hub and around `1.62 DOT` at Polkadot Bridge Hub;
- to deliver a single Kusama -> Polkadot message, you would need to pay around `1.70 DOT` at Polkadot
Bridge Hub and around `0.05 KSM` at Kusama Bridge Hub.
Those values are not constants - they depend on call weights (that may change from release to release),
on transaction sizes (that depends on message size and chain state) and congestion factor. In any
case - it is your duty to make sure that the relayer has enough funds to pay transaction fees.
## Claiming your Compensations and Rewards
Hopefully you have successfully delivered some messages and now can claim your compensation and reward.
This requires submitting several transactions. But first, let's check that you actually have something to
claim. For that, let's check the state of the pallet that tracks all rewards.
To check your rewards at the Kusama Bridge Hub, go to the
[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/chainstate)
targeting Kusama Bridge Hub, select the `bridgeRelayers` pallet, choose `relayerRewards` map and
your relayer account. Then:
- set the `laneId` to `0x00000001`
- set the `bridgedChainId` to `bhpd`;
- check the both variants of the `owner` field: `ThisChain` is used to pay for message delivery transactions
and `BridgedChain` is used to pay for message confirmation transactions.
If check shows that you have some rewards, you can craft the claim transaction, with similar parameters.
For that, go to `Extrinsics` tab of the
[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/extrinsics)
and submit the following transaction (make sure to change `owner` before):
![Claim Rewards Extrinsic](./bridge-relayers-claim-rewards.png)
To claim rewards on Polkadot Bridge Hub you can follow the same process. The only difference is that you
need to set value of the `bridgedChainId` to `bhks`.
## Starting your Relayer
### Starting your Rococo <> Westend Relayer
You may find the relayer image reference in the
[Releases](https://github.com/paritytech/parity-bridges-common/releases)
of this repository. Make sure to check supported (bundled) versions
of release there. For Rococo <> Westend bridge, normally you may use the
latest published release. The release notes always contain the docker
image reference and source files, required to build relayer manually.
Once you have the docker image, update variables and run the following script:
```sh
export DOCKER_IMAGE=<image-of-substrate-relay>
export ROCOCO_HOST=<rococo-ws-rpc-host-here>
export ROCOCO_PORT=<rococo-ws-rpc-port-here>
# or set it to '--rococo-secure' if wss is used above
export ROCOCO_IS_SECURE=
export BRIDGE_HUB_ROCOCO_HOST=<bridge-hub-rococo-ws-rpc-host-here>
export BRIDGE_HUB_ROCOCO_PORT=<bridge-hub-rococo-ws-rpc-port-here>
# or set it to '--bridge-hub-rococo-secure' if wss is used above
export BRIDGE_HUB_ROCOCO_IS_SECURE=
export BRIDGE_HUB_ROCOCO_KEY_FILE=<absolute-path-to-file-with-account-key-at-bridge-hub-rococo>
export WESTEND_HOST=<westend-wss-rpc-host-here>
export WESTEND_PORT=<westend-wss-rpc-port-here>
# or set it to '--westend-secure' if wss is used above
export WESTEND_IS_SECURE=
export BRIDGE_HUB_WESTEND_HOST=<bridge-hub-westend-ws-rpc-host-here>
export BRIDGE_HUB_WESTEND_PORT=<bridge-hub-westend-ws-rpc-port-here>
# or set it to '--bridge-hub-westend-secure ' if wss is used above
export BRIDGE_HUB_WESTEND_IS_SECURE=
export BRIDGE_HUB_WESTEND_KEY_FILE=<absolute-path-to-file-with-account-key-at-bridge-hub-westend>
# you can get extended relay logs (e.g. for debugging issues) by passing `-e RUST_LOG=bridge=trace`
# argument to the `docker` binary
docker run \
-v $BRIDGE_HUB_ROCOCO_KEY_FILE:/bhr.key \
-v $BRIDGE_HUB_WESTEND_KEY_FILE:/bhw.key \
$DOCKER_IMAGE \
relay-headers-and-messages bridge-hub-rococo-bridge-hub-westend \
--rococo-host $ROCOCO_HOST \
--rococo-port $ROCOCO_PORT \
$ROCOCO_IS_SECURE \
--rococo-version-mode Auto \
--bridge-hub-rococo-host $BRIDGE_HUB_ROCOCO_HOST \
--bridge-hub-rococo-port $BRIDGE_HUB_ROCOCO_PORT \
$BRIDGE_HUB_ROCOCO_IS_SECURE \
--bridge-hub-rococo-version-mode Auto \
--bridge-hub-rococo-signer-file /bhr.key \
--bridge-hub-rococo-transactions-mortality 16 \
--westend-host $WESTEND_HOST \
--westend-port $WESTEND_PORT \
$WESTEND_IS_SECURE \
--westend-version-mode Auto \
--bridge-hub-westend-host $BRIDGE_HUB_WESTEND_HOST \
--bridge-hub-westend-port $BRIDGE_HUB_WESTEND_PORT \
$BRIDGE_HUB_WESTEND_IS_SECURE \
--bridge-hub-westend-version-mode Auto \
--bridge-hub-westend-signer-file /bhw.key \
--bridge-hub-westend-transactions-mortality 16 \
--lane 00000002
```
### Starting your Polkadot <> Kusama Relayer
*Work in progress, coming soon*
### Watching your relayer state
Our relayer provides some Prometheus metrics that you may convert into some fancy Grafana dashboards
and alerts. By default, metrics are exposed at port `9616`. To expose endpoint to the localhost, change
the docker command by adding following two lines:
```sh
docker run \
..
-p 127.0.0.1:9616:9616 \ # tell Docker to bind container port 9616 to host port 9616
# and listen for connections on the host' localhost interface
..
$DOCKER_IMAGE \
relay-headers-and-messages bridge-hub-rococo-bridge-hub-westend \
--prometheus-host 0.0.0.0 \ # tell `substrate-relay` binary to accept Prometheus endpoint
# connections from everywhere
..
```
You can find more info on configuring Prometheus and Grafana in the
[Monitor your node](https://wiki.polkadot.network/docs/maintain-guides-how-to-monitor-your-node)
guide from Polkadot wiki.
We have our own set of Grafana dashboards and alerts. You may use them for inspiration.
Please find them in this folder:
- for Rococo <> Westend bridge: [rococo-westend](https://github.com/paritytech/parity-bridges-common/tree/master/deployments/bridges/rococo-westend).
- for Polkadot <> Kusama bridge: *work in progress, coming soon*