Skip to content

Commit 023689c

Browse files
authored
Do not require new headers if lane is empty (#1725)
* do not require new headers if lane is empty * handle edge case (need proof-of-delivery-confirmations to be able to submit delivery tx) in required_source_header_at_target * clippy
1 parent bddf1fa commit 023689c

File tree

1 file changed

+90
-21
lines changed

1 file changed

+90
-21
lines changed

relays/messages/src/message_race_delivery.rs

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -318,16 +318,31 @@ where
318318
&self,
319319
current_best: &SourceHeaderIdOf<P>,
320320
) -> Option<SourceHeaderIdOf<P>> {
321+
let has_nonces_to_deliver = !self.strategy.is_empty();
321322
let header_required_for_messages_delivery =
322323
self.strategy.required_source_header_at_target(current_best);
323-
let header_required_for_reward_confirmations_delivery =
324-
self.latest_confirmed_nonces_at_source.back().map(|(id, _)| id.clone());
324+
let header_required_for_reward_confirmations_delivery = self
325+
.latest_confirmed_nonces_at_source
326+
.back()
327+
.filter(|(id, nonce)| *nonce != 0 && id.0 > current_best.0)
328+
.map(|(id, _)| id.clone());
325329
match (
330+
has_nonces_to_deliver,
326331
header_required_for_messages_delivery,
327332
header_required_for_reward_confirmations_delivery,
328333
) {
329-
(Some(id1), Some(id2)) => Some(if id1.0 > id2.0 { id1 } else { id2 }),
330-
(a, b) => a.or(b),
334+
// if we need to delver messages and proof-of-delivery-confirmations, then we need to
335+
// select the most recent header to avoid extra roundtrips
336+
(true, Some(id1), Some(id2)) => Some(if id1.0 > id2.0 { id1 } else { id2 }),
337+
// if we only need to deliver messages - fine, let's require some source header
338+
//
339+
// if we need new header for proof-of-delivery-confirmations - let's also ask for that.
340+
// Even though it may require additional header, we'll be sure that we won't block the
341+
// lane (sometimes we can't deliver messages without proof-of-delivery-confirmations)
342+
(true, a, b) => a.or(b),
343+
// we never submit delivery transaction without messages, so if `has_nonces_to_deliver`
344+
// if `false`, we don't need any source headers at target
345+
(false, _, _) => None,
331346
}
332347
}
333348

@@ -926,38 +941,58 @@ mod tests {
926941
// - all messages [20; 23] have been generated at source block#1;
927942
let (mut state, mut strategy) = prepare_strategy();
928943
//
929-
// - messages [20; 21] have been delivered, but messages [11; 20] can't be delivered because
930-
// of unrewarded relayers vector capacity;
931-
strategy.max_unconfirmed_nonces_at_target = 2;
944+
// - messages [20; 23] have been delivered
932945
assert_eq!(
933946
strategy.select_nonces_to_deliver(state.clone()).await,
934-
Some(((20..=21), proof_parameters(false, 2)))
947+
Some(((20..=23), proof_parameters(false, 4)))
935948
);
936949
strategy.finalized_target_nonces_updated(
937950
TargetClientNonces {
938-
latest_nonce: 21,
951+
latest_nonce: 23,
939952
nonces_data: DeliveryRaceTargetNoncesData {
940953
confirmed_nonce: 19,
941954
unrewarded_relayers: UnrewardedRelayersState {
942-
unrewarded_relayer_entries: 2,
943-
messages_in_oldest_entry: 2,
944-
total_messages: 2,
945-
last_delivered_nonce: 19,
955+
unrewarded_relayer_entries: 1,
956+
messages_in_oldest_entry: 4,
957+
total_messages: 4,
958+
last_delivered_nonce: 23,
946959
},
947960
},
948961
},
949962
&mut state,
950963
);
951-
assert_eq!(strategy.select_nonces_to_deliver(state).await, None);
952-
//
953-
// - messages [1; 10] receiving confirmation has been delivered at source block#2;
954-
strategy.source_nonces_updated(
955-
header_id(2),
956-
SourceClientNonces { new_nonces: MessageDetailsMap::new(), confirmed_nonce: Some(21) },
957-
);
964+
// nothing needs to be delivered now and we don't need any new headers
965+
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
966+
assert_eq!(strategy.required_source_header_at_target(&header_id(1)), None);
967+
968+
// now let's generate two more nonces [24; 25] at the soruce;
969+
strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 19, 0));
958970
//
959-
// - so now we'll need to relay source block#11 to be able to accept messages [11; 20].
971+
// - so now we'll need to relay source block#2 to be able to accept messages [24; 25].
972+
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
960973
assert_eq!(strategy.required_source_header_at_target(&header_id(1)), Some(header_id(2)));
974+
975+
// let's relay source block#2
976+
state.best_finalized_source_header_id_at_source = Some(header_id(2));
977+
state.best_finalized_source_header_id_at_best_target = Some(header_id(2));
978+
state.best_target_header_id = Some(header_id(2));
979+
state.best_finalized_target_header_id = Some(header_id(2));
980+
981+
// and ask strategy again => still nothing to deliver, because parallel confirmations
982+
// race need to be pushed further
983+
assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None);
984+
assert_eq!(strategy.required_source_header_at_target(&header_id(2)), None);
985+
986+
// let's confirm messages [20; 23]
987+
strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 23, 0));
988+
989+
// and ask strategy again => now we have everything required to deliver remaining
990+
// [24; 25] nonces and proof of [20; 23] confirmation
991+
assert_eq!(
992+
strategy.select_nonces_to_deliver(state).await,
993+
Some(((24..=25), proof_parameters(true, 2))),
994+
);
995+
assert_eq!(strategy.required_source_header_at_target(&header_id(2)), None);
961996
}
962997

963998
#[async_std::test]
@@ -985,4 +1020,38 @@ mod tests {
9851020
Some(((20..=24), proof_parameters(false, 5)))
9861021
);
9871022
}
1023+
1024+
#[test]
1025+
#[allow(clippy::reversed_empty_ranges)]
1026+
fn no_source_headers_required_at_target_if_lanes_are_empty() {
1027+
let mut strategy = TestStrategy {
1028+
max_unrewarded_relayer_entries_at_target: 4,
1029+
max_unconfirmed_nonces_at_target: 4,
1030+
max_messages_in_single_batch: 4,
1031+
max_messages_weight_in_single_batch: Weight::from_ref_time(4),
1032+
max_messages_size_in_single_batch: 4,
1033+
latest_confirmed_nonces_at_source: VecDeque::new(),
1034+
lane_source_client: TestSourceClient::default(),
1035+
lane_target_client: TestTargetClient::default(),
1036+
metrics_msg: None,
1037+
target_nonces: None,
1038+
strategy: BasicStrategy::new(),
1039+
};
1040+
1041+
let source_header_id = header_id(10);
1042+
strategy.source_nonces_updated(
1043+
source_header_id,
1044+
// MessageDeliveryRaceSource::nonces returns Some(0), because that's how it is
1045+
// represented in memory (there's no Options in OutboundLaneState)
1046+
source_nonces(1u64..=0u64, 0, 0),
1047+
);
1048+
1049+
// even though `latest_confirmed_nonces_at_source` is not empty, new headers are not
1050+
// requested
1051+
assert_eq!(
1052+
strategy.latest_confirmed_nonces_at_source,
1053+
VecDeque::from([(source_header_id, 0)])
1054+
);
1055+
assert_eq!(strategy.required_source_header_at_target(&source_header_id), None);
1056+
}
9881057
}

0 commit comments

Comments
 (0)