mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 11:01:01 +00:00
Backport: Implement basic equivocations detection loop (#2375)
* Implement basic equivocations detection loop (#2367) * FinalityProofsBuf adjustments - store a Vec<FinalityProof> - transform prune `buf_limit` to Option * FinalityProof: add target_header_hash() * Target client: implement best_synced_header_hash() * Implement first version of the equivocations detection loop * Address code review comments * Leftover * polkadot-staging adjustments
This commit is contained in:
committed by
Bastian Köcher
parent
cc3bbc690b
commit
588508acd4
@@ -32,7 +32,7 @@ pub trait FinalityPipeline: 'static + Clone + Debug + Send + Sync {
|
||||
/// Synced headers are identified by this number.
|
||||
type Number: relay_utils::BlockNumberBase;
|
||||
/// Finality proof type.
|
||||
type FinalityProof: FinalityProof<Self::Number>;
|
||||
type FinalityProof: FinalityProof<Self::Hash, Self::Number>;
|
||||
}
|
||||
|
||||
/// Source client used in finality related loops.
|
||||
|
||||
@@ -319,8 +319,10 @@ impl<P: FinalitySyncPipeline, SC: SourceClient<P>, TC: TargetClient<P>> Finality
|
||||
.as_ref()
|
||||
.map(|justified_header| justified_header.number())
|
||||
.unwrap_or(info.best_number_at_target);
|
||||
self.finality_proofs_buf
|
||||
.prune(oldest_finality_proof_to_keep, self.sync_params.recent_finality_proofs_limit);
|
||||
self.finality_proofs_buf.prune(
|
||||
oldest_finality_proof_to_keep,
|
||||
Some(self.sync_params.recent_finality_proofs_limit),
|
||||
);
|
||||
|
||||
Ok(maybe_justified_header)
|
||||
}
|
||||
|
||||
@@ -20,11 +20,8 @@ use bp_header_chain::FinalityProof;
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
use std::pin::Pin;
|
||||
|
||||
/// Finality proofs container. Ordered by target header number.
|
||||
pub type FinalityProofs<P> =
|
||||
Vec<(<P as FinalityPipeline>::Number, <P as FinalityPipeline>::FinalityProof)>;
|
||||
|
||||
/// Source finality proofs stream that may be restarted.
|
||||
#[derive(Default)]
|
||||
pub struct FinalityProofsStream<P: FinalityPipeline, SC: SourceClientBase<P>> {
|
||||
/// The underlying stream.
|
||||
stream: Option<Pin<Box<SC::FinalityProofsStream>>>,
|
||||
@@ -75,16 +72,16 @@ impl<P: FinalityPipeline, SC: SourceClientBase<P>> FinalityProofsStream<P, SC> {
|
||||
|
||||
/// Source finality proofs buffer.
|
||||
pub struct FinalityProofsBuf<P: FinalityPipeline> {
|
||||
/// Proofs buffer.
|
||||
buf: FinalityProofs<P>,
|
||||
/// Proofs buffer. Ordered by target header number.
|
||||
buf: Vec<P::FinalityProof>,
|
||||
}
|
||||
|
||||
impl<P: FinalityPipeline> FinalityProofsBuf<P> {
|
||||
pub fn new(buf: FinalityProofs<P>) -> Self {
|
||||
pub fn new(buf: Vec<P::FinalityProof>) -> Self {
|
||||
Self { buf }
|
||||
}
|
||||
|
||||
pub fn buf(&self) -> &FinalityProofs<P> {
|
||||
pub fn buf(&self) -> &Vec<P::FinalityProof> {
|
||||
&self.buf
|
||||
}
|
||||
|
||||
@@ -98,7 +95,7 @@ impl<P: FinalityPipeline> FinalityProofsBuf<P> {
|
||||
last_header_number = Some(target_header_number);
|
||||
proofs_count += 1;
|
||||
|
||||
self.buf.push((target_header_number, finality_proof));
|
||||
self.buf.push(finality_proof);
|
||||
}
|
||||
|
||||
if proofs_count != 0 {
|
||||
@@ -113,15 +110,19 @@ impl<P: FinalityPipeline> FinalityProofsBuf<P> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prune(&mut self, until_hdr_num: P::Number, buf_limit: usize) {
|
||||
let kept_hdr_idx = self
|
||||
/// Prune all finality proofs that target header numbers older than `first_to_keep`.
|
||||
pub fn prune(&mut self, first_to_keep: P::Number, maybe_buf_limit: Option<usize>) {
|
||||
let first_to_keep_idx = self
|
||||
.buf
|
||||
.binary_search_by_key(&until_hdr_num, |(hdr_num, _)| *hdr_num)
|
||||
.binary_search_by_key(&first_to_keep, |hdr| hdr.target_header_number())
|
||||
.map(|idx| idx + 1)
|
||||
.unwrap_or_else(|idx| idx);
|
||||
let buf_limit_idx = self.buf.len().saturating_sub(buf_limit);
|
||||
let buf_limit_idx = match maybe_buf_limit {
|
||||
Some(buf_limit) => self.buf.len().saturating_sub(buf_limit),
|
||||
None => 0,
|
||||
};
|
||||
|
||||
self.buf = self.buf.split_off(std::cmp::max(kept_hdr_idx, buf_limit_idx));
|
||||
self.buf = self.buf.split_off(std::cmp::max(first_to_keep_idx, buf_limit_idx));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,13 +141,13 @@ mod tests {
|
||||
fn finality_proofs_buf_fill_works() {
|
||||
// when stream is currently empty, nothing is changed
|
||||
let mut finality_proofs_buf =
|
||||
FinalityProofsBuf::<TestFinalitySyncPipeline> { buf: vec![(1, TestFinalityProof(1))] };
|
||||
FinalityProofsBuf::<TestFinalitySyncPipeline> { buf: vec![TestFinalityProof(1)] };
|
||||
let mut stream =
|
||||
FinalityProofsStream::<TestFinalitySyncPipeline, TestSourceClient>::from_stream(
|
||||
Box::pin(futures::stream::pending()),
|
||||
);
|
||||
finality_proofs_buf.fill(&mut stream);
|
||||
assert_eq!(finality_proofs_buf.buf, vec![(1, TestFinalityProof(1))]);
|
||||
assert_eq!(finality_proofs_buf.buf, vec![TestFinalityProof(1)]);
|
||||
assert!(stream.stream.is_some());
|
||||
|
||||
// when stream has entry with target, it is added to the recent proofs container
|
||||
@@ -158,10 +159,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
finality_proofs_buf.fill(&mut stream);
|
||||
assert_eq!(
|
||||
finality_proofs_buf.buf,
|
||||
vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]
|
||||
);
|
||||
assert_eq!(finality_proofs_buf.buf, vec![TestFinalityProof(1), TestFinalityProof(4)]);
|
||||
assert!(stream.stream.is_some());
|
||||
|
||||
// when stream has ended, we'll need to restart it
|
||||
@@ -170,21 +168,20 @@ mod tests {
|
||||
Box::pin(futures::stream::empty()),
|
||||
);
|
||||
finality_proofs_buf.fill(&mut stream);
|
||||
assert_eq!(
|
||||
finality_proofs_buf.buf,
|
||||
vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]
|
||||
);
|
||||
assert_eq!(finality_proofs_buf.buf, vec![TestFinalityProof(1), TestFinalityProof(4)]);
|
||||
assert!(stream.stream.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finality_proofs_buf_prune_works() {
|
||||
let original_finality_proofs_buf: FinalityProofs<TestFinalitySyncPipeline> = vec![
|
||||
(10, TestFinalityProof(10)),
|
||||
(13, TestFinalityProof(13)),
|
||||
(15, TestFinalityProof(15)),
|
||||
(17, TestFinalityProof(17)),
|
||||
(19, TestFinalityProof(19)),
|
||||
let original_finality_proofs_buf: Vec<
|
||||
<TestFinalitySyncPipeline as FinalityPipeline>::FinalityProof,
|
||||
> = vec![
|
||||
TestFinalityProof(10),
|
||||
TestFinalityProof(13),
|
||||
TestFinalityProof(15),
|
||||
TestFinalityProof(17),
|
||||
TestFinalityProof(19),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
@@ -193,35 +190,35 @@ mod tests {
|
||||
let mut finality_proofs_buf = FinalityProofsBuf::<TestFinalitySyncPipeline> {
|
||||
buf: original_finality_proofs_buf.clone(),
|
||||
};
|
||||
finality_proofs_buf.prune(10, 1024);
|
||||
finality_proofs_buf.prune(10, None);
|
||||
assert_eq!(&original_finality_proofs_buf[1..], finality_proofs_buf.buf,);
|
||||
|
||||
// when there are no proof for justified header in the vec
|
||||
let mut finality_proofs_buf = FinalityProofsBuf::<TestFinalitySyncPipeline> {
|
||||
buf: original_finality_proofs_buf.clone(),
|
||||
};
|
||||
finality_proofs_buf.prune(11, 1024);
|
||||
finality_proofs_buf.prune(11, None);
|
||||
assert_eq!(&original_finality_proofs_buf[1..], finality_proofs_buf.buf,);
|
||||
|
||||
// when there are too many entries after initial prune && they also need to be pruned
|
||||
let mut finality_proofs_buf = FinalityProofsBuf::<TestFinalitySyncPipeline> {
|
||||
buf: original_finality_proofs_buf.clone(),
|
||||
};
|
||||
finality_proofs_buf.prune(10, 2);
|
||||
finality_proofs_buf.prune(10, Some(2));
|
||||
assert_eq!(&original_finality_proofs_buf[3..], finality_proofs_buf.buf,);
|
||||
|
||||
// when last entry is pruned
|
||||
let mut finality_proofs_buf = FinalityProofsBuf::<TestFinalitySyncPipeline> {
|
||||
buf: original_finality_proofs_buf.clone(),
|
||||
};
|
||||
finality_proofs_buf.prune(19, 2);
|
||||
finality_proofs_buf.prune(19, Some(2));
|
||||
assert_eq!(&original_finality_proofs_buf[5..], finality_proofs_buf.buf,);
|
||||
|
||||
// when post-last entry is pruned
|
||||
let mut finality_proofs_buf = FinalityProofsBuf::<TestFinalitySyncPipeline> {
|
||||
buf: original_finality_proofs_buf.clone(),
|
||||
};
|
||||
finality_proofs_buf.prune(20, 2);
|
||||
finality_proofs_buf.prune(20, Some(2));
|
||||
assert_eq!(&original_finality_proofs_buf[5..], finality_proofs_buf.buf,);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::{
|
||||
SourceClient, SourceHeader, TargetClient,
|
||||
};
|
||||
|
||||
use bp_header_chain::FinalityProof;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// Unjustified headers container. Ordered by header number.
|
||||
@@ -120,18 +121,18 @@ impl<P: FinalitySyncPipeline> JustifiedHeaderSelector<P> {
|
||||
while let (Some(finality_proof), Some(unjustified_header)) =
|
||||
(maybe_finality_proof, maybe_unjustified_header)
|
||||
{
|
||||
match finality_proof.0.cmp(&unjustified_header.number()) {
|
||||
match finality_proof.target_header_number().cmp(&unjustified_header.number()) {
|
||||
Ordering::Equal => {
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"Managed to improve selected {} finality proof {:?} to {:?}.",
|
||||
P::SOURCE_NAME,
|
||||
maybe_justified_header.as_ref().map(|justified_header| justified_header.number()),
|
||||
finality_proof.0
|
||||
finality_proof.target_header_number()
|
||||
);
|
||||
return Some(JustifiedHeader {
|
||||
header: unjustified_header.clone(),
|
||||
proof: finality_proof.1.clone(),
|
||||
proof: finality_proof.clone(),
|
||||
})
|
||||
},
|
||||
Ordering::Less => maybe_unjustified_header = unjustified_headers_iter.next(),
|
||||
@@ -160,7 +161,7 @@ mod tests {
|
||||
fn select_better_recent_finality_proof_works() {
|
||||
// if there are no unjustified headers, nothing is changed
|
||||
let finality_proofs_buf =
|
||||
FinalityProofsBuf::<TestFinalitySyncPipeline>::new(vec![(5, TestFinalityProof(5))]);
|
||||
FinalityProofsBuf::<TestFinalitySyncPipeline>::new(vec![TestFinalityProof(5)]);
|
||||
let justified_header =
|
||||
JustifiedHeader { header: TestSourceHeader(false, 2, 2), proof: TestFinalityProof(2) };
|
||||
let selector = JustifiedHeaderSelector::Regular(vec![], justified_header.clone());
|
||||
@@ -179,8 +180,8 @@ mod tests {
|
||||
// if there's no intersection between recent finality proofs and unjustified headers,
|
||||
// nothing is changed
|
||||
let finality_proofs_buf = FinalityProofsBuf::<TestFinalitySyncPipeline>::new(vec![
|
||||
(1, TestFinalityProof(1)),
|
||||
(4, TestFinalityProof(4)),
|
||||
TestFinalityProof(1),
|
||||
TestFinalityProof(4),
|
||||
]);
|
||||
let justified_header =
|
||||
JustifiedHeader { header: TestSourceHeader(false, 2, 2), proof: TestFinalityProof(2) };
|
||||
@@ -193,8 +194,8 @@ mod tests {
|
||||
// if there's intersection between recent finality proofs and unjustified headers, but there
|
||||
// are no proofs in this intersection, nothing is changed
|
||||
let finality_proofs_buf = FinalityProofsBuf::<TestFinalitySyncPipeline>::new(vec![
|
||||
(7, TestFinalityProof(7)),
|
||||
(11, TestFinalityProof(11)),
|
||||
TestFinalityProof(7),
|
||||
TestFinalityProof(11),
|
||||
]);
|
||||
let justified_header =
|
||||
JustifiedHeader { header: TestSourceHeader(false, 2, 2), proof: TestFinalityProof(2) };
|
||||
@@ -213,8 +214,8 @@ mod tests {
|
||||
// - this better (last from intersection) proof is selected;
|
||||
// - 'obsolete' unjustified headers are pruned.
|
||||
let finality_proofs_buf = FinalityProofsBuf::<TestFinalitySyncPipeline>::new(vec![
|
||||
(7, TestFinalityProof(7)),
|
||||
(9, TestFinalityProof(9)),
|
||||
TestFinalityProof(7),
|
||||
TestFinalityProof(9),
|
||||
]);
|
||||
let justified_header =
|
||||
JustifiedHeader { header: TestSourceHeader(false, 2, 2), proof: TestFinalityProof(2) };
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
pub use crate::{
|
||||
base::{FinalityPipeline, SourceClientBase},
|
||||
finality_loop::{metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient},
|
||||
finality_proofs::{FinalityProofsBuf, FinalityProofsStream},
|
||||
sync_loop_metrics::SyncLoopMetrics,
|
||||
};
|
||||
|
||||
|
||||
@@ -106,7 +106,11 @@ impl SourceHeader<TestHash, TestNumber, GrandpaConsensusLogReader<TestNumber>>
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TestFinalityProof(pub TestNumber);
|
||||
|
||||
impl FinalityProof<TestNumber> for TestFinalityProof {
|
||||
impl FinalityProof<TestHash, TestNumber> for TestFinalityProof {
|
||||
fn target_header_hash(&self) -> TestHash {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn target_header_number(&self) -> TestNumber {
|
||||
self.0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user