1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
34// 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.
89// 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.
1314// 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/>.
1617//! enforcement strategy
1819use num_traits::Zero;
20use std::ops::RangeInclusive;
2122use bp_messages::{MessageNonce, Weight};
2324use crate::{
25 message_lane::MessageLane,
26 message_lane_loop::{MessageDetails, MessageDetailsMap},
27 message_race_loop::NoncesRange,
28 message_race_strategy::SourceRangesQueue,
29};
3031/// Reference data for participating in relay
32pub struct RelayReference<P: MessageLane> {
33/// Messages size summary
34pub selected_size: u32,
3536/// Index by all ready nonces
37pub index: usize,
38/// Current nonce
39pub nonce: MessageNonce,
40/// Current nonce details
41pub details: MessageDetails<P::SourceChainBalance>,
42}
4344/// Relay reference data
45pub struct RelayMessagesBatchReference<P: MessageLane> {
46/// Maximal number of relayed messages in single delivery transaction.
47pub max_messages_in_this_batch: MessageNonce,
48/// Maximal cumulative dispatch weight of relayed messages in single delivery transaction.
49pub max_messages_weight_in_single_batch: Weight,
50/// Maximal cumulative size of relayed messages in single delivery transaction.
51pub 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.
54pub best_target_nonce: MessageNonce,
55/// Source queue.
56pub 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.
62pub nonces_queue_range: RangeInclusive<usize>,
63}
6465/// Limits of the message race transactions.
66#[derive(Clone)]
67pub struct MessageRaceLimits;
6869impl MessageRaceLimits {
70pub async fn decide<P: MessageLane>(
71 reference: RelayMessagesBatchReference<P>,
72 ) -> Option<RangeInclusive<MessageNonce>> {
73let mut hard_selected_count = 0;
7475let mut selected_weight = Weight::zero();
76let mut selected_count: MessageNonce = 0;
7778let 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 );
8283// relay reference
84let mut relay_reference = RelayReference::<P> {
85 selected_size: 0,
8687 index: 0,
88 nonce: 0,
89 details: MessageDetails {
90 dispatch_weight: Weight::zero(),
91 size: 0,
92 reward: P::SourceChainBalance::zero(),
93 },
94 };
9596let 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();
102for (index, (nonce, details)) in all_ready_nonces {
103 relay_reference.index = index;
104 relay_reference.nonce = *nonce;
105 relay_reference.details = *details;
106107// 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.
111112 // limit messages in the batch by weight
113let new_selected_weight = match selected_weight.checked_add(&details.dispatch_weight) {
114Some(new_selected_weight)
115if 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 => {
119log::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 };
130131// limit messages in the batch by size
132let new_selected_size = match relay_reference.selected_size.checked_add(details.size) {
133Some(new_selected_size)
134if new_selected_size <= reference.max_messages_size_in_single_batch =>
135 new_selected_size,
136 new_selected_size if selected_count == 0 => {
137log::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 };
148149// limit number of messages in the batch
150let new_selected_count = selected_count + 1;
151if new_selected_count > reference.max_messages_in_this_batch {
152break
153}
154 relay_reference.selected_size = new_selected_size;
155156 hard_selected_count = index + 1;
157 selected_weight = new_selected_weight;
158 selected_count = new_selected_count;
159 }
160161if hard_selected_count != 0 {
162let selected_max_nonce =
163 hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1;
164Some(hard_selected_begin_nonce..=selected_max_nonce)
165 } else {
166None
167}
168 }
169}