// 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_hash(&self) -> TestHash { Default::default() } 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 free_source_headers_interval(&self) -> Result, TestError> { Ok(Some(3)) } async fn submit_finality_proof( &self, header: TestSourceHeader, proof: TestFinalityProof, _is_free_execution_expected: bool, ) -> 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()) } }