// 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 . //! 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, SourceClient as MessageLaneSourceClient, TargetClient as MessageLaneTargetClient, }, message_race_loop::NoncesRange, message_race_strategy::SourceRangesQueue, metrics::MessageLaneLoopMetrics, }; /// Reference data for participating in relay pub struct RelayReference< P: MessageLane, SourceClient: MessageLaneSourceClient

, TargetClient: MessageLaneTargetClient

, > { /// The client that is connected to the message lane source node. pub lane_source_client: SourceClient, /// The client that is connected to the message lane target node. pub lane_target_client: TargetClient, /// Metrics reference. pub metrics: Option, /// Messages size summary pub selected_size: u32, /// Hard check begin nonce pub hard_selected_begin_nonce: MessageNonce, /// Index by all ready nonces pub index: usize, /// Current nonce pub nonce: MessageNonce, /// Current nonce details pub details: MessageDetails, } /// Relay reference data pub struct RelayMessagesBatchReference< P: MessageLane, SourceClient: MessageLaneSourceClient

, TargetClient: MessageLaneTargetClient

, > { /// 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, /// The client that is connected to the message lane source node. pub lane_source_client: SourceClient, /// The client that is connected to the message lane target node. pub lane_target_client: TargetClient, /// Metrics reference. pub metrics: Option, /// 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, >, /// Range of indices within the `nonces_queue` that are available for selection. pub nonces_queue_range: RangeInclusive, } /// Limits of the message race transactions. #[derive(Clone)] pub struct MessageRaceLimits; impl MessageRaceLimits { pub async fn decide< P: MessageLane, SourceClient: MessageLaneSourceClient

, TargetClient: MessageLaneTargetClient

, >( reference: RelayMessagesBatchReference, ) -> Option> { 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 { lane_source_client: reference.lane_source_client.clone(), lane_target_client: reference.lane_target_client.clone(), metrics: reference.metrics.clone(), selected_size: 0, hard_selected_begin_nonce, 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 } } }