Complex headers+messages Millau<->Rialto relay (#878)

* complex headers+messages relay

* post-merge fix

* fix + test issue with on-demand not starting
This commit is contained in:
Svyatoslav Nikolsky
2021-04-14 13:01:03 +03:00
committed by Bastian Köcher
parent 0d60f42b5e
commit e2131724fb
29 changed files with 931 additions and 308 deletions
@@ -140,8 +140,8 @@ pub trait SourceClient<P: MessageLane>: RelayClient {
proof: P::MessagesReceivingProof,
) -> Result<(), Self::Error>;
/// Activate (or deactivate) headers relay that relays target headers to source node.
async fn activate_target_to_source_headers_relay(&self, activate: bool);
/// We need given finalized target header on source to continue synchronization.
async fn require_target_header_on_source(&self, id: TargetHeaderIdOf<P>);
}
/// Target client trait.
@@ -181,8 +181,8 @@ pub trait TargetClient<P: MessageLane>: RelayClient {
proof: P::MessagesProof,
) -> Result<RangeInclusive<MessageNonce>, Self::Error>;
/// Activate (or deactivate) headers relay that relays source headers to target node.
async fn activate_source_to_target_headers_relay(&self, activate: bool);
/// We need given finalized source header on target to continue synchronization.
async fn require_source_header_on_target(&self, id: SourceHeaderIdOf<P>);
}
/// State of the client.
@@ -232,9 +232,9 @@ pub async fn run<P: MessageLane>(
let exit_signal = exit_signal.shared();
relay_utils::relay_loop(source_client, target_client)
.reconnect_delay(params.reconnect_delay)
.with_metrics(metrics_prefix::<P>(&params.lane), metrics_params)
.loop_metric(MessageLaneLoopMetrics::default())?
.standalone_metric(GlobalMetrics::default())?
.with_metrics(Some(metrics_prefix::<P>(&params.lane)), metrics_params)
.loop_metric(|registry, prefix| MessageLaneLoopMetrics::new(registry, prefix))?
.standalone_metric(|registry, prefix| GlobalMetrics::new(registry, prefix))?
.expose()
.await?
.run(|source_client, target_client, metrics| {
@@ -475,8 +475,10 @@ pub(crate) mod tests {
target_latest_received_nonce: MessageNonce,
target_latest_confirmed_received_nonce: MessageNonce,
submitted_messages_proofs: Vec<TestMessagesProof>,
is_target_to_source_headers_relay_activated: bool,
is_source_to_target_headers_relay_activated: bool,
target_to_source_header_required: Option<TestTargetHeaderId>,
target_to_source_header_requirements: Vec<TestTargetHeaderId>,
source_to_target_header_required: Option<TestSourceHeaderId>,
source_to_target_header_requirements: Vec<TestSourceHeaderId>,
}
#[derive(Clone)]
@@ -582,9 +584,10 @@ pub(crate) mod tests {
Ok(())
}
async fn activate_target_to_source_headers_relay(&self, activate: bool) {
async fn require_target_header_on_source(&self, id: TargetHeaderIdOf<TestMessageLane>) {
let mut data = self.data.lock();
data.is_target_to_source_headers_relay_activated = activate;
data.target_to_source_header_required = Some(id);
data.target_to_source_header_requirements.push(id);
(self.tick)(&mut *data);
}
}
@@ -686,9 +689,10 @@ pub(crate) mod tests {
Ok(nonces)
}
async fn activate_source_to_target_headers_relay(&self, activate: bool) {
async fn require_source_header_on_target(&self, id: SourceHeaderIdOf<TestMessageLane>) {
let mut data = self.data.lock();
data.is_source_to_target_headers_relay_activated = activate;
data.source_to_target_header_required = Some(id);
data.source_to_target_header_requirements.push(id);
(self.tick)(&mut *data);
}
}
@@ -806,16 +810,16 @@ pub(crate) mod tests {
},
Arc::new(|data: &mut TestClientData| {
// headers relay must only be started when we need new target headers at source node
if data.is_target_to_source_headers_relay_activated {
if data.target_to_source_header_required.is_some() {
assert!(data.source_state.best_finalized_peer_at_best_self.0 < data.target_state.best_self.0);
data.is_target_to_source_headers_relay_activated = false;
data.target_to_source_header_required = None;
}
}),
Arc::new(move |data: &mut TestClientData| {
// headers relay must only be started when we need new source headers at target node
if data.is_target_to_source_headers_relay_activated {
if data.source_to_target_header_required.is_some() {
assert!(data.target_state.best_finalized_peer_at_best_self.0 < data.source_state.best_self.0);
data.is_target_to_source_headers_relay_activated = false;
data.source_to_target_header_required = None;
}
// syncing source headers -> target chain (all at once)
if data.target_state.best_finalized_peer_at_best_self.0 < data.source_state.best_finalized_self.0 {
@@ -837,7 +841,7 @@ pub(crate) mod tests {
HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.0 + 1);
data.source_state.best_finalized_self = data.source_state.best_self;
}
// if source has received all messages receiving confirmations => increase source block so that confirmations may be sent
// if source has received all messages receiving confirmations => stop
if data.source_latest_confirmed_received_nonce == 10 {
exit_sender.unbounded_send(()).unwrap();
}
@@ -853,5 +857,9 @@ pub(crate) mod tests {
assert_eq!(result.submitted_messages_proofs[1].0, 5..=8);
assert_eq!(result.submitted_messages_proofs[2].0, 9..=10);
assert!(!result.submitted_messages_receiving_proofs.is_empty());
// check that we have at least once required new source->target or target->source headers
assert!(!result.target_to_source_header_requirements.is_empty());
assert!(!result.source_to_target_header_requirements.is_empty());
}
}
@@ -166,8 +166,8 @@ where
type Error = C::Error;
type TargetNoncesData = DeliveryRaceTargetNoncesData;
async fn require_more_source_headers(&self, activate: bool) {
self.client.activate_source_to_target_headers_relay(activate).await
async fn require_source_header(&self, id: SourceHeaderIdOf<P>) {
self.client.require_source_header_on_target(id).await
}
async fn nonces(
@@ -291,6 +291,10 @@ impl<P: MessageLane> RaceStrategy<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::M
self.strategy.is_empty()
}
fn required_source_header_at_target(&self, current_best: &SourceHeaderIdOf<P>) -> Option<SourceHeaderIdOf<P>> {
self.strategy.required_source_header_at_target(current_best)
}
fn best_at_source(&self) -> Option<MessageNonce> {
self.strategy.best_at_source()
}
@@ -123,8 +123,9 @@ pub trait TargetClient<P: MessageRace> {
/// Type of the additional data from the target client, used by the race.
type TargetNoncesData: std::fmt::Debug;
/// Ask headers relay to relay more headers from race source to race target.
async fn require_more_source_headers(&self, activate: bool);
/// Ask headers relay to relay finalized headers up to (and including) given header
/// from race source to race target.
async fn require_source_header(&self, id: P::SourceHeaderId);
/// Return nonces that are known to the target client.
async fn nonces(
@@ -152,6 +153,8 @@ pub trait RaceStrategy<SourceHeaderId, TargetHeaderId, Proof>: Debug {
/// Should return true if nothing has to be synced.
fn is_empty(&self) -> bool;
/// Return id of source header that is required to be on target to continue synchronization.
fn required_source_header_at_target(&self, current_best: &SourceHeaderId) -> Option<SourceHeaderId>;
/// Return best nonce at source node.
///
/// `Some` is returned only if we are sure that the value is greater or equal
@@ -219,7 +222,6 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
TargetNoncesData = TC::TargetNoncesData,
>,
) -> Result<(), FailedClient> {
let mut is_strategy_empty = true;
let mut progress_context = Instant::now();
let mut race_state = RaceState::default();
let mut stall_countdown = Instant::now();
@@ -307,6 +309,15 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
async_std::task::sleep,
|| format!("Error retrieving nonces from {}", P::source_name()),
).fail_if_connection_error(FailedClient::Source)?;
// ask for more headers if we have nonces to deliver and required headers are missing
let required_source_header_id = race_state
.best_finalized_source_header_id_at_best_target
.as_ref()
.and_then(|best|strategy.required_source_header_at_target(best));
if let Some(required_source_header_id) = required_source_header_id {
race_target.require_source_header(required_source_header_id).await;
}
},
nonces = target_best_nonces => {
target_best_nonces_required = false;
@@ -408,13 +419,6 @@ pub async fn run<P: MessageRace, SC: SourceClient<P>, TC: TargetClient<P>>(
progress_context = print_race_progress::<P, _>(progress_context, &strategy);
// ask for more headers if we have nonces to deliver
let prev_is_strategy_empty = is_strategy_empty;
is_strategy_empty = strategy.is_empty();
if is_strategy_empty != prev_is_strategy_empty {
race_target.require_more_source_headers(!is_strategy_empty).await;
}
if stall_countdown.elapsed() > stall_timeout {
log::warn!(
target: "bridge",
@@ -159,8 +159,8 @@ where
type Error = C::Error;
type TargetNoncesData = ();
async fn require_more_source_headers(&self, activate: bool) {
self.client.activate_target_to_source_headers_relay(activate).await
async fn require_source_header(&self, id: TargetHeaderIdOf<P>) {
self.client.require_target_header_on_source(id).await
}
async fn nonces(
@@ -162,6 +162,15 @@ where
self.source_queue.is_empty()
}
fn required_source_header_at_target(
&self,
current_best: &HeaderId<SourceHeaderHash, SourceHeaderNumber>,
) -> Option<HeaderId<SourceHeaderHash, SourceHeaderNumber>> {
self.source_queue
.back()
.and_then(|(h, _)| if h.0 > current_best.0 { Some(h.clone()) } else { None })
}
fn best_at_source(&self) -> Option<MessageNonce> {
let best_in_queue = self.source_queue.back().map(|(_, range)| range.end());
match (best_in_queue, self.best_target_nonce) {
+23 -20
View File
@@ -20,7 +20,7 @@ use crate::message_lane::MessageLane;
use crate::message_lane_loop::{SourceClientState, TargetClientState};
use bp_messages::MessageNonce;
use relay_utils::metrics::{register, GaugeVec, Metrics, Opts, Registry, U64};
use relay_utils::metrics::{metric_name, register, GaugeVec, Opts, PrometheusError, Registry, U64};
/// Message lane relay metrics.
///
@@ -34,25 +34,28 @@ pub struct MessageLaneLoopMetrics {
lane_state_nonces: GaugeVec<U64>,
}
impl Metrics for MessageLaneLoopMetrics {
fn register(&self, registry: &Registry) -> Result<(), String> {
register(self.best_block_numbers.clone(), registry).map_err(|e| e.to_string())?;
register(self.lane_state_nonces.clone(), registry).map_err(|e| e.to_string())?;
Ok(())
}
}
impl Default for MessageLaneLoopMetrics {
fn default() -> Self {
MessageLaneLoopMetrics {
best_block_numbers: GaugeVec::new(
Opts::new("best_block_numbers", "Best finalized block numbers"),
&["type"],
)
.expect("metric is static and thus valid; qed"),
lane_state_nonces: GaugeVec::new(Opts::new("lane_state_nonces", "Nonces of the lane state"), &["type"])
.expect("metric is static and thus valid; qed"),
}
impl MessageLaneLoopMetrics {
/// Create and register messages loop metrics.
pub fn new(registry: &Registry, prefix: Option<&str>) -> Result<Self, PrometheusError> {
Ok(MessageLaneLoopMetrics {
best_block_numbers: register(
GaugeVec::new(
Opts::new(
metric_name(prefix, "best_block_numbers"),
"Best finalized block numbers",
),
&["type"],
)?,
registry,
)?,
lane_state_nonces: register(
GaugeVec::new(
Opts::new(metric_name(prefix, "lane_state_nonces"), "Nonces of the lane state"),
&["type"],
)?,
registry,
)?,
})
}
}