// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see .
//! Tests for finality synchronization loop.
#![cfg(test)]
use crate::{
base::SourceClientBase,
finality_loop::{SourceClient, TargetClient},
FinalityPipeline, FinalitySyncPipeline, SourceHeader,
};
use async_trait::async_trait;
use bp_header_chain::{FinalityProof, GrandpaConsensusLogReader};
use futures::{Stream, StreamExt};
use parking_lot::Mutex;
use relay_utils::{
relay_loop::Client as RelayClient, HeaderId, MaybeConnectionError, TrackedTransactionStatus,
TransactionTracker,
};
use std::{collections::HashMap, pin::Pin, sync::Arc};
type IsMandatory = bool;
pub type TestNumber = u64;
type TestHash = u64;
#[derive(Clone, Debug)]
pub struct TestTransactionTracker(pub TrackedTransactionStatus>);
impl Default for TestTransactionTracker {
fn default() -> TestTransactionTracker {
TestTransactionTracker(TrackedTransactionStatus::Finalized(Default::default()))
}
}
#[async_trait]
impl TransactionTracker for TestTransactionTracker {
type HeaderId = HeaderId;
async fn wait(self) -> TrackedTransactionStatus> {
self.0
}
}
#[derive(Debug, Clone)]
pub enum TestError {
NonConnection,
}
impl MaybeConnectionError for TestError {
fn is_connection_error(&self) -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TestFinalitySyncPipeline;
impl FinalityPipeline for TestFinalitySyncPipeline {
const SOURCE_NAME: &'static str = "TestSource";
const TARGET_NAME: &'static str = "TestTarget";
type Hash = TestHash;
type Number = TestNumber;
type FinalityProof = TestFinalityProof;
}
impl FinalitySyncPipeline for TestFinalitySyncPipeline {
type ConsensusLogReader = GrandpaConsensusLogReader;
type Header = TestSourceHeader;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TestSourceHeader(pub IsMandatory, pub TestNumber, pub TestHash);
impl SourceHeader>
for TestSourceHeader
{
fn hash(&self) -> TestHash {
self.2
}
fn number(&self) -> TestNumber {
self.1
}
fn is_mandatory(&self) -> bool {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TestFinalityProof(pub TestNumber);
impl FinalityProof for TestFinalityProof {
fn target_header_number(&self) -> TestNumber {
self.0
}
}
#[derive(Debug, Clone, Default)]
pub struct ClientsData {
pub source_best_block_number: TestNumber,
pub source_headers: HashMap)>,
pub source_proofs: Vec,
pub target_best_block_id: HeaderId,
pub target_headers: Vec<(TestSourceHeader, TestFinalityProof)>,
pub target_transaction_tracker: TestTransactionTracker,
}
#[derive(Clone)]
pub struct TestSourceClient {
pub on_method_call: Arc,
pub data: Arc>,
}
#[async_trait]
impl RelayClient for TestSourceClient {
type Error = TestError;
async fn reconnect(&mut self) -> Result<(), TestError> {
unreachable!()
}
}
#[async_trait]
impl SourceClientBase for TestSourceClient {
type FinalityProofsStream = Pin + 'static + Send>>;
async fn finality_proofs(&self) -> Result {
let mut data = self.data.lock();
(self.on_method_call)(&mut data);
Ok(futures::stream::iter(data.source_proofs.clone()).boxed())
}
}
#[async_trait]
impl SourceClient for TestSourceClient {
async fn best_finalized_block_number(&self) -> Result {
let mut data = self.data.lock();
(self.on_method_call)(&mut data);
Ok(data.source_best_block_number)
}
async fn header_and_finality_proof(
&self,
number: TestNumber,
) -> Result<(TestSourceHeader, Option), TestError> {
let mut data = self.data.lock();
(self.on_method_call)(&mut data);
data.source_headers.get(&number).cloned().ok_or(TestError::NonConnection)
}
}
#[derive(Clone)]
pub struct TestTargetClient {
pub on_method_call: Arc,
pub data: Arc>,
}
#[async_trait]
impl RelayClient for TestTargetClient {
type Error = TestError;
async fn reconnect(&mut self) -> Result<(), TestError> {
unreachable!()
}
}
#[async_trait]
impl TargetClient for TestTargetClient {
type TransactionTracker = TestTransactionTracker;
async fn best_finalized_source_block_id(
&self,
) -> Result, TestError> {
let mut data = self.data.lock();
(self.on_method_call)(&mut data);
Ok(data.target_best_block_id)
}
async fn submit_finality_proof(
&self,
header: TestSourceHeader,
proof: TestFinalityProof,
) -> Result {
let mut data = self.data.lock();
(self.on_method_call)(&mut data);
data.target_best_block_id = HeaderId(header.number(), header.hash());
data.target_headers.push((header, proof));
(self.on_method_call)(&mut data);
Ok(data.target_transaction_tracker.clone())
}
}