refund extra weight in receive_messages_delivery_proof call (#2031)

This commit is contained in:
Svyatoslav Nikolsky
2023-04-12 09:52:55 +03:00
committed by Bastian Köcher
parent f7380490c0
commit 9e48162df2
4 changed files with 104 additions and 43 deletions
+84 -36
View File
@@ -63,7 +63,7 @@ use bp_messages::{
MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData,
OutboundMessageDetails, UnrewardedRelayersState, OutboundMessageDetails, UnrewardedRelayersState,
}; };
use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, Size}; use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, PreComputedSize, Size};
use codec::{Decode, Encode, MaxEncodedLen}; use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get}; use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get};
use sp_runtime::{traits::UniqueSaturatedFrom, SaturatedConversion}; use sp_runtime::{traits::UniqueSaturatedFrom, SaturatedConversion};
@@ -423,10 +423,11 @@ pub mod pallet {
pub fn receive_messages_delivery_proof( pub fn receive_messages_delivery_proof(
origin: OriginFor<T>, origin: OriginFor<T>,
proof: MessagesDeliveryProofOf<T, I>, proof: MessagesDeliveryProofOf<T, I>,
relayers_state: UnrewardedRelayersState, mut relayers_state: UnrewardedRelayersState,
) -> DispatchResult { ) -> DispatchResultWithPostInfo {
Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?; Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?;
let proof_size = proof.size();
let confirmation_relayer = ensure_signed(origin)?; let confirmation_relayer = ensure_signed(origin)?;
let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof) let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof)
.map_err(|err| { .map_err(|err| {
@@ -499,13 +500,27 @@ pub mod pallet {
}); });
// if some new messages have been confirmed, reward relayers // if some new messages have been confirmed, reward relayers
T::DeliveryConfirmationPayments::pay_reward( let actually_rewarded_relayers = T::DeliveryConfirmationPayments::pay_reward(
lane_id, lane_id,
lane_data.relayers, lane_data.relayers,
&confirmation_relayer, &confirmation_relayer,
&received_range, &received_range,
); );
}
// update relayers state with actual numbers to compute actual weight below
relayers_state.unrewarded_relayer_entries = sp_std::cmp::min(
relayers_state.unrewarded_relayer_entries,
actually_rewarded_relayers,
);
relayers_state.total_messages = sp_std::cmp::min(
relayers_state.total_messages,
received_range
.end()
.checked_sub(*received_range.start())
.and_then(|x| x.checked_add(1))
.unwrap_or(MessageNonce::MAX),
);
};
log::trace!( log::trace!(
target: LOG_TARGET, target: LOG_TARGET,
@@ -514,11 +529,15 @@ pub mod pallet {
lane_id, lane_id,
); );
// TODO: https://github.com/paritytech/parity-bridges-common/issues/2020 // because of lags, the inbound lane state (`lane_data`) may have entries for
// we need to refund unused weight (because the inbound lane state may contain // already rewarded relayers and messages (if all entries are duplicated, then
// already confirmed messages and already rewarded relayer entries) // this transaction must be filtered out by our signed extension)
let actual_weight = T::WeightInfo::receive_messages_delivery_proof_weight(
&PreComputedSize(proof_size as usize),
&relayers_state,
);
Ok(()) Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes })
} }
} }
@@ -940,8 +959,9 @@ mod tests {
message, message_payload, run_test, unrewarded_relayer, AccountId, DbWeight, message, message_payload, run_test, unrewarded_relayer, AccountId, DbWeight,
RuntimeEvent as TestEvent, RuntimeOrigin, TestDeliveryConfirmationPayments, RuntimeEvent as TestEvent, RuntimeOrigin, TestDeliveryConfirmationPayments,
TestDeliveryPayments, TestMessagesDeliveryProof, TestMessagesProof, TestRelayer, TestDeliveryPayments, TestMessagesDeliveryProof, TestMessagesProof, TestRelayer,
TestRuntime, MAX_OUTBOUND_PAYLOAD_SIZE, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TestRuntime, TestWeightInfo, MAX_OUTBOUND_PAYLOAD_SIZE, PAYLOAD_REJECTED_BY_TARGET_CHAIN,
TEST_LANE_ID, TEST_LANE_ID_2, TEST_LANE_ID_3, TEST_RELAYER_A, TEST_RELAYER_B, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_LANE_ID_2, TEST_LANE_ID_3, TEST_RELAYER_A,
TEST_RELAYER_B,
}; };
use bp_messages::{BridgeMessagesCall, UnrewardedRelayer, UnrewardedRelayersState}; use bp_messages::{BridgeMessagesCall, UnrewardedRelayer, UnrewardedRelayersState};
use bp_test_utils::generate_owned_bridge_module_tests; use bp_test_utils::generate_owned_bridge_module_tests;
@@ -1398,50 +1418,78 @@ mod tests {
)); ));
// this reports delivery of message 1 => reward is paid to TEST_RELAYER_A // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof( let single_message_delivery_proof = TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into_iter().collect(),
..Default::default()
},
)));
let single_message_delivery_proof_size = single_message_delivery_proof.size();
let result = Pallet::<TestRuntime>::receive_messages_delivery_proof(
RuntimeOrigin::signed(1), RuntimeOrigin::signed(1),
TestMessagesDeliveryProof(Ok(( single_message_delivery_proof,
TEST_LANE_ID,
InboundLaneData {
relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)]
.into_iter()
.collect(),
..Default::default()
}
))),
UnrewardedRelayersState { UnrewardedRelayersState {
unrewarded_relayer_entries: 1, unrewarded_relayer_entries: 1,
total_messages: 1, total_messages: 1,
last_delivered_nonce: 1, last_delivered_nonce: 1,
..Default::default() ..Default::default()
}, },
)); );
assert_ok!(result);
assert_eq!(
result.unwrap().actual_weight.unwrap(),
TestWeightInfo::receive_messages_delivery_proof_weight(
&PreComputedSize(single_message_delivery_proof_size as _),
&UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 1,
..Default::default()
},
)
);
assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1));
assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1));
// this reports delivery of both message 1 and message 2 => reward is paid only to // this reports delivery of both message 1 and message 2 => reward is paid only to
// TEST_RELAYER_B // TEST_RELAYER_B
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof( let two_messages_delivery_proof = TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
relayers: vec![
unrewarded_relayer(1, 1, TEST_RELAYER_A),
unrewarded_relayer(2, 2, TEST_RELAYER_B),
]
.into_iter()
.collect(),
..Default::default()
},
)));
let two_messages_delivery_proof_size = two_messages_delivery_proof.size();
let result = Pallet::<TestRuntime>::receive_messages_delivery_proof(
RuntimeOrigin::signed(1), RuntimeOrigin::signed(1),
TestMessagesDeliveryProof(Ok(( two_messages_delivery_proof,
TEST_LANE_ID,
InboundLaneData {
relayers: vec![
unrewarded_relayer(1, 1, TEST_RELAYER_A),
unrewarded_relayer(2, 2, TEST_RELAYER_B)
]
.into_iter()
.collect(),
..Default::default()
}
))),
UnrewardedRelayersState { UnrewardedRelayersState {
unrewarded_relayer_entries: 2, unrewarded_relayer_entries: 2,
total_messages: 2, total_messages: 2,
last_delivered_nonce: 2, last_delivered_nonce: 2,
..Default::default() ..Default::default()
}, },
)); );
assert_ok!(result);
// even though the pre-dispatch weight was for two messages, the actual weight is
// for single message only
assert_eq!(
result.unwrap().actual_weight.unwrap(),
TestWeightInfo::receive_messages_delivery_proof_weight(
&PreComputedSize(two_messages_delivery_proof_size as _),
&UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 1,
..Default::default()
},
)
);
assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1));
assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1));
}); });
+8 -2
View File
@@ -148,9 +148,12 @@ parameter_types! {
pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2];
} }
/// weights of messages pallet calls we use in tests.
pub type TestWeightInfo = ();
impl Config for TestRuntime { impl Config for TestRuntime {
type RuntimeEvent = RuntimeEvent; type RuntimeEvent = RuntimeEvent;
type WeightInfo = (); type WeightInfo = TestWeightInfo;
type ActiveOutboundLanes = ActiveOutboundLanes; type ActiveOutboundLanes = ActiveOutboundLanes;
type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
@@ -381,12 +384,15 @@ impl DeliveryConfirmationPayments<AccountId> for TestDeliveryConfirmationPayment
messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>, messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
_confirmation_relayer: &AccountId, _confirmation_relayer: &AccountId,
received_range: &RangeInclusive<MessageNonce>, received_range: &RangeInclusive<MessageNonce>,
) { ) -> MessageNonce {
let relayers_rewards = calc_relayers_rewards(messages_relayers, received_range); let relayers_rewards = calc_relayers_rewards(messages_relayers, received_range);
let rewarded_relayers = relayers_rewards.len();
for (relayer, reward) in &relayers_rewards { for (relayer, reward) in &relayers_rewards {
let key = (b":relayer-reward:", relayer, reward).encode(); let key = (b":relayer-reward:", relayer, reward).encode();
frame_support::storage::unhashed::put(&key, &true); frame_support::storage::unhashed::put(&key, &true);
} }
rewarded_relayers as _
} }
} }
@@ -20,7 +20,7 @@ use crate::{Config, Pallet};
use bp_messages::{ use bp_messages::{
source_chain::{DeliveryConfirmationPayments, RelayersRewards}, source_chain::{DeliveryConfirmationPayments, RelayersRewards},
LaneId, LaneId, MessageNonce,
}; };
use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
use frame_support::{sp_runtime::SaturatedConversion, traits::Get}; use frame_support::{sp_runtime::SaturatedConversion, traits::Get};
@@ -47,9 +47,10 @@ where
messages_relayers: VecDeque<bp_messages::UnrewardedRelayer<T::AccountId>>, messages_relayers: VecDeque<bp_messages::UnrewardedRelayer<T::AccountId>>,
confirmation_relayer: &T::AccountId, confirmation_relayer: &T::AccountId,
received_range: &RangeInclusive<bp_messages::MessageNonce>, received_range: &RangeInclusive<bp_messages::MessageNonce>,
) { ) -> MessageNonce {
let relayers_rewards = let relayers_rewards =
bp_messages::calc_relayers_rewards::<T::AccountId>(messages_relayers, received_range); bp_messages::calc_relayers_rewards::<T::AccountId>(messages_relayers, received_range);
let rewarded_relayers = relayers_rewards.len();
register_relayers_rewards::<T>( register_relayers_rewards::<T>(
confirmation_relayer, confirmation_relayer,
@@ -61,6 +62,8 @@ where
), ),
DeliveryReward::get(), DeliveryReward::get(),
); );
rewarded_relayers as _
} }
} }
@@ -98,12 +98,14 @@ pub trait DeliveryConfirmationPayments<AccountId> {
/// ///
/// The implementation may also choose to pay reward to the `confirmation_relayer`, which is /// The implementation may also choose to pay reward to the `confirmation_relayer`, which is
/// a relayer that has submitted delivery confirmation transaction. /// a relayer that has submitted delivery confirmation transaction.
///
/// Returns number of actually rewarded relayers.
fn pay_reward( fn pay_reward(
lane_id: LaneId, lane_id: LaneId,
messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>, messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
confirmation_relayer: &AccountId, confirmation_relayer: &AccountId,
received_range: &RangeInclusive<MessageNonce>, received_range: &RangeInclusive<MessageNonce>,
); ) -> MessageNonce;
} }
impl<AccountId> DeliveryConfirmationPayments<AccountId> for () { impl<AccountId> DeliveryConfirmationPayments<AccountId> for () {
@@ -114,8 +116,9 @@ impl<AccountId> DeliveryConfirmationPayments<AccountId> for () {
_messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>, _messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
_confirmation_relayer: &AccountId, _confirmation_relayer: &AccountId,
_received_range: &RangeInclusive<MessageNonce>, _received_range: &RangeInclusive<MessageNonce>,
) { ) -> MessageNonce {
// this implementation is not rewarding relayers at all // this implementation is not rewarding relayers at all
0
} }
} }
@@ -202,6 +205,7 @@ impl<AccountId> DeliveryConfirmationPayments<AccountId> for ForbidOutboundMessag
_messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>, _messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
_confirmation_relayer: &AccountId, _confirmation_relayer: &AccountId,
_received_range: &RangeInclusive<MessageNonce>, _received_range: &RangeInclusive<MessageNonce>,
) { ) -> MessageNonce {
0
} }
} }