referrerpolicy=no-referrer-when-downgrade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.

//! enforcement strategy

use num_traits::Zero;
use std::ops::RangeInclusive;

use bp_messages::{MessageNonce, Weight};

use crate::{
	message_lane::MessageLane,
	message_lane_loop::{MessageDetails, MessageDetailsMap},
	message_race_loop::NoncesRange,
	message_race_strategy::SourceRangesQueue,
};

/// Reference data for participating in relay
pub struct RelayReference<P: MessageLane> {
	/// Messages size summary
	pub selected_size: u32,

	/// Index by all ready nonces
	pub index: usize,
	/// Current nonce
	pub nonce: MessageNonce,
	/// Current nonce details
	pub details: MessageDetails<P::SourceChainBalance>,
}

/// Relay reference data
pub struct RelayMessagesBatchReference<P: MessageLane> {
	/// Maximal number of relayed messages in single delivery transaction.
	pub max_messages_in_this_batch: MessageNonce,
	/// Maximal cumulative dispatch weight of relayed messages in single delivery transaction.
	pub max_messages_weight_in_single_batch: Weight,
	/// Maximal cumulative size of relayed messages in single delivery transaction.
	pub max_messages_size_in_single_batch: u32,
	/// Best available nonce at the **best** target block. We do not want to deliver nonces
	/// less than this nonce, even though the block may be retracted.
	pub best_target_nonce: MessageNonce,
	/// Source queue.
	pub nonces_queue: SourceRangesQueue<
		P::SourceHeaderHash,
		P::SourceHeaderNumber,
		MessageDetailsMap<P::SourceChainBalance>,
	>,
	/// Range of indices within the `nonces_queue` that are available for selection.
	pub nonces_queue_range: RangeInclusive<usize>,
}

/// Limits of the message race transactions.
#[derive(Clone)]
pub struct MessageRaceLimits;

impl MessageRaceLimits {
	pub async fn decide<P: MessageLane>(
		reference: RelayMessagesBatchReference<P>,
	) -> Option<RangeInclusive<MessageNonce>> {
		let mut hard_selected_count = 0;

		let mut selected_weight = Weight::zero();
		let mut selected_count: MessageNonce = 0;

		let hard_selected_begin_nonce = std::cmp::max(
			reference.best_target_nonce + 1,
			reference.nonces_queue[*reference.nonces_queue_range.start()].1.begin(),
		);

		// relay reference
		let mut relay_reference = RelayReference::<P> {
			selected_size: 0,

			index: 0,
			nonce: 0,
			details: MessageDetails {
				dispatch_weight: Weight::zero(),
				size: 0,
				reward: P::SourceChainBalance::zero(),
			},
		};

		let all_ready_nonces = reference
			.nonces_queue
			.range(reference.nonces_queue_range.clone())
			.flat_map(|(_, ready_nonces)| ready_nonces.iter())
			.filter(|(nonce, _)| **nonce >= hard_selected_begin_nonce)
			.enumerate();
		for (index, (nonce, details)) in all_ready_nonces {
			relay_reference.index = index;
			relay_reference.nonce = *nonce;
			relay_reference.details = *details;

			// Since we (hopefully) have some reserves in `max_messages_weight_in_single_batch`
			// and `max_messages_size_in_single_batch`, we may still try to submit transaction
			// with single message if message overflows these limits. The worst case would be if
			// transaction will be rejected by the target runtime, but at least we have tried.

			// limit messages in the batch by weight
			let new_selected_weight = match selected_weight.checked_add(&details.dispatch_weight) {
				Some(new_selected_weight)
					if new_selected_weight
						.all_lte(reference.max_messages_weight_in_single_batch) =>
					new_selected_weight,
				new_selected_weight if selected_count == 0 => {
					log::warn!(
						target: "bridge",
						"Going to submit message delivery transaction with declared dispatch \
						weight {:?} that overflows maximal configured weight {}",
						new_selected_weight,
						reference.max_messages_weight_in_single_batch,
					);
					new_selected_weight.unwrap_or(Weight::MAX)
				},
				_ => break,
			};

			// limit messages in the batch by size
			let new_selected_size = match relay_reference.selected_size.checked_add(details.size) {
				Some(new_selected_size)
					if new_selected_size <= reference.max_messages_size_in_single_batch =>
					new_selected_size,
				new_selected_size if selected_count == 0 => {
					log::warn!(
						target: "bridge",
						"Going to submit message delivery transaction with message \
						size {:?} that overflows maximal configured size {}",
						new_selected_size,
						reference.max_messages_size_in_single_batch,
					);
					new_selected_size.unwrap_or(u32::MAX)
				},
				_ => break,
			};

			// limit number of messages in the batch
			let new_selected_count = selected_count + 1;
			if new_selected_count > reference.max_messages_in_this_batch {
				break
			}
			relay_reference.selected_size = new_selected_size;

			hard_selected_count = index + 1;
			selected_weight = new_selected_weight;
			selected_count = new_selected_count;
		}

		if hard_selected_count != 0 {
			let selected_max_nonce =
				hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1;
			Some(hard_selected_begin_nonce..=selected_max_nonce)
		} else {
			None
		}
	}
}