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,
};
pub struct RelayReference<P: MessageLane> {
pub selected_size: u32,
pub index: usize,
pub nonce: MessageNonce,
pub details: MessageDetails<P::SourceChainBalance>,
}
pub struct RelayMessagesBatchReference<P: MessageLane> {
pub max_messages_in_this_batch: MessageNonce,
pub max_messages_weight_in_single_batch: Weight,
pub max_messages_size_in_single_batch: u32,
pub best_target_nonce: MessageNonce,
pub nonces_queue: SourceRangesQueue<
P::SourceHeaderHash,
P::SourceHeaderNumber,
MessageDetailsMap<P::SourceChainBalance>,
>,
pub nonces_queue_range: RangeInclusive<usize>,
}
#[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(),
);
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;
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,
};
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,
};
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
}
}
}