mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 21:01:02 +00:00
Merge commit '392447f5c8f986ded2559a78457f4cd87942f393' into update-bridges-subtree-r/w
This commit is contained in:
@@ -43,18 +43,19 @@ pub struct FinalitySyncParams {
|
||||
/// `min(source_block_time, target_block_time)`.
|
||||
///
|
||||
/// This parameter may be used to limit transactions rate. Increase the value && you'll get
|
||||
/// infrequent updates => sparse headers => potential slow down of bridge applications, but pallet storage
|
||||
/// won't be super large. Decrease the value to near `source_block_time` and you'll get
|
||||
/// transaction for (almost) every block of the source chain => all source headers will be known
|
||||
/// to the target chain => bridge applications will run faster, but pallet storage may explode
|
||||
/// (but if pruning is there, then it's fine).
|
||||
/// infrequent updates => sparse headers => potential slow down of bridge applications, but
|
||||
/// pallet storage won't be super large. Decrease the value to near `source_block_time` and
|
||||
/// you'll get transaction for (almost) every block of the source chain => all source headers
|
||||
/// will be known to the target chain => bridge applications will run faster, but pallet
|
||||
/// storage may explode (but if pruning is there, then it's fine).
|
||||
pub tick: Duration,
|
||||
/// Number of finality proofs to keep in internal buffer between loop wakeups.
|
||||
/// Number of finality proofs to keep in internal buffer between loop iterations.
|
||||
///
|
||||
/// While in "major syncing" state, we still read finality proofs from the stream. They're stored
|
||||
/// in the internal buffer between loop wakeups. When we're close to the tip of the chain, we may
|
||||
/// meet finality delays if headers are not finalized frequently. So instead of waiting for next
|
||||
/// finality proof to appear in the stream, we may use existing proof from that buffer.
|
||||
/// While in "major syncing" state, we still read finality proofs from the stream. They're
|
||||
/// stored in the internal buffer between loop iterations. When we're close to the tip of the
|
||||
/// chain, we may meet finality delays if headers are not finalized frequently. So instead of
|
||||
/// waiting for next finality proof to appear in the stream, we may use existing proof from
|
||||
/// that buffer.
|
||||
pub recent_finality_proofs_limit: usize,
|
||||
/// Timeout before we treat our transactions as lost and restart the whole sync process.
|
||||
pub stall_timeout: Duration,
|
||||
@@ -89,10 +90,15 @@ pub trait TargetClient<P: FinalitySyncPipeline>: RelayClient {
|
||||
async fn best_finalized_source_block_number(&self) -> Result<P::Number, Self::Error>;
|
||||
|
||||
/// Submit header finality proof.
|
||||
async fn submit_finality_proof(&self, header: P::Header, proof: P::FinalityProof) -> Result<(), Self::Error>;
|
||||
async fn submit_finality_proof(
|
||||
&self,
|
||||
header: P::Header,
|
||||
proof: P::FinalityProof,
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs sync loop.
|
||||
/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs
|
||||
/// sync loop.
|
||||
pub fn metrics_prefix<P: FinalitySyncPipeline>() -> String {
|
||||
format!("{}_to_{}_Sync", P::SOURCE_NAME, P::TARGET_NAME)
|
||||
}
|
||||
@@ -104,12 +110,12 @@ pub async fn run<P: FinalitySyncPipeline>(
|
||||
sync_params: FinalitySyncParams,
|
||||
metrics_params: MetricsParams,
|
||||
exit_signal: impl Future<Output = ()> + 'static + Send,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), relay_utils::Error> {
|
||||
let exit_signal = exit_signal.shared();
|
||||
relay_utils::relay_loop(source_client, target_client)
|
||||
.with_metrics(Some(metrics_prefix::<P>()), metrics_params)
|
||||
.loop_metric(|registry, prefix| SyncLoopMetrics::new(registry, prefix))?
|
||||
.standalone_metric(|registry, prefix| GlobalMetrics::new(registry, prefix))?
|
||||
.loop_metric(SyncLoopMetrics::new)?
|
||||
.standalone_metric(GlobalMetrics::new)?
|
||||
.expose()
|
||||
.await?
|
||||
.run(metrics_prefix::<P>(), move |source_client, target_client, metrics| {
|
||||
@@ -127,15 +133,11 @@ pub async fn run<P: FinalitySyncPipeline>(
|
||||
/// Unjustified headers container. Ordered by header number.
|
||||
pub(crate) type UnjustifiedHeaders<H> = Vec<H>;
|
||||
/// Finality proofs container. Ordered by target header number.
|
||||
pub(crate) type FinalityProofs<P> = Vec<(
|
||||
<P as FinalitySyncPipeline>::Number,
|
||||
<P as FinalitySyncPipeline>::FinalityProof,
|
||||
)>;
|
||||
pub(crate) type FinalityProofs<P> =
|
||||
Vec<(<P as FinalitySyncPipeline>::Number, <P as FinalitySyncPipeline>::FinalityProof)>;
|
||||
/// Reference to finality proofs container.
|
||||
pub(crate) type FinalityProofsRef<'a, P> = &'a [(
|
||||
<P as FinalitySyncPipeline>::Number,
|
||||
<P as FinalitySyncPipeline>::FinalityProof,
|
||||
)];
|
||||
pub(crate) type FinalityProofsRef<'a, P> =
|
||||
&'a [(<P as FinalitySyncPipeline>::Number, <P as FinalitySyncPipeline>::FinalityProof)];
|
||||
|
||||
/// Error that may happen inside finality synchronization loop.
|
||||
#[derive(Debug)]
|
||||
@@ -186,10 +188,7 @@ pub(crate) struct RestartableFinalityProofsStream<S> {
|
||||
#[cfg(test)]
|
||||
impl<S> From<S> for RestartableFinalityProofsStream<S> {
|
||||
fn from(stream: S) -> Self {
|
||||
RestartableFinalityProofsStream {
|
||||
needs_restart: false,
|
||||
stream: Box::pin(stream),
|
||||
}
|
||||
RestartableFinalityProofsStream { needs_restart: false, stream: Box::pin(stream) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,14 +259,12 @@ async fn run_until_connection_lost<P: FinalitySyncPipeline>(
|
||||
last_transaction = updated_last_transaction;
|
||||
retry_backoff.reset();
|
||||
sync_params.tick
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
log::error!(target: "bridge", "Finality sync loop iteration has failed with error: {:?}", error);
|
||||
error.fail_if_connection_error()?;
|
||||
retry_backoff
|
||||
.next_backoff()
|
||||
.unwrap_or(relay_utils::relay_loop::RECONNECT_DELAY)
|
||||
}
|
||||
retry_backoff.next_backoff().unwrap_or(relay_utils::relay_loop::RECONNECT_DELAY)
|
||||
},
|
||||
};
|
||||
if finality_proofs_stream.needs_restart {
|
||||
log::warn!(target: "bridge", "{} finality proofs stream is being restarted", P::SOURCE_NAME);
|
||||
@@ -297,10 +294,8 @@ where
|
||||
TC: TargetClient<P>,
|
||||
{
|
||||
// read best source headers ids from source and target nodes
|
||||
let best_number_at_source = source_client
|
||||
.best_finalized_block_number()
|
||||
.await
|
||||
.map_err(Error::Source)?;
|
||||
let best_number_at_source =
|
||||
source_client.best_finalized_block_number().await.map_err(Error::Source)?;
|
||||
let best_number_at_target = target_client
|
||||
.best_finalized_source_block_number()
|
||||
.await
|
||||
@@ -309,7 +304,8 @@ where
|
||||
metrics_sync.update_best_block_at_source(best_number_at_source);
|
||||
metrics_sync.update_best_block_at_target(best_number_at_target);
|
||||
}
|
||||
*state.progress = print_sync_progress::<P>(*state.progress, best_number_at_source, best_number_at_target);
|
||||
*state.progress =
|
||||
print_sync_progress::<P>(*state.progress, best_number_at_source, best_number_at_target);
|
||||
|
||||
// if we have already submitted header, then we just need to wait for it
|
||||
// if we're waiting too much, then we believe our transaction has been lost and restart sync
|
||||
@@ -324,9 +320,9 @@ where
|
||||
P::TARGET_NAME,
|
||||
);
|
||||
|
||||
return Err(Error::Stalled);
|
||||
return Err(Error::Stalled)
|
||||
} else {
|
||||
return Ok(Some(last_transaction));
|
||||
return Ok(Some(last_transaction))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,10 +339,8 @@ where
|
||||
.await?
|
||||
{
|
||||
Some((header, justification)) => {
|
||||
let new_transaction = Transaction {
|
||||
time: Instant::now(),
|
||||
submitted_header_number: header.number(),
|
||||
};
|
||||
let new_transaction =
|
||||
Transaction { time: Instant::now(), submitted_header_number: header.number() };
|
||||
|
||||
log::debug!(
|
||||
target: "bridge",
|
||||
@@ -361,7 +355,7 @@ where
|
||||
.await
|
||||
.map_err(Error::Target)?;
|
||||
Ok(Some(new_transaction))
|
||||
}
|
||||
},
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
@@ -398,15 +392,15 @@ where
|
||||
)
|
||||
.await?;
|
||||
let (mut unjustified_headers, mut selected_finality_proof) = match selected_finality_proof {
|
||||
SelectedFinalityProof::Mandatory(header, finality_proof) => return Ok(Some((header, finality_proof))),
|
||||
SelectedFinalityProof::Mandatory(header, finality_proof) =>
|
||||
return Ok(Some((header, finality_proof))),
|
||||
_ if sync_params.only_mandatory_headers => {
|
||||
// we are not reading finality proofs from the stream, so eventually it'll break
|
||||
// but we don't care about transient proofs at all, so it is acceptable
|
||||
return Ok(None);
|
||||
}
|
||||
SelectedFinalityProof::Regular(unjustified_headers, header, finality_proof) => {
|
||||
(unjustified_headers, Some((header, finality_proof)))
|
||||
}
|
||||
return Ok(None)
|
||||
},
|
||||
SelectedFinalityProof::Regular(unjustified_headers, header, finality_proof) =>
|
||||
(unjustified_headers, Some((header, finality_proof))),
|
||||
SelectedFinalityProof::None(unjustified_headers) => (unjustified_headers, None),
|
||||
};
|
||||
|
||||
@@ -451,7 +445,11 @@ pub(crate) enum SelectedFinalityProof<Header, FinalityProof> {
|
||||
/// Otherwise, `SelectedFinalityProof::None` is returned.
|
||||
///
|
||||
/// Unless we have found mandatory header, all missing headers are collected and returned.
|
||||
pub(crate) async fn read_missing_headers<P: FinalitySyncPipeline, SC: SourceClient<P>, TC: TargetClient<P>>(
|
||||
pub(crate) async fn read_missing_headers<
|
||||
P: FinalitySyncPipeline,
|
||||
SC: SourceClient<P>,
|
||||
TC: TargetClient<P>,
|
||||
>(
|
||||
source_client: &SC,
|
||||
_target_client: &TC,
|
||||
best_number_at_source: P::Number,
|
||||
@@ -470,22 +468,30 @@ pub(crate) async fn read_missing_headers<P: FinalitySyncPipeline, SC: SourceClie
|
||||
match (is_mandatory, finality_proof) {
|
||||
(true, Some(finality_proof)) => {
|
||||
log::trace!(target: "bridge", "Header {:?} is mandatory", header_number);
|
||||
return Ok(SelectedFinalityProof::Mandatory(header, finality_proof));
|
||||
}
|
||||
return Ok(SelectedFinalityProof::Mandatory(header, finality_proof))
|
||||
},
|
||||
(true, None) => return Err(Error::MissingMandatoryFinalityProof(header.number())),
|
||||
(false, Some(finality_proof)) => {
|
||||
log::trace!(target: "bridge", "Header {:?} has persistent finality proof", header_number);
|
||||
unjustified_headers.clear();
|
||||
selected_finality_proof = Some((header, finality_proof));
|
||||
}
|
||||
},
|
||||
(false, None) => {
|
||||
unjustified_headers.push(header);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
header_number = header_number + One::one();
|
||||
}
|
||||
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"Read {} {} headers. Selected finality proof for header: {:?}",
|
||||
best_number_at_source.saturating_sub(best_number_at_target),
|
||||
P::SOURCE_NAME,
|
||||
selected_finality_proof.as_ref().map(|(header, _)| header),
|
||||
);
|
||||
|
||||
Ok(match selected_finality_proof {
|
||||
Some((header, proof)) => SelectedFinalityProof::Regular(unjustified_headers, header, proof),
|
||||
None => SelectedFinalityProof::None(unjustified_headers),
|
||||
@@ -493,22 +499,46 @@ pub(crate) async fn read_missing_headers<P: FinalitySyncPipeline, SC: SourceClie
|
||||
}
|
||||
|
||||
/// Read finality proofs from the stream.
|
||||
pub(crate) fn read_finality_proofs_from_stream<P: FinalitySyncPipeline, FPS: Stream<Item = P::FinalityProof>>(
|
||||
pub(crate) fn read_finality_proofs_from_stream<
|
||||
P: FinalitySyncPipeline,
|
||||
FPS: Stream<Item = P::FinalityProof>,
|
||||
>(
|
||||
finality_proofs_stream: &mut RestartableFinalityProofsStream<FPS>,
|
||||
recent_finality_proofs: &mut FinalityProofs<P>,
|
||||
) {
|
||||
let mut proofs_count = 0;
|
||||
let mut first_header_number = None;
|
||||
let mut last_header_number = None;
|
||||
loop {
|
||||
let next_proof = finality_proofs_stream.stream.next();
|
||||
let finality_proof = match next_proof.now_or_never() {
|
||||
Some(Some(finality_proof)) => finality_proof,
|
||||
Some(None) => {
|
||||
finality_proofs_stream.needs_restart = true;
|
||||
break;
|
||||
}
|
||||
break
|
||||
},
|
||||
None => break,
|
||||
};
|
||||
|
||||
recent_finality_proofs.push((finality_proof.target_header_number(), finality_proof));
|
||||
let target_header_number = finality_proof.target_header_number();
|
||||
if first_header_number.is_none() {
|
||||
first_header_number = Some(target_header_number);
|
||||
}
|
||||
last_header_number = Some(target_header_number);
|
||||
proofs_count += 1;
|
||||
|
||||
recent_finality_proofs.push((target_header_number, finality_proof));
|
||||
}
|
||||
|
||||
if proofs_count != 0 {
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"Read {} finality proofs from {} finality stream for headers in range [{:?}; {:?}]",
|
||||
proofs_count,
|
||||
P::SOURCE_NAME,
|
||||
first_header_number,
|
||||
last_header_number,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,7 +550,13 @@ pub(crate) fn select_better_recent_finality_proof<P: FinalitySyncPipeline>(
|
||||
selected_finality_proof: Option<(P::Header, P::FinalityProof)>,
|
||||
) -> Option<(P::Header, P::FinalityProof)> {
|
||||
if unjustified_headers.is_empty() || recent_finality_proofs.is_empty() {
|
||||
return selected_finality_proof;
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"Can not improve selected {} finality proof {:?}. No unjustified headers and recent proofs",
|
||||
P::SOURCE_NAME,
|
||||
selected_finality_proof.as_ref().map(|(h, _)| h.number()),
|
||||
);
|
||||
return selected_finality_proof
|
||||
}
|
||||
|
||||
const NOT_EMPTY_PROOF: &str = "we have checked that the vec is not empty; qed";
|
||||
@@ -542,9 +578,24 @@ pub(crate) fn select_better_recent_finality_proof<P: FinalitySyncPipeline>(
|
||||
let selected_finality_proof_index = recent_finality_proofs
|
||||
.binary_search_by_key(intersection.end(), |(number, _)| *number)
|
||||
.unwrap_or_else(|index| index.saturating_sub(1));
|
||||
let (selected_header_number, finality_proof) = &recent_finality_proofs[selected_finality_proof_index];
|
||||
if !intersection.contains(selected_header_number) {
|
||||
return selected_finality_proof;
|
||||
let (selected_header_number, finality_proof) =
|
||||
&recent_finality_proofs[selected_finality_proof_index];
|
||||
let has_selected_finality_proof = intersection.contains(selected_header_number);
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"Trying to improve selected {} finality proof {:?}. Headers range: [{:?}; {:?}]. Proofs range: [{:?}; {:?}].\
|
||||
Trying to improve to: {:?}. Result: {}",
|
||||
P::SOURCE_NAME,
|
||||
selected_finality_proof.as_ref().map(|(h, _)| h.number()),
|
||||
unjustified_range_begin,
|
||||
unjustified_range_end,
|
||||
buffered_range_begin,
|
||||
buffered_range_end,
|
||||
selected_header_number,
|
||||
if has_selected_finality_proof { "improved" } else { "not improved" },
|
||||
);
|
||||
if !has_selected_finality_proof {
|
||||
return selected_finality_proof
|
||||
}
|
||||
|
||||
// now remove all obsolete headers and extract selected header
|
||||
@@ -560,20 +611,15 @@ pub(crate) fn prune_recent_finality_proofs<P: FinalitySyncPipeline>(
|
||||
recent_finality_proofs: &mut FinalityProofs<P>,
|
||||
recent_finality_proofs_limit: usize,
|
||||
) {
|
||||
let position =
|
||||
recent_finality_proofs.binary_search_by_key(&justified_header_number, |(header_number, _)| *header_number);
|
||||
let position = recent_finality_proofs
|
||||
.binary_search_by_key(&justified_header_number, |(header_number, _)| *header_number);
|
||||
|
||||
// remove all obsolete elements
|
||||
*recent_finality_proofs = recent_finality_proofs.split_off(
|
||||
position
|
||||
.map(|position| position + 1)
|
||||
.unwrap_or_else(|position| position),
|
||||
);
|
||||
*recent_finality_proofs = recent_finality_proofs
|
||||
.split_off(position.map(|position| position + 1).unwrap_or_else(|position| position));
|
||||
|
||||
// now - limit vec by size
|
||||
let split_index = recent_finality_proofs
|
||||
.len()
|
||||
.saturating_sub(recent_finality_proofs_limit);
|
||||
let split_index = recent_finality_proofs.len().saturating_sub(recent_finality_proofs_limit);
|
||||
*recent_finality_proofs = recent_finality_proofs.split_off(split_index);
|
||||
}
|
||||
|
||||
@@ -585,15 +631,15 @@ fn print_sync_progress<P: FinalitySyncPipeline>(
|
||||
let (prev_time, prev_best_number_at_target) = progress_context;
|
||||
let now = Instant::now();
|
||||
|
||||
let need_update = now - prev_time > Duration::from_secs(10)
|
||||
|| prev_best_number_at_target
|
||||
let need_update = now - prev_time > Duration::from_secs(10) ||
|
||||
prev_best_number_at_target
|
||||
.map(|prev_best_number_at_target| {
|
||||
best_number_at_target.saturating_sub(prev_best_number_at_target) > 10.into()
|
||||
})
|
||||
.unwrap_or(true);
|
||||
|
||||
if !need_update {
|
||||
return (prev_time, prev_best_number_at_target);
|
||||
return (prev_time, prev_best_number_at_target)
|
||||
}
|
||||
|
||||
log::info!(
|
||||
|
||||
@@ -18,17 +18,21 @@
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use crate::finality_loop::{
|
||||
prune_recent_finality_proofs, read_finality_proofs_from_stream, run, select_better_recent_finality_proof,
|
||||
select_header_to_submit, FinalityProofs, FinalitySyncParams, RestartableFinalityProofsStream, SourceClient,
|
||||
TargetClient,
|
||||
use crate::{
|
||||
finality_loop::{
|
||||
prune_recent_finality_proofs, read_finality_proofs_from_stream, run,
|
||||
select_better_recent_finality_proof, select_header_to_submit, FinalityProofs,
|
||||
FinalitySyncParams, RestartableFinalityProofsStream, SourceClient, TargetClient,
|
||||
},
|
||||
FinalityProof, FinalitySyncPipeline, SourceHeader,
|
||||
};
|
||||
use crate::{FinalityProof, FinalitySyncPipeline, SourceHeader};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
use parking_lot::Mutex;
|
||||
use relay_utils::{metrics::MetricsParams, relay_loop::Client as RelayClient, MaybeConnectionError};
|
||||
use relay_utils::{
|
||||
metrics::MetricsParams, relay_loop::Client as RelayClient, MaybeConnectionError,
|
||||
};
|
||||
use std::{collections::HashMap, pin::Pin, sync::Arc, time::Duration};
|
||||
|
||||
type IsMandatory = bool;
|
||||
@@ -121,10 +125,7 @@ impl SourceClient<TestFinalitySyncPipeline> for TestSourceClient {
|
||||
) -> Result<(TestSourceHeader, Option<TestFinalityProof>), TestError> {
|
||||
let mut data = self.data.lock();
|
||||
(self.on_method_call)(&mut *data);
|
||||
data.source_headers
|
||||
.get(&number)
|
||||
.cloned()
|
||||
.ok_or(TestError::NonConnection)
|
||||
data.source_headers.get(&number).cloned().ok_or(TestError::NonConnection)
|
||||
}
|
||||
|
||||
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, TestError> {
|
||||
@@ -157,7 +158,11 @@ impl TargetClient<TestFinalitySyncPipeline> for TestTargetClient {
|
||||
Ok(data.target_best_block_number)
|
||||
}
|
||||
|
||||
async fn submit_finality_proof(&self, header: TestSourceHeader, proof: TestFinalityProof) -> Result<(), TestError> {
|
||||
async fn submit_finality_proof(
|
||||
&self,
|
||||
header: TestSourceHeader,
|
||||
proof: TestFinalityProof,
|
||||
) -> Result<(), TestError> {
|
||||
let mut data = self.data.lock();
|
||||
(self.on_method_call)(&mut *data);
|
||||
data.target_best_block_number = header.number();
|
||||
@@ -171,11 +176,12 @@ fn prepare_test_clients(
|
||||
state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static,
|
||||
source_headers: HashMap<TestNumber, (TestSourceHeader, Option<TestFinalityProof>)>,
|
||||
) -> (TestSourceClient, TestTargetClient) {
|
||||
let internal_state_function: Arc<dyn Fn(&mut ClientsData) + Send + Sync> = Arc::new(move |data| {
|
||||
if state_function(data) {
|
||||
exit_sender.unbounded_send(()).unwrap();
|
||||
}
|
||||
});
|
||||
let internal_state_function: Arc<dyn Fn(&mut ClientsData) + Send + Sync> =
|
||||
Arc::new(move |data| {
|
||||
if state_function(data) {
|
||||
exit_sender.unbounded_send(()).unwrap();
|
||||
}
|
||||
});
|
||||
let clients_data = Arc::new(Mutex::new(ClientsData {
|
||||
source_best_block_number: 10,
|
||||
source_headers,
|
||||
@@ -189,14 +195,13 @@ fn prepare_test_clients(
|
||||
on_method_call: internal_state_function.clone(),
|
||||
data: clients_data.clone(),
|
||||
},
|
||||
TestTargetClient {
|
||||
on_method_call: internal_state_function,
|
||||
data: clients_data,
|
||||
},
|
||||
TestTargetClient { on_method_call: internal_state_function, data: clients_data },
|
||||
)
|
||||
}
|
||||
|
||||
fn run_sync_loop(state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static) -> ClientsData {
|
||||
fn run_sync_loop(
|
||||
state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static,
|
||||
) -> ClientsData {
|
||||
let (exit_sender, exit_receiver) = futures::channel::mpsc::unbounded();
|
||||
let (source_client, target_client) = prepare_test_clients(
|
||||
exit_sender,
|
||||
@@ -234,12 +239,13 @@ fn run_sync_loop(state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync
|
||||
#[test]
|
||||
fn finality_sync_loop_works() {
|
||||
let client_data = run_sync_loop(|data| {
|
||||
// header#7 has persistent finality proof, but it isn't mandatory => it isn't submitted, because
|
||||
// header#8 has persistent finality proof && it is mandatory => it is submitted
|
||||
// header#9 has persistent finality proof, but it isn't mandatory => it is submitted, because
|
||||
// there are no more persistent finality proofs
|
||||
// header#7 has persistent finality proof, but it isn't mandatory => it isn't submitted,
|
||||
// because header#8 has persistent finality proof && it is mandatory => it is submitted
|
||||
// header#9 has persistent finality proof, but it isn't mandatory => it is submitted,
|
||||
// because there are no more persistent finality proofs
|
||||
//
|
||||
// once this ^^^ is done, we generate more blocks && read proof for blocks 12 and 14 from the stream
|
||||
// once this ^^^ is done, we generate more blocks && read proof for blocks 12 and 14 from
|
||||
// the stream
|
||||
if data.target_best_block_number == 9 {
|
||||
data.source_best_block_number = 14;
|
||||
data.source_headers.insert(11, (TestSourceHeader(false, 11), None));
|
||||
@@ -287,10 +293,7 @@ fn run_only_mandatory_headers_mode_test(
|
||||
vec![
|
||||
(6, (TestSourceHeader(false, 6), Some(TestFinalityProof(6)))),
|
||||
(7, (TestSourceHeader(false, 7), Some(TestFinalityProof(7)))),
|
||||
(
|
||||
8,
|
||||
(TestSourceHeader(has_mandatory_headers, 8), Some(TestFinalityProof(8))),
|
||||
),
|
||||
(8, (TestSourceHeader(has_mandatory_headers, 8), Some(TestFinalityProof(8)))),
|
||||
(9, (TestSourceHeader(false, 9), Some(TestFinalityProof(9)))),
|
||||
(10, (TestSourceHeader(false, 10), Some(TestFinalityProof(10)))),
|
||||
]
|
||||
@@ -357,7 +360,8 @@ fn select_better_recent_finality_proof_works() {
|
||||
Some((TestSourceHeader(false, 2), TestFinalityProof(2))),
|
||||
);
|
||||
|
||||
// if there's no intersection between recent finality proofs and unjustified headers, nothing is changed
|
||||
// if there's no intersection between recent finality proofs and unjustified headers, nothing is
|
||||
// changed
|
||||
let mut unjustified_headers = vec![TestSourceHeader(false, 9), TestSourceHeader(false, 10)];
|
||||
assert_eq!(
|
||||
select_better_recent_finality_proof::<TestFinalitySyncPipeline>(
|
||||
@@ -368,13 +372,10 @@ fn select_better_recent_finality_proof_works() {
|
||||
Some((TestSourceHeader(false, 2), TestFinalityProof(2))),
|
||||
);
|
||||
|
||||
// if there's intersection between recent finality proofs and unjustified headers, but there are no
|
||||
// proofs in this intersection, nothing is changed
|
||||
let mut unjustified_headers = vec![
|
||||
TestSourceHeader(false, 8),
|
||||
TestSourceHeader(false, 9),
|
||||
TestSourceHeader(false, 10),
|
||||
];
|
||||
// if there's intersection between recent finality proofs and unjustified headers, but there are
|
||||
// no proofs in this intersection, nothing is changed
|
||||
let mut unjustified_headers =
|
||||
vec![TestSourceHeader(false, 8), TestSourceHeader(false, 9), TestSourceHeader(false, 10)];
|
||||
assert_eq!(
|
||||
select_better_recent_finality_proof::<TestFinalitySyncPipeline>(
|
||||
&[(7, TestFinalityProof(7)), (11, TestFinalityProof(11))],
|
||||
@@ -385,22 +386,15 @@ fn select_better_recent_finality_proof_works() {
|
||||
);
|
||||
assert_eq!(
|
||||
unjustified_headers,
|
||||
vec![
|
||||
TestSourceHeader(false, 8),
|
||||
TestSourceHeader(false, 9),
|
||||
TestSourceHeader(false, 10)
|
||||
]
|
||||
vec![TestSourceHeader(false, 8), TestSourceHeader(false, 9), TestSourceHeader(false, 10)]
|
||||
);
|
||||
|
||||
// if there's intersection between recent finality proofs and unjustified headers and there's
|
||||
// a proof in this intersection:
|
||||
// - this better (last from intersection) proof is selected;
|
||||
// - 'obsolete' unjustified headers are pruned.
|
||||
let mut unjustified_headers = vec![
|
||||
TestSourceHeader(false, 8),
|
||||
TestSourceHeader(false, 9),
|
||||
TestSourceHeader(false, 10),
|
||||
];
|
||||
let mut unjustified_headers =
|
||||
vec![TestSourceHeader(false, 8), TestSourceHeader(false, 9), TestSourceHeader(false, 10)];
|
||||
assert_eq!(
|
||||
select_better_recent_finality_proof::<TestFinalitySyncPipeline>(
|
||||
&[(7, TestFinalityProof(7)), (9, TestFinalityProof(9))],
|
||||
@@ -416,7 +410,10 @@ fn read_finality_proofs_from_stream_works() {
|
||||
// when stream is currently empty, nothing is changed
|
||||
let mut recent_finality_proofs = vec![(1, TestFinalityProof(1))];
|
||||
let mut stream = futures::stream::pending().into();
|
||||
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(&mut stream, &mut recent_finality_proofs);
|
||||
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(
|
||||
&mut stream,
|
||||
&mut recent_finality_proofs,
|
||||
);
|
||||
assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1))]);
|
||||
assert!(!stream.needs_restart);
|
||||
|
||||
@@ -424,20 +421,20 @@ fn read_finality_proofs_from_stream_works() {
|
||||
let mut stream = futures::stream::iter(vec![TestFinalityProof(4)])
|
||||
.chain(futures::stream::pending())
|
||||
.into();
|
||||
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(&mut stream, &mut recent_finality_proofs);
|
||||
assert_eq!(
|
||||
recent_finality_proofs,
|
||||
vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]
|
||||
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(
|
||||
&mut stream,
|
||||
&mut recent_finality_proofs,
|
||||
);
|
||||
assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]);
|
||||
assert!(!stream.needs_restart);
|
||||
|
||||
// when stream has ended, we'll need to restart it
|
||||
let mut stream = futures::stream::empty().into();
|
||||
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(&mut stream, &mut recent_finality_proofs);
|
||||
assert_eq!(
|
||||
recent_finality_proofs,
|
||||
vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]
|
||||
read_finality_proofs_from_stream::<TestFinalitySyncPipeline, _>(
|
||||
&mut stream,
|
||||
&mut recent_finality_proofs,
|
||||
);
|
||||
assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]);
|
||||
assert!(stream.needs_restart);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
//! are still submitted to the target node, but are treated as auxiliary data as we are not trying
|
||||
//! to submit all source headers to the target node.
|
||||
|
||||
pub use crate::finality_loop::{metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient};
|
||||
pub use crate::finality_loop::{
|
||||
metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient,
|
||||
};
|
||||
|
||||
use bp_header_chain::FinalityProof;
|
||||
use std::fmt::Debug;
|
||||
|
||||
Reference in New Issue
Block a user