greedy relayer don't need message dispatch to be prepaid if dispatch is supposed to be paid at the target chain (#1016)

This commit is contained in:
Svyatoslav Nikolsky
2021-06-24 17:25:43 +03:00
committed by Bastian Köcher
parent 010588e95a
commit 3e103c16ce
5 changed files with 102 additions and 21 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ use codec::{Decode, Encode};
use frame_support::{weights::Weight, RuntimeDebug}; use frame_support::{weights::Weight, RuntimeDebug};
/// Where message dispatch fee is paid? /// Where message dispatch fee is paid?
#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq)] #[derive(Encode, Decode, RuntimeDebug, Clone, Copy, PartialEq, Eq)]
pub enum DispatchFeePayment { pub enum DispatchFeePayment {
/// The dispacth fee is paid at the source chain. /// The dispacth fee is paid at the source chain.
AtSourceChain, AtSourceChain,
@@ -23,7 +23,7 @@ use crate::on_demand_headers::OnDemandHeadersRelay;
use async_trait::async_trait; use async_trait::async_trait;
use bp_messages::{LaneId, MessageNonce}; use bp_messages::{LaneId, MessageNonce};
use bp_runtime::ChainId; use bp_runtime::{messages::DispatchFeePayment, ChainId};
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use frame_support::{traits::Instance, weights::Weight}; use frame_support::{traits::Instance, weights::Weight};
@@ -351,6 +351,7 @@ fn make_message_details_map<C: Chain>(
size: details.size as _, size: details.size as _,
// TODO: https://github.com/paritytech/parity-bridges-common/issues/997 // TODO: https://github.com/paritytech/parity-bridges-common/issues/997
reward: num_traits::Zero::zero(), reward: num_traits::Zero::zero(),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
}, },
); );
expected_nonce = details.nonce + 1; expected_nonce = details.nonce + 1;
@@ -363,7 +364,6 @@ fn make_message_details_map<C: Chain>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use bp_runtime::messages::DispatchFeePayment;
fn message_details_from_rpc( fn message_details_from_rpc(
nonces: RangeInclusive<MessageNonce>, nonces: RangeInclusive<MessageNonce>,
@@ -390,7 +390,8 @@ mod tests {
MessageDetails { MessageDetails {
dispatch_weight: 0, dispatch_weight: 0,
size: 0, size: 0,
reward: 0 reward: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
} }
), ),
( (
@@ -398,7 +399,8 @@ mod tests {
MessageDetails { MessageDetails {
dispatch_weight: 0, dispatch_weight: 0,
size: 0, size: 0,
reward: 0 reward: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
} }
), ),
( (
@@ -406,7 +408,8 @@ mod tests {
MessageDetails { MessageDetails {
dispatch_weight: 0, dispatch_weight: 0,
size: 0, size: 0,
reward: 0 reward: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
} }
), ),
] ]
@@ -425,7 +428,8 @@ mod tests {
MessageDetails { MessageDetails {
dispatch_weight: 0, dispatch_weight: 0,
size: 0, size: 0,
reward: 0 reward: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
} }
), ),
( (
@@ -433,7 +437,8 @@ mod tests {
MessageDetails { MessageDetails {
dispatch_weight: 0, dispatch_weight: 0,
size: 0, size: 0,
reward: 0 reward: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
} }
), ),
] ]
+1
View File
@@ -17,4 +17,5 @@ parking_lot = "0.11.0"
# Bridge Dependencies # Bridge Dependencies
bp-messages = { path = "../../primitives/messages" } bp-messages = { path = "../../primitives/messages" }
bp-runtime = { path = "../../primitives/runtime" }
relay-utils = { path = "../utils" } relay-utils = { path = "../utils" }
@@ -31,6 +31,7 @@ use crate::metrics::MessageLaneLoopMetrics;
use async_trait::async_trait; use async_trait::async_trait;
use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight}; use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight};
use bp_runtime::messages::DispatchFeePayment;
use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt}; use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt};
use relay_utils::{ use relay_utils::{
interval, interval,
@@ -97,6 +98,8 @@ pub struct MessageDetails<SourceChainBalance> {
pub size: u32, pub size: u32,
/// The relayer reward paid in the source chain tokens. /// The relayer reward paid in the source chain tokens.
pub reward: SourceChainBalance, pub reward: SourceChainBalance,
/// Where the fee for dispatching message is paid?
pub dispatch_fee_payment: DispatchFeePayment,
} }
/// Messages details map. /// Messages details map.
@@ -454,7 +457,7 @@ pub(crate) mod tests {
} }
pub const CONFIRMATION_TRANSACTION_COST: TestSourceChainBalance = 1; pub const CONFIRMATION_TRANSACTION_COST: TestSourceChainBalance = 1;
pub const DELIVERY_TRANSACTION_COST: TestSourceChainBalance = 1; pub const BASE_MESSAGE_DELIVERY_TRANSACTION_COST: TestSourceChainBalance = 1;
pub type TestSourceChainBalance = u64; pub type TestSourceChainBalance = u64;
pub type TestSourceHeaderId = HeaderId<TestSourceHeaderNumber, TestSourceHeaderHash>; pub type TestSourceHeaderId = HeaderId<TestSourceHeaderNumber, TestSourceHeaderHash>;
@@ -590,6 +593,7 @@ pub(crate) mod tests {
dispatch_weight: 1, dispatch_weight: 1,
size: 1, size: 1,
reward: 1, reward: 1,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
}, },
) )
}) })
@@ -768,11 +772,13 @@ pub(crate) mod tests {
async fn estimate_delivery_transaction_in_source_tokens( async fn estimate_delivery_transaction_in_source_tokens(
&self, &self,
_nonces: RangeInclusive<MessageNonce>, nonces: RangeInclusive<MessageNonce>,
_total_dispatch_weight: Weight, total_dispatch_weight: Weight,
total_size: u32, total_size: u32,
) -> TestSourceChainBalance { ) -> TestSourceChainBalance {
DELIVERY_TRANSACTION_COST * (total_size as TestSourceChainBalance) BASE_MESSAGE_DELIVERY_TRANSACTION_COST * (nonces.end() - nonces.start() + 1)
+ total_dispatch_weight
+ total_size as TestSourceChainBalance
} }
} }
@@ -28,6 +28,7 @@ use crate::metrics::MessageLaneLoopMetrics;
use async_trait::async_trait; use async_trait::async_trait;
use bp_messages::{MessageNonce, UnrewardedRelayersState, Weight}; use bp_messages::{MessageNonce, UnrewardedRelayersState, Weight};
use bp_runtime::messages::DispatchFeePayment;
use futures::stream::FusedStream; use futures::stream::FusedStream;
use num_traits::{SaturatingAdd, Zero}; use num_traits::{SaturatingAdd, Zero};
use relay_utils::FailedClient; use relay_utils::FailedClient;
@@ -560,6 +561,7 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
let mut soft_selected_count = 0; let mut soft_selected_count = 0;
let mut selected_weight: Weight = 0; let mut selected_weight: Weight = 0;
let mut selected_unpaid_weight: Weight = 0;
let mut selected_size: u32 = 0; let mut selected_size: u32 = 0;
let mut selected_count: MessageNonce = 0; let mut selected_count: MessageNonce = 0;
@@ -627,6 +629,18 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
break; break;
} }
// If dispatch fee has been paid at the source chain, it means that it is **relayer** who's
// paying for dispatch at the target chain AND reward must cover this dispatch fee.
//
// If dispatch fee is paid at the target chain, it means that it'll be withdrawn from the
// dispatch origin account AND reward is not covering this fee.
//
// So in the latter case we're not adding the dispatch weight to the delivery transaction weight.
let new_selected_unpaid_weight = match details.dispatch_fee_payment {
DispatchFeePayment::AtSourceChain => selected_unpaid_weight.saturating_add(details.dispatch_weight),
DispatchFeePayment::AtTargetChain => selected_unpaid_weight,
};
// now the message has passed all 'strong' checks, and we CAN deliver it. But do we WANT // now the message has passed all 'strong' checks, and we CAN deliver it. But do we WANT
// to deliver it? It depends on the relayer strategy. // to deliver it? It depends on the relayer strategy.
match relayer_mode { match relayer_mode {
@@ -637,7 +651,7 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
let delivery_transaction_cost = lane_target_client let delivery_transaction_cost = lane_target_client
.estimate_delivery_transaction_in_source_tokens( .estimate_delivery_transaction_in_source_tokens(
0..=(new_selected_count as MessageNonce - 1), 0..=(new_selected_count as MessageNonce - 1),
new_selected_weight, new_selected_unpaid_weight,
new_selected_size as u32, new_selected_size as u32,
) )
.await; .await;
@@ -685,6 +699,7 @@ async fn select_nonces_for_delivery_transaction<P: MessageLane>(
hard_selected_count = index + 1; hard_selected_count = index + 1;
selected_weight = new_selected_weight; selected_weight = new_selected_weight;
selected_unpaid_weight = new_selected_unpaid_weight;
selected_size = new_selected_size; selected_size = new_selected_size;
selected_count = new_selected_count; selected_count = new_selected_count;
} }
@@ -740,13 +755,19 @@ mod tests {
use crate::message_lane_loop::{ use crate::message_lane_loop::{
tests::{ tests::{
header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance, TestSourceClient, header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance, TestSourceClient,
TestSourceHeaderId, TestTargetClient, TestTargetHeaderId, CONFIRMATION_TRANSACTION_COST, TestSourceHeaderId, TestTargetClient, TestTargetHeaderId, BASE_MESSAGE_DELIVERY_TRANSACTION_COST,
DELIVERY_TRANSACTION_COST, CONFIRMATION_TRANSACTION_COST,
}, },
MessageDetails, MessageDetails,
}; };
use bp_runtime::messages::DispatchFeePayment::*;
const DEFAULT_REWARD: TestSourceChainBalance = CONFIRMATION_TRANSACTION_COST + DELIVERY_TRANSACTION_COST; const DEFAULT_DISPATCH_WEIGHT: Weight = 1;
const DEFAULT_SIZE: u32 = 1;
const DEFAULT_REWARD: TestSourceChainBalance = CONFIRMATION_TRANSACTION_COST
+ BASE_MESSAGE_DELIVERY_TRANSACTION_COST
+ DEFAULT_DISPATCH_WEIGHT
+ (DEFAULT_SIZE as TestSourceChainBalance);
type TestRaceState = RaceState<TestSourceHeaderId, TestTargetHeaderId, TestMessagesProof>; type TestRaceState = RaceState<TestSourceHeaderId, TestTargetHeaderId, TestMessagesProof>;
type TestStrategy = MessageDeliveryStrategy<TestMessageLane, TestSourceClient, TestTargetClient>; type TestStrategy = MessageDeliveryStrategy<TestMessageLane, TestSourceClient, TestTargetClient>;
@@ -755,6 +776,7 @@ mod tests {
new_nonces: RangeInclusive<MessageNonce>, new_nonces: RangeInclusive<MessageNonce>,
confirmed_nonce: MessageNonce, confirmed_nonce: MessageNonce,
reward: TestSourceChainBalance, reward: TestSourceChainBalance,
dispatch_fee_payment: DispatchFeePayment,
) -> SourceClientNonces<MessageDetailsMap<TestSourceChainBalance>> { ) -> SourceClientNonces<MessageDetailsMap<TestSourceChainBalance>> {
SourceClientNonces { SourceClientNonces {
new_nonces: new_nonces new_nonces: new_nonces
@@ -763,9 +785,10 @@ mod tests {
( (
nonce, nonce,
MessageDetails { MessageDetails {
dispatch_weight: 1, dispatch_weight: DEFAULT_DISPATCH_WEIGHT,
size: 1, size: DEFAULT_SIZE,
reward, reward,
dispatch_fee_payment,
}, },
) )
}) })
@@ -811,7 +834,7 @@ mod tests {
race_strategy race_strategy
.strategy .strategy
.source_nonces_updated(header_id(1), source_nonces(20..=23, 19, DEFAULT_REWARD)); .source_nonces_updated(header_id(1), source_nonces(20..=23, 19, DEFAULT_REWARD, AtSourceChain));
let target_nonces = TargetClientNonces { let target_nonces = TargetClientNonces {
latest_nonce: 19, latest_nonce: 19,
@@ -845,6 +868,7 @@ mod tests {
dispatch_weight: idx, dispatch_weight: idx,
size: idx as _, size: idx as _,
reward: idx as _, reward: idx as _,
dispatch_fee_payment: AtSourceChain,
}, },
) )
}) })
@@ -1135,7 +1159,12 @@ mod tests {
#[async_std::test] #[async_std::test]
async fn no_losses_relayer_is_not_delivering_messages_if_cost_is_larger_than_reward() { async fn no_losses_relayer_is_not_delivering_messages_if_cost_is_larger_than_reward() {
let (mut state, mut strategy) = prepare_strategy(); let (mut state, mut strategy) = prepare_strategy();
let nonces = source_nonces(24..=25, 19, DEFAULT_REWARD - DELIVERY_TRANSACTION_COST); let nonces = source_nonces(
24..=25,
19,
DEFAULT_REWARD - BASE_MESSAGE_DELIVERY_TRANSACTION_COST,
AtSourceChain,
);
strategy.strategy.source_nonces_updated(header_id(2), nonces); strategy.strategy.source_nonces_updated(header_id(2), nonces);
state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); state.best_finalized_source_header_id_at_best_target = Some(header_id(2));
strategy.relayer_mode = RelayerMode::NoLosses; strategy.relayer_mode = RelayerMode::NoLosses;
@@ -1150,6 +1179,46 @@ mod tests {
); );
} }
#[async_std::test]
async fn no_losses_relayer_is_delivering_unpaid_messages() {
async fn test_with_dispatch_fee_payment(
dispatch_fee_payment: DispatchFeePayment,
) -> Option<(RangeInclusive<MessageNonce>, MessageProofParameters)> {
let (mut state, mut strategy) = prepare_strategy();
let nonces = source_nonces(
24..=24,
19,
DEFAULT_REWARD - DEFAULT_DISPATCH_WEIGHT,
dispatch_fee_payment,
);
strategy.strategy.source_nonces_updated(header_id(2), nonces);
state.best_finalized_source_header_id_at_best_target = Some(header_id(2));
strategy.max_unrewarded_relayer_entries_at_target = 100;
strategy.max_unconfirmed_nonces_at_target = 100;
strategy.max_messages_in_single_batch = 100;
strategy.max_messages_weight_in_single_batch = 100;
strategy.max_messages_size_in_single_batch = 100;
strategy.relayer_mode = RelayerMode::NoLosses;
// so now we have:
// - 20..=23 with reward = cost
// - 24..=24 with reward less than cost, but we're deducting `DEFAULT_DISPATCH_WEIGHT` from the
// cost, so it should be fine;
// => when MSG#24 fee is paid at the target chain, strategy shall select all 20..=24
// => when MSG#25 fee is paid at the source chain, strategy shall only select 20..=23
strategy.select_nonces_to_deliver(state).await
}
assert_eq!(
test_with_dispatch_fee_payment(AtTargetChain).await,
Some(((20..=24), proof_parameters(false, 5)))
);
assert_eq!(
test_with_dispatch_fee_payment(AtSourceChain).await,
Some(((20..=23), proof_parameters(false, 4)))
);
}
#[async_std::test] #[async_std::test]
async fn relayer_uses_flattened_view_of_the_source_queue_to_select_nonces() { async fn relayer_uses_flattened_view_of_the_source_queue_to_select_nonces() {
// Real scenario that has happened on test deployments: // Real scenario that has happened on test deployments:
@@ -1161,7 +1230,7 @@ mod tests {
// This was happening because selector (`select_nonces_for_delivery_transaction`) has been called // This was happening because selector (`select_nonces_for_delivery_transaction`) has been called
// for every `source_queue` entry separately without preserving any context. // for every `source_queue` entry separately without preserving any context.
let (mut state, mut strategy) = prepare_strategy(); let (mut state, mut strategy) = prepare_strategy();
let nonces = source_nonces(24..=25, 19, DEFAULT_REWARD - DELIVERY_TRANSACTION_COST); let nonces = source_nonces(24..=25, 19, DEFAULT_REWARD, AtSourceChain);
strategy.strategy.source_nonces_updated(header_id(2), nonces); strategy.strategy.source_nonces_updated(header_id(2), nonces);
strategy.max_unrewarded_relayer_entries_at_target = 100; strategy.max_unrewarded_relayer_entries_at_target = 100;
strategy.max_unconfirmed_nonces_at_target = 100; strategy.max_unconfirmed_nonces_at_target = 100;