referrerpolicy=no-referrer-when-downgrade

messages_relay/
message_race_limits.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! enforcement strategy
18
19use num_traits::Zero;
20use std::ops::RangeInclusive;
21
22use bp_messages::{MessageNonce, Weight};
23
24use crate::{
25	message_lane::MessageLane,
26	message_lane_loop::{MessageDetails, MessageDetailsMap},
27	message_race_loop::NoncesRange,
28	message_race_strategy::SourceRangesQueue,
29};
30
31/// Reference data for participating in relay
32pub struct RelayReference<P: MessageLane> {
33	/// Messages size summary
34	pub selected_size: u32,
35
36	/// Index by all ready nonces
37	pub index: usize,
38	/// Current nonce
39	pub nonce: MessageNonce,
40	/// Current nonce details
41	pub details: MessageDetails<P::SourceChainBalance>,
42}
43
44/// Relay reference data
45pub struct RelayMessagesBatchReference<P: MessageLane> {
46	/// Maximal number of relayed messages in single delivery transaction.
47	pub max_messages_in_this_batch: MessageNonce,
48	/// Maximal cumulative dispatch weight of relayed messages in single delivery transaction.
49	pub max_messages_weight_in_single_batch: Weight,
50	/// Maximal cumulative size of relayed messages in single delivery transaction.
51	pub max_messages_size_in_single_batch: u32,
52	/// Best available nonce at the **best** target block. We do not want to deliver nonces
53	/// less than this nonce, even though the block may be retracted.
54	pub best_target_nonce: MessageNonce,
55	/// Source queue.
56	pub nonces_queue: SourceRangesQueue<
57		P::SourceHeaderHash,
58		P::SourceHeaderNumber,
59		MessageDetailsMap<P::SourceChainBalance>,
60	>,
61	/// Range of indices within the `nonces_queue` that are available for selection.
62	pub nonces_queue_range: RangeInclusive<usize>,
63}
64
65/// Limits of the message race transactions.
66#[derive(Clone)]
67pub struct MessageRaceLimits;
68
69impl MessageRaceLimits {
70	pub async fn decide<P: MessageLane>(
71		reference: RelayMessagesBatchReference<P>,
72	) -> Option<RangeInclusive<MessageNonce>> {
73		let mut hard_selected_count = 0;
74
75		let mut selected_weight = Weight::zero();
76		let mut selected_count: MessageNonce = 0;
77
78		let hard_selected_begin_nonce = std::cmp::max(
79			reference.best_target_nonce + 1,
80			reference.nonces_queue[*reference.nonces_queue_range.start()].1.begin(),
81		);
82
83		// relay reference
84		let mut relay_reference = RelayReference::<P> {
85			selected_size: 0,
86
87			index: 0,
88			nonce: 0,
89			details: MessageDetails {
90				dispatch_weight: Weight::zero(),
91				size: 0,
92				reward: P::SourceChainBalance::zero(),
93			},
94		};
95
96		let all_ready_nonces = reference
97			.nonces_queue
98			.range(reference.nonces_queue_range.clone())
99			.flat_map(|(_, ready_nonces)| ready_nonces.iter())
100			.filter(|(nonce, _)| **nonce >= hard_selected_begin_nonce)
101			.enumerate();
102		for (index, (nonce, details)) in all_ready_nonces {
103			relay_reference.index = index;
104			relay_reference.nonce = *nonce;
105			relay_reference.details = *details;
106
107			// Since we (hopefully) have some reserves in `max_messages_weight_in_single_batch`
108			// and `max_messages_size_in_single_batch`, we may still try to submit transaction
109			// with single message if message overflows these limits. The worst case would be if
110			// transaction will be rejected by the target runtime, but at least we have tried.
111
112			// limit messages in the batch by weight
113			let new_selected_weight = match selected_weight.checked_add(&details.dispatch_weight) {
114				Some(new_selected_weight)
115					if new_selected_weight
116						.all_lte(reference.max_messages_weight_in_single_batch) =>
117					new_selected_weight,
118				new_selected_weight if selected_count == 0 => {
119					log::warn!(
120						target: "bridge",
121						"Going to submit message delivery transaction with declared dispatch \
122						weight {:?} that overflows maximal configured weight {}",
123						new_selected_weight,
124						reference.max_messages_weight_in_single_batch,
125					);
126					new_selected_weight.unwrap_or(Weight::MAX)
127				},
128				_ => break,
129			};
130
131			// limit messages in the batch by size
132			let new_selected_size = match relay_reference.selected_size.checked_add(details.size) {
133				Some(new_selected_size)
134					if new_selected_size <= reference.max_messages_size_in_single_batch =>
135					new_selected_size,
136				new_selected_size if selected_count == 0 => {
137					log::warn!(
138						target: "bridge",
139						"Going to submit message delivery transaction with message \
140						size {:?} that overflows maximal configured size {}",
141						new_selected_size,
142						reference.max_messages_size_in_single_batch,
143					);
144					new_selected_size.unwrap_or(u32::MAX)
145				},
146				_ => break,
147			};
148
149			// limit number of messages in the batch
150			let new_selected_count = selected_count + 1;
151			if new_selected_count > reference.max_messages_in_this_batch {
152				break
153			}
154			relay_reference.selected_size = new_selected_size;
155
156			hard_selected_count = index + 1;
157			selected_weight = new_selected_weight;
158			selected_count = new_selected_count;
159		}
160
161		if hard_selected_count != 0 {
162			let selected_max_nonce =
163				hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1;
164			Some(hard_selected_begin_nonce..=selected_max_nonce)
165		} else {
166			None
167		}
168	}
169}