Skip to content

Commit dcaec27

Browse files
authored
RefundRelayerForMessagesFromParachain improvements (#1879)
* RefundRelayerForMessagesFromParachain improvements * Address code review comments
1 parent 5457f06 commit dcaec27

File tree

13 files changed

+753
-724
lines changed

13 files changed

+753
-724
lines changed

bin/millau/runtime/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,6 @@ pub type BridgeRefundRialtoParachainRelayers =
608608
RialtoGrandpaInstance,
609609
WithRialtoParachainsInstance,
610610
WithRialtoParachainMessagesInstance,
611-
BridgeRejectObsoleteHeadersAndMessages,
612611
RialtoParachainId,
613612
RialtoParachainMessagesLane,
614613
Runtime,

bin/runtime-common/src/lib.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,16 @@
1818
1919
#![cfg_attr(not(feature = "std"), no_std)]
2020

21-
use bp_runtime::FilterCall;
21+
use crate::messages_call_ext::MessagesCallSubType;
22+
use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType;
23+
use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype;
2224
use sp_runtime::transaction_validity::TransactionValidity;
2325
use xcm::v3::NetworkId;
2426

2527
pub mod messages;
2628
pub mod messages_api;
2729
pub mod messages_benchmarking;
28-
pub mod messages_extension;
30+
pub mod messages_call_ext;
2931
pub mod parachains_benchmarking;
3032
pub mod refund_relayer_extension;
3133

@@ -44,21 +46,39 @@ pub trait BridgeRuntimeFilterCall<Call> {
4446
fn validate(call: &Call) -> TransactionValidity;
4547
}
4648

47-
impl<Call, T, I> BridgeRuntimeFilterCall<Call> for pallet_bridge_grandpa::Pallet<T, I>
49+
impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall> for pallet_bridge_grandpa::Pallet<T, I>
4850
where
49-
pallet_bridge_grandpa::Pallet<T, I>: FilterCall<Call>,
51+
T: pallet_bridge_grandpa::Config<I>,
52+
T::RuntimeCall: GrandpaCallSubType<T, I>,
5053
{
51-
fn validate(call: &Call) -> TransactionValidity {
52-
<pallet_bridge_grandpa::Pallet<T, I> as FilterCall<Call>>::validate(call)
54+
fn validate(call: &T::RuntimeCall) -> TransactionValidity {
55+
GrandpaCallSubType::<T, I>::check_obsolete_submit_finality_proof(call)
5356
}
5457
}
5558

56-
impl<Call, T, I> BridgeRuntimeFilterCall<Call> for pallet_bridge_parachains::Pallet<T, I>
59+
impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall>
60+
for pallet_bridge_parachains::Pallet<T, I>
5761
where
58-
pallet_bridge_parachains::Pallet<T, I>: FilterCall<Call>,
62+
T: pallet_bridge_parachains::Config<I>,
63+
T::RuntimeCall: ParachainsCallSubtype<T, I>,
5964
{
60-
fn validate(call: &Call) -> TransactionValidity {
61-
<pallet_bridge_parachains::Pallet<T, I> as FilterCall<Call>>::validate(call)
65+
fn validate(call: &T::RuntimeCall) -> TransactionValidity {
66+
ParachainsCallSubtype::<T, I>::check_obsolete_submit_parachain_heads(call)
67+
}
68+
}
69+
70+
impl<T: pallet_bridge_messages::Config<I>, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall>
71+
for pallet_bridge_messages::Pallet<T, I>
72+
where
73+
T::RuntimeCall: MessagesCallSubType<T, I>,
74+
{
75+
/// Validate messages in order to avoid "mining" messages delivery and delivery confirmation
76+
/// transactions, that are delivering outdated messages/confirmations. Without this validation,
77+
/// even honest relayers may lose their funds if there are multiple relays running and
78+
/// submitting the same messages/confirmations.
79+
fn validate(call: &T::RuntimeCall) -> TransactionValidity {
80+
call.check_obsolete_receive_messages_proof()?;
81+
call.check_obsolete_receive_messages_delivery_proof()
6282
}
6383
}
6484

bin/runtime-common/src/messages_extension.rs renamed to bin/runtime-common/src/messages_call_ext.rs

Lines changed: 104 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,56 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
1616

17-
use crate::{
18-
messages::{
19-
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
20-
},
21-
BridgeRuntimeFilterCall,
17+
use crate::messages::{
18+
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
2219
};
20+
use bp_messages::{LaneId, MessageNonce};
2321
use frame_support::{dispatch::CallableCallFor, traits::IsSubType};
2422
use pallet_bridge_messages::{Config, Pallet};
25-
use sp_runtime::transaction_validity::TransactionValidity;
23+
use sp_runtime::{transaction_validity::TransactionValidity, RuntimeDebug};
24+
25+
/// Info about a `ReceiveMessagesProof` call which tries to update a single lane.
26+
#[derive(Copy, Clone, PartialEq, RuntimeDebug)]
27+
pub struct ReceiveMessagesProofInfo {
28+
pub lane_id: LaneId,
29+
pub best_proof_nonce: MessageNonce,
30+
pub best_stored_nonce: MessageNonce,
31+
}
32+
33+
/// Helper struct that provides methods for working with the `ReceiveMessagesProof` call.
34+
pub struct ReceiveMessagesProofHelper<T: Config<I>, I: 'static> {
35+
pub _phantom_data: sp_std::marker::PhantomData<(T, I)>,
36+
}
37+
38+
impl<T: Config<I>, I: 'static> ReceiveMessagesProofHelper<T, I> {
39+
/// Check if the `ReceiveMessagesProof` call delivered at least some of the messages that
40+
/// it contained.
41+
pub fn was_partially_successful(info: &ReceiveMessagesProofInfo) -> bool {
42+
let inbound_lane_data = pallet_bridge_messages::InboundLanes::<T, I>::get(info.lane_id);
43+
inbound_lane_data.last_delivered_nonce() > info.best_stored_nonce
44+
}
45+
}
46+
47+
/// Trait representing a call that is a sub type of `pallet_bridge_messages::Call`.
48+
pub trait MessagesCallSubType<T: Config<I, RuntimeCall = Self>, I: 'static>:
49+
IsSubType<CallableCallFor<Pallet<T, I>, T>>
50+
{
51+
/// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call.
52+
fn receive_messages_proof_info(&self) -> Option<ReceiveMessagesProofInfo>;
53+
54+
/// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call,
55+
/// if the call is for the provided lane.
56+
fn receive_messages_proof_info_for(&self, lane_id: LaneId) -> Option<ReceiveMessagesProofInfo>;
57+
58+
/// Check that a `ReceiveMessagesProof` call is trying to deliver at least some messages that
59+
/// are better than the ones we know of.
60+
fn check_obsolete_receive_messages_proof(&self) -> TransactionValidity;
61+
62+
/// Check that a `ReceiveMessagesDeliveryProof` call is trying to deliver at least some message
63+
/// confirmations that are better than the ones we know of.
64+
fn check_obsolete_receive_messages_delivery_proof(&self) -> TransactionValidity;
65+
}
2666

27-
/// Validate messages in order to avoid "mining" messages delivery and delivery confirmation
28-
/// transactions, that are delivering outdated messages/confirmations. Without this validation,
29-
/// even honest relayers may lose their funds if there are multiple relays running and submitting
30-
/// the same messages/confirmations.
3167
impl<
3268
BridgedHeaderHash,
3369
SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain<
@@ -42,52 +78,69 @@ impl<
4278
T: frame_system::Config<RuntimeCall = Call>
4379
+ Config<I, SourceHeaderChain = SourceHeaderChain, TargetHeaderChain = TargetHeaderChain>,
4480
I: 'static,
45-
> BridgeRuntimeFilterCall<Call> for Pallet<T, I>
81+
> MessagesCallSubType<T, I> for T::RuntimeCall
4682
{
47-
fn validate(call: &Call) -> TransactionValidity {
48-
match call.is_sub_type() {
49-
Some(pallet_bridge_messages::Call::<T, I>::receive_messages_proof {
50-
ref proof,
51-
..
52-
}) => {
53-
let inbound_lane_data =
54-
pallet_bridge_messages::InboundLanes::<T, I>::get(proof.lane);
55-
if proof.nonces_end <= inbound_lane_data.last_delivered_nonce() {
56-
log::trace!(
57-
target: pallet_bridge_messages::LOG_TARGET,
58-
"Rejecting obsolete messages delivery transaction: \
83+
fn receive_messages_proof_info(&self) -> Option<ReceiveMessagesProofInfo> {
84+
if let Some(pallet_bridge_messages::Call::<T, I>::receive_messages_proof {
85+
ref proof,
86+
..
87+
}) = self.is_sub_type()
88+
{
89+
let inbound_lane_data = pallet_bridge_messages::InboundLanes::<T, I>::get(proof.lane);
90+
91+
return Some(ReceiveMessagesProofInfo {
92+
lane_id: proof.lane,
93+
best_proof_nonce: proof.nonces_end,
94+
best_stored_nonce: inbound_lane_data.last_delivered_nonce(),
95+
})
96+
}
97+
98+
None
99+
}
100+
101+
fn receive_messages_proof_info_for(&self, lane_id: LaneId) -> Option<ReceiveMessagesProofInfo> {
102+
self.receive_messages_proof_info().filter(|info| info.lane_id == lane_id)
103+
}
104+
105+
fn check_obsolete_receive_messages_proof(&self) -> TransactionValidity {
106+
if let Some(proof_info) = self.receive_messages_proof_info() {
107+
if proof_info.best_proof_nonce <= proof_info.best_stored_nonce {
108+
log::trace!(
109+
target: pallet_bridge_messages::LOG_TARGET,
110+
"Rejecting obsolete messages delivery transaction: \
59111
lane {:?}, bundled {:?}, best {:?}",
60-
proof.lane,
61-
proof.nonces_end,
62-
inbound_lane_data.last_delivered_nonce(),
63-
);
112+
proof_info.lane_id,
113+
proof_info.best_proof_nonce,
114+
proof_info.best_stored_nonce,
115+
);
64116

65-
return sp_runtime::transaction_validity::InvalidTransaction::Stale.into()
66-
}
67-
},
68-
Some(pallet_bridge_messages::Call::<T, I>::receive_messages_delivery_proof {
69-
ref proof,
70-
ref relayers_state,
71-
..
72-
}) => {
73-
let latest_delivered_nonce = relayers_state.last_delivered_nonce;
74-
75-
let outbound_lane_data =
76-
pallet_bridge_messages::OutboundLanes::<T, I>::get(proof.lane);
77-
if latest_delivered_nonce <= outbound_lane_data.latest_received_nonce {
78-
log::trace!(
79-
target: pallet_bridge_messages::LOG_TARGET,
80-
"Rejecting obsolete messages confirmation transaction: \
117+
return sp_runtime::transaction_validity::InvalidTransaction::Stale.into()
118+
}
119+
}
120+
121+
Ok(sp_runtime::transaction_validity::ValidTransaction::default())
122+
}
123+
124+
fn check_obsolete_receive_messages_delivery_proof(&self) -> TransactionValidity {
125+
if let Some(pallet_bridge_messages::Call::<T, I>::receive_messages_delivery_proof {
126+
ref proof,
127+
ref relayers_state,
128+
..
129+
}) = self.is_sub_type()
130+
{
131+
let outbound_lane_data = pallet_bridge_messages::OutboundLanes::<T, I>::get(proof.lane);
132+
if relayers_state.last_delivered_nonce <= outbound_lane_data.latest_received_nonce {
133+
log::trace!(
134+
target: pallet_bridge_messages::LOG_TARGET,
135+
"Rejecting obsolete messages confirmation transaction: \
81136
lane {:?}, bundled {:?}, best {:?}",
82-
proof.lane,
83-
latest_delivered_nonce,
84-
outbound_lane_data.latest_received_nonce,
85-
);
137+
proof.lane,
138+
relayers_state.last_delivered_nonce,
139+
outbound_lane_data.latest_received_nonce,
140+
);
86141

87-
return sp_runtime::transaction_validity::InvalidTransaction::Stale.into()
88-
}
89-
},
90-
_ => {},
142+
return sp_runtime::transaction_validity::InvalidTransaction::Stale.into()
143+
}
91144
}
92145

93146
Ok(sp_runtime::transaction_validity::ValidTransaction::default())

0 commit comments

Comments
 (0)