diff --git a/Cargo.lock b/Cargo.lock index e44f271687..8179605794 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1943,7 +1943,7 @@ dependencies = [ [[package]] name = "finality-aleph" -version = "0.5.2" +version = "0.5.3" dependencies = [ "aggregator 0.1.0", "aggregator 0.2.0", diff --git a/bin/node/src/service.rs b/bin/node/src/service.rs index 53cbec030e..561899ef35 100644 --- a/bin/node/src/service.rs +++ b/bin/node/src/service.rs @@ -13,6 +13,7 @@ use finality_aleph::{ }; use futures::channel::mpsc; use log::warn; +use sc_client_api::{Backend, HeaderBackend}; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; use sc_network::NetworkService; use sc_service::{ @@ -21,6 +22,7 @@ use sc_service::{ }; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_api::ProvideRuntimeApi; +use sp_blockchain::Backend as _; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use sp_runtime::{ generic::BlockId, @@ -33,7 +35,7 @@ type FullClient = sc_service::TFullClient; type FullBackend = sc_service::TFullBackend; type FullSelectChain = sc_consensus::LongestChain; -fn get_backup_path(aleph_config: &AlephCli, base_path: &Path) -> Option { +fn backup_path(aleph_config: &AlephCli, base_path: &Path) -> Option { if aleph_config.no_backup() { return None; } @@ -253,7 +255,7 @@ pub fn new_authority( other: (block_import, justification_tx, justification_rx, mut telemetry, metrics), } = new_partial(&config)?; - let backup_path = get_backup_path( + let backup_path = backup_path( &aleph_config, config .base_path @@ -282,7 +284,7 @@ pub fn new_authority( let (_rpc_handlers, network, network_starter) = setup( config, - backend, + backend.clone(), &keystore_container, import_queue, transaction_pool.clone(), @@ -339,9 +341,11 @@ pub fn new_authority( if aleph_config.external_addresses().is_empty() { panic!("Cannot run a validator node without external addresses, stopping."); } + let blockchain_backend = BlockchainBackendImpl { backend }; let aleph_config = AlephConfig { network, client, + blockchain_backend, select_chain, session_period, millisecs_per_block, @@ -379,7 +383,7 @@ pub fn new_full( other: (_, justification_tx, justification_rx, mut telemetry, metrics), } = new_partial(&config)?; - let backup_path = get_backup_path( + let backup_path = backup_path( &aleph_config, config .base_path @@ -390,7 +394,7 @@ pub fn new_full( let (_rpc_handlers, network, network_starter) = setup( config, - backend, + backend.clone(), &keystore_container, import_queue, transaction_pool, @@ -414,9 +418,11 @@ pub fn new_full( .unwrap(), ); + let blockchain_backend = BlockchainBackendImpl { backend }; let aleph_config = AlephConfig { network, client, + blockchain_backend, select_chain, session_period, millisecs_per_block, @@ -439,3 +445,24 @@ pub fn new_full( network_starter.start_network(); Ok(task_manager) } + +struct BlockchainBackendImpl { + backend: Arc, +} +impl finality_aleph::BlockchainBackend for BlockchainBackendImpl { + fn children(&self, parent_hash: ::Hash) -> Vec<::Hash> { + self.backend + .blockchain() + .children(parent_hash) + .unwrap_or_default() + } + fn info(&self) -> sp_blockchain::Info { + self.backend.blockchain().info() + } + fn header( + &self, + block_id: sp_api::BlockId, + ) -> sp_blockchain::Result::Header>> { + self.backend.blockchain().header(block_id) + } +} diff --git a/finality-aleph/Cargo.toml b/finality-aleph/Cargo.toml index 89b014f34c..cb59ffb654 100644 --- a/finality-aleph/Cargo.toml +++ b/finality-aleph/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "finality-aleph" -version = "0.5.2" +version = "0.5.3" authors = ["Cardinal Cryptography"] edition = "2021" license = "Apache 2.0" diff --git a/finality-aleph/src/justification/handler.rs b/finality-aleph/src/justification/handler.rs index cf2b752549..2c038fa0e2 100644 --- a/finality-aleph/src/justification/handler.rs +++ b/finality-aleph/src/justification/handler.rs @@ -1,12 +1,8 @@ -use std::{ - sync::Arc, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use futures::{channel::mpsc, Stream, StreamExt}; use futures_timer::Delay; use log::{debug, error}; -use sc_client_api::HeaderBackend; use sp_api::BlockT; use sp_runtime::traits::Header; use tokio::time::timeout; @@ -17,53 +13,52 @@ use crate::{ requester::BlockRequester, JustificationHandlerConfig, JustificationNotification, JustificationRequestScheduler, SessionInfo, SessionInfoProvider, Verifier, }, - network, Metrics, STATUS_REPORT_INTERVAL, + network, BlockchainBackend, Metrics, STATUS_REPORT_INTERVAL, }; -pub struct JustificationHandler +pub struct JustificationHandler where B: BlockT, V: Verifier, RB: network::RequestBlocks + 'static, - C: HeaderBackend + Send + Sync + 'static, S: JustificationRequestScheduler, SI: SessionInfoProvider, F: BlockFinalizer, + BB: BlockchainBackend + 'static, { session_info_provider: SI, - block_requester: BlockRequester, + block_requester: BlockRequester, verifier_timeout: Duration, notification_timeout: Duration, } -impl JustificationHandler +impl JustificationHandler where B: BlockT, V: Verifier, RB: network::RequestBlocks + 'static, - C: HeaderBackend + Send + Sync + 'static, S: JustificationRequestScheduler, SI: SessionInfoProvider, F: BlockFinalizer, + BB: BlockchainBackend + 'static, { pub fn new( session_info_provider: SI, block_requester: RB, - client: Arc, + blockchain_backend: BB, finalizer: F, justification_request_scheduler: S, metrics: Option::Hash>>, - justification_handler_config: JustificationHandlerConfig, + justification_handler_config: JustificationHandlerConfig, ) -> Self { Self { session_info_provider, block_requester: BlockRequester::new( block_requester, - client, + blockchain_backend, finalizer, justification_request_scheduler, metrics, - justification_handler_config.min_allowed_delay, ), verifier_timeout: justification_handler_config.verifier_timeout, notification_timeout: justification_handler_config.notification_timeout, diff --git a/finality-aleph/src/justification/mod.rs b/finality-aleph/src/justification/mod.rs index fcc16bf9b5..f5cde9d497 100644 --- a/finality-aleph/src/justification/mod.rs +++ b/finality-aleph/src/justification/mod.rs @@ -55,36 +55,29 @@ pub struct JustificationNotification { } #[derive(Clone)] -pub struct JustificationHandlerConfig { +pub struct JustificationHandlerConfig { /// How long should we wait when the session verifier is not yet available. verifier_timeout: Duration, /// How long should we wait for any notification. notification_timeout: Duration, - ///Distance (in amount of blocks) between the best and the block we want to request justification - min_allowed_delay: NumberFor, } -impl Default for JustificationHandlerConfig { +impl Default for JustificationHandlerConfig { fn default() -> Self { Self { verifier_timeout: Duration::from_millis(500), - notification_timeout: Duration::from_millis(1000), - min_allowed_delay: 3u32.into(), + // request justifications slightly more frequently than they're created + notification_timeout: Duration::from_millis(800), } } } #[cfg(test)] -impl JustificationHandlerConfig { - pub fn new( - verifier_timeout: Duration, - notification_timeout: Duration, - min_allowed_delay: NumberFor, - ) -> Self { +impl JustificationHandlerConfig { + pub fn new(verifier_timeout: Duration, notification_timeout: Duration) -> Self { Self { verifier_timeout, notification_timeout, - min_allowed_delay, } } } diff --git a/finality-aleph/src/justification/requester.rs b/finality-aleph/src/justification/requester.rs index 1e140f8c8d..1bc4595df9 100644 --- a/finality-aleph/src/justification/requester.rs +++ b/finality-aleph/src/justification/requester.rs @@ -1,10 +1,10 @@ -use std::{fmt, marker::PhantomData, sync::Arc, time::Instant}; +use std::{fmt, marker::PhantomData, time::Instant}; use aleph_primitives::ALEPH_ENGINE_ID; use log::{debug, error, info, warn}; -use sc_client_api::HeaderBackend; +use sc_client_api::blockchain::Info; use sp_api::{BlockId, BlockT, NumberFor}; -use sp_runtime::traits::Header; +use sp_runtime::traits::{Header, One, Saturating}; use crate::{ finalization::BlockFinalizer, @@ -13,7 +13,7 @@ use crate::{ JustificationRequestScheduler, Verifier, }, metrics::Checkpoint, - network, Metrics, + network, BlockHashNum, BlockchainBackend, Metrics, }; /// Threshold for how many tries are needed so that JustificationRequestStatus is logged @@ -21,100 +21,114 @@ const REPORT_THRESHOLD: u32 = 2; /// This structure is created for keeping and reporting status of BlockRequester pub struct JustificationRequestStatus { - block_number: Option>, - block_hash: Option, - tries: u32, + block_hash_number: Option>, + block_tries: u32, + parent: Option, + n_children: usize, + children_tries: u32, report_threshold: u32, } impl JustificationRequestStatus { fn new() -> Self { Self { - block_number: None, - block_hash: None, - tries: 0, + block_hash_number: None, + block_tries: 0, + parent: None, + n_children: 0, + children_tries: 0, report_threshold: REPORT_THRESHOLD, } } - fn save_block_number(&mut self, num: NumberFor) { - if Some(num) == self.block_number { - self.tries += 1; + fn save_children(&mut self, hash: B::Hash, n_children: usize) { + if self.parent == Some(hash) { + self.children_tries += 1; } else { - self.block_number = Some(num); - self.block_hash = None; - self.tries = 1; + self.parent = Some(hash); + self.children_tries = 1; } + self.n_children = n_children; } - fn insert_hash(&mut self, hash: B::Hash) { - self.block_hash = Some(hash); + fn save_block(&mut self, hn: BlockHashNum) { + if self.block_hash_number == Some(hn.clone()) { + self.block_tries += 1; + } else { + self.block_hash_number = Some(hn); + self.block_tries = 1; + } } fn should_report(&self) -> bool { - self.tries >= self.report_threshold + self.block_tries >= self.report_threshold || self.children_tries >= self.report_threshold } } impl fmt::Display for JustificationRequestStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(n) = self.block_number { - let mut status = format!("tries - {}; requested block number - {}; ", self.tries, n); - if let Some(header) = self.block_hash { - status.push_str(&format!("hash - {}; ", header)); - } else { - status.push_str("hash - unknown; "); + if self.block_tries >= self.report_threshold { + if let Some(hn) = &self.block_hash_number { + write!( + f, + "tries - {}; requested block number - {}; hash - {};", + self.block_tries, hn.num, hn.hash, + )?; + } + } + if self.children_tries >= self.report_threshold { + if let Some(parent) = self.parent { + write!( + f, + "tries - {}; requested {} children of finalized block {}; ", + self.children_tries, self.n_children, parent + )?; } - - write!(f, "{}", status)?; } Ok(()) } } -pub struct BlockRequester +pub struct BlockRequester where B: BlockT, RB: network::RequestBlocks + 'static, - C: HeaderBackend + Send + Sync + 'static, S: JustificationRequestScheduler, F: BlockFinalizer, V: Verifier, + BB: BlockchainBackend + 'static, { block_requester: RB, - client: Arc, + blockchain_backend: BB, finalizer: F, justification_request_scheduler: S, metrics: Option::Hash>>, - min_allowed_delay: NumberFor, request_status: JustificationRequestStatus, _phantom: PhantomData, } -impl BlockRequester +impl BlockRequester where B: BlockT, RB: network::RequestBlocks + 'static, - C: HeaderBackend + Send + Sync + 'static, S: JustificationRequestScheduler, F: BlockFinalizer, V: Verifier, + BB: BlockchainBackend + 'static, { pub fn new( block_requester: RB, - client: Arc, + blockchain_backend: BB, finalizer: F, justification_request_scheduler: S, metrics: Option::Hash>>, - min_allowed_delay: NumberFor, ) -> Self { BlockRequester { block_requester, - client, + blockchain_backend, finalizer, justification_request_scheduler, metrics, - min_allowed_delay, request_status: JustificationRequestStatus::new(), _phantom: PhantomData, } @@ -169,29 +183,12 @@ where } } - pub fn request_justification(&mut self, num: NumberFor) { + pub fn request_justification(&mut self, wanted: NumberFor) { match self.justification_request_scheduler.schedule_action() { SchedulerActions::Request => { - let num = if num > self.client.info().best_number - && self.client.info().best_number > self.min_allowed_delay - { - self.client.info().best_number - self.min_allowed_delay - } else { - num - }; - - debug!(target: "aleph-justification", "Trying to request block {:?}", num); - self.request_status.save_block_number(num); - - if let Ok(Some(header)) = self.client.header(BlockId::Number(num)) { - self.request_status.insert_hash(header.hash()); - debug!(target: "aleph-justification", "We have block {:?} with hash {:?}. Requesting justification.", num, header.hash()); - self.justification_request_scheduler.on_request_sent(); - self.block_requester - .request_justification(&header.hash(), *header.number()); - } else { - debug!(target: "aleph-justification", "Cancelling request, because we don't have block {:?}.", num); - } + let info = self.blockchain_backend.info(); + self.request_children(&info); + self.request_wanted(wanted, &info); } SchedulerActions::ClearQueue => { debug!(target: "aleph-justification", "Clearing justification request queue"); @@ -202,6 +199,64 @@ where } pub fn finalized_number(&self) -> NumberFor { - self.client.info().finalized_number + self.blockchain_backend.info().finalized_number + } + + fn do_request(&mut self, hash: &::Hash, num: NumberFor) { + debug!(target: "aleph-justification", + "We have block {:?} with hash {:?}. Requesting justification.", num, hash); + self.justification_request_scheduler.on_request_sent(); + self.block_requester.request_justification(hash, num); + } + + // We request justifications for all the children of last finalized block. + // Assuming that we request at the same pace that finalization is progressing, it ensures + // that we are up to date with finalization. + // We also request the child that it's on the same branch as top_wanted since a fork may happen + // somewhere in between them. + fn request_children(&mut self, info: &Info) { + let finalized_hash = info.finalized_hash; + let finalized_number = info.finalized_number; + + let children = self.blockchain_backend.children(finalized_hash); + + if !children.is_empty() { + self.request_status + .save_children(finalized_hash, children.len()); + } + + for child in &children { + self.do_request(child, finalized_number + NumberFor::::one()); + } + } + + // This request is important in the case when we are far behind and want to catch up. + fn request_wanted(&mut self, mut top_wanted: NumberFor, info: &Info) { + let best_number = info.best_number; + if best_number <= top_wanted { + // most probably block best_number is not yet finalized + top_wanted = best_number.saturating_sub(NumberFor::::one()); + } + let finalized_number = info.finalized_number; + // We know that top_wanted >= finalized_number, so + // - if top_wanted == finalized_number, then we don't want to request it + // - if top_wanted == finalized_number + 1, then we already requested it + if top_wanted <= finalized_number + NumberFor::::one() { + return; + } + match self.blockchain_backend.header(BlockId::Number(top_wanted)) { + Ok(Some(header)) => { + let hash = header.hash(); + let num = *header.number(); + self.do_request(&hash, num); + self.request_status.save_block((hash, num).into()); + } + Ok(None) => { + warn!(target: "aleph-justification", "Cancelling request, because we don't have block {:?}.", top_wanted); + } + Err(err) => { + warn!(target: "aleph-justification", "Cancelling request, because fetching block {:?} failed {:?}.", top_wanted, err); + } + } } } diff --git a/finality-aleph/src/lib.rs b/finality-aleph/src/lib.rs index b805b4dfd2..7dc23e2bc9 100644 --- a/finality-aleph/src/lib.rs +++ b/finality-aleph/src/lib.rs @@ -8,7 +8,7 @@ use futures::{ channel::{mpsc, oneshot}, Future, }; -use sc_client_api::{backend::Backend, BlockchainEvents, Finalizer, LockImportRun, TransactionFor}; +use sc_client_api::{Backend, BlockchainEvents, Finalizer, LockImportRun, TransactionFor}; use sc_consensus::BlockImport; use sc_network::NetworkService; use sc_network_common::ExHashT; @@ -243,9 +243,10 @@ impl From<(H, N)> for HashNum { pub type BlockHashNum = HashNum<::Hash, NumberFor>; -pub struct AlephConfig { +pub struct AlephConfig { pub network: Arc>, pub client: Arc, + pub blockchain_backend: BB, pub select_chain: SC, pub spawn_handle: SpawnTaskHandle, pub keystore: Arc, @@ -258,3 +259,12 @@ pub struct AlephConfig { pub external_addresses: Vec, pub validator_port: u16, } + +pub trait BlockchainBackend { + fn children(&self, parent_hash: ::Hash) -> Vec<::Hash>; + fn info(&self) -> sp_blockchain::Info; + fn header( + &self, + block_id: sp_api::BlockId, + ) -> sp_blockchain::Result::Header>>; +} diff --git a/finality-aleph/src/nodes/mod.rs b/finality-aleph/src/nodes/mod.rs index 28326a287e..91e063284d 100644 --- a/finality-aleph/src/nodes/mod.rs +++ b/finality-aleph/src/nodes/mod.rs @@ -27,7 +27,7 @@ use crate::{ mpsc::UnboundedSender, session_id_from_block_num, session_map::ReadOnlySessionMap, - JustificationNotification, Metrics, MillisecsPerBlock, SessionPeriod, + BlockchainBackend, JustificationNotification, Metrics, MillisecsPerBlock, SessionPeriod, }; #[cfg(test)] @@ -84,9 +84,10 @@ impl Verifier for JustificationVerifier { } } -struct JustificationParams { +struct JustificationParams { pub network: Arc>, pub client: Arc, + pub blockchain_backend: BB, pub justification_rx: mpsc::UnboundedReceiver>, pub metrics: Option::Hash>>, pub session_period: SessionPeriod, @@ -127,8 +128,8 @@ impl SessionInfoProvider for SessionInfoProv } } -fn setup_justification_handler( - just_params: JustificationParams, +fn setup_justification_handler( + just_params: JustificationParams, ) -> ( UnboundedSender>, impl Future, @@ -139,10 +140,12 @@ where C: crate::ClientForAleph + Send + Sync + 'static, C::Api: aleph_primitives::AlephSessionApi, BE: Backend + 'static, + BB: BlockchainBackend + 'static + Send, { let JustificationParams { network, client, + blockchain_backend, justification_rx, metrics, session_period, @@ -153,7 +156,7 @@ where let handler = JustificationHandler::new( SessionInfoProviderImpl::new(session_map, session_period), network, - client.clone(), + blockchain_backend, AlephFinalizer::new(client), JustificationRequestSchedulerImpl::new(&session_period, &millisecs_per_block, MAX_ATTEMPTS), metrics, diff --git a/finality-aleph/src/nodes/nonvalidator_node.rs b/finality-aleph/src/nodes/nonvalidator_node.rs index 0e349767d7..a768f1cfd5 100644 --- a/finality-aleph/src/nodes/nonvalidator_node.rs +++ b/finality-aleph/src/nodes/nonvalidator_node.rs @@ -7,21 +7,23 @@ use sp_runtime::traits::Block; use crate::{ nodes::{setup_justification_handler, JustificationParams}, session_map::{AuthorityProviderImpl, FinalityNotificatorImpl, SessionMapUpdater}, - AlephConfig, + AlephConfig, BlockchainBackend, }; -pub async fn run_nonvalidator_node(aleph_config: AlephConfig) +pub async fn run_nonvalidator_node(aleph_config: AlephConfig) where B: Block, H: ExHashT, C: crate::ClientForAleph + Send + Sync + 'static, C::Api: aleph_primitives::AlephSessionApi, BE: Backend + 'static, + BB: BlockchainBackend + Send + 'static, SC: SelectChain + 'static, { let AlephConfig { network, client, + blockchain_backend, metrics, session_period, millisecs_per_block, @@ -42,6 +44,7 @@ where justification_rx, network, client, + blockchain_backend, metrics, session_period, millisecs_per_block, diff --git a/finality-aleph/src/nodes/validator_node.rs b/finality-aleph/src/nodes/validator_node.rs index 9cb3dda2a3..ec06def4bb 100644 --- a/finality-aleph/src/nodes/validator_node.rs +++ b/finality-aleph/src/nodes/validator_node.rs @@ -24,7 +24,7 @@ use crate::{ session_map::{AuthorityProviderImpl, FinalityNotificatorImpl, SessionMapUpdater}, tcp_network::{new_tcp_network, KEY_TYPE}, validator_network::Service, - AlephConfig, + AlephConfig, BlockchainBackend, }; pub async fn new_pen(mnemonic: &str, keystore: Arc) -> AuthorityPen { @@ -37,18 +37,20 @@ pub async fn new_pen(mnemonic: &str, keystore: Arc) -> Authorit .expect("we just generated this key so everything should work") } -pub async fn run_validator_node(aleph_config: AlephConfig) +pub async fn run_validator_node(aleph_config: AlephConfig) where B: Block, H: ExHashT, C: crate::ClientForAleph + Send + Sync + 'static, C::Api: aleph_primitives::AlephSessionApi, BE: Backend + 'static, + BB: BlockchainBackend + Send + 'static, SC: SelectChain + 'static, { let AlephConfig { network, client, + blockchain_backend, select_chain, spawn_handle, keystore, @@ -106,6 +108,7 @@ where justification_rx, network: network.clone(), client: client.clone(), + blockchain_backend, metrics: metrics.clone(), session_period, millisecs_per_block, diff --git a/finality-aleph/src/testing/justification.rs b/finality-aleph/src/testing/justification.rs index 5777d4e72d..9cb3fd4373 100644 --- a/finality-aleph/src/testing/justification.rs +++ b/finality-aleph/src/testing/justification.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, collections::VecDeque, sync::Arc, time::Duration}; +use std::{cell::RefCell, collections::VecDeque, time::Duration}; use futures::{ channel::mpsc::{unbounded, UnboundedSender}, @@ -12,7 +12,7 @@ use AcceptancePolicy::*; use crate::{ justification::{AlephJustification, JustificationHandler, JustificationHandlerConfig}, testing::mocks::{ - create_block, AcceptancePolicy, Client, JustificationRequestSchedulerImpl, + create_block, AcceptancePolicy, Backend, JustificationRequestSchedulerImpl, MockedBlockFinalizer, MockedBlockRequester, SessionInfoProviderImpl, TBlock, VerifierWrapper, }, @@ -26,15 +26,15 @@ type TJustHandler = JustificationHandler< TBlock, VerifierWrapper, MockedBlockRequester, - Client, JustificationRequestSchedulerImpl, SessionInfoProviderImpl, MockedBlockFinalizer, + Backend, >; type Sender = UnboundedSender>; type Environment = ( TJustHandler, - Client, + Backend, MockedBlockRequester, MockedBlockFinalizer, JustificationRequestSchedulerImpl, @@ -67,7 +67,7 @@ fn prepare_env( verification_policy: AcceptancePolicy, request_policy: AcceptancePolicy, ) -> Environment { - let client = Client::new(finalization_height); + let backend = Backend::new(finalization_height); let info_provider = SessionInfoProviderImpl::new(SESSION_PERIOD, verification_policy); let finalizer = MockedBlockFinalizer::new(); let requester = MockedBlockRequester::new(); @@ -77,7 +77,7 @@ fn prepare_env( let justification_handler = JustificationHandler::new( info_provider, requester.clone(), - Arc::new(client.clone()), + backend.clone(), finalizer.clone(), justification_request_scheduler.clone(), None, @@ -86,7 +86,7 @@ fn prepare_env( ( justification_handler, - client, + backend, requester, finalizer, justification_request_scheduler, @@ -125,19 +125,19 @@ where S: FnOnce( Sender, Sender, - Client, + Backend, MockedBlockRequester, MockedBlockFinalizer, JustificationRequestSchedulerImpl, ) -> F, { - let (justification_handler, client, requester, finalizer, justification_request_scheduler) = + let (justification_handler, backend, requester, finalizer, justification_request_scheduler) = env; let (handle_run, auth_just_tx, imp_just_tx) = run_justification_handler(justification_handler); scenario( auth_just_tx.clone(), imp_just_tx.clone(), - client, + backend, requester, finalizer, justification_request_scheduler, @@ -186,8 +186,8 @@ async fn expect_not_requested( async fn leads_to_finalization_when_appropriate_justification_comes() { run_test( prepare_env(FINALIZED_HEIGHT, AlwaysAccept, AlwaysReject), - |_, imp_just_tx, client, _, finalizer, justification_request_scheduler| async move { - let block = client.next_block_to_finalize(); + |_, imp_just_tx, backend, _, finalizer, justification_request_scheduler| async move { + let block = backend.next_block_to_finalize(); let message = create_justification_notification_for(block.clone()); imp_just_tx.unbounded_send(message).unwrap(); expect_finalized(&finalizer, &justification_request_scheduler, block).await; @@ -201,8 +201,8 @@ async fn waits_for_verifier_before_finalizing() { let verification_policy = FromSequence(RefCell::new(VecDeque::from(vec![false, false, true]))); run_test( prepare_env(FINALIZED_HEIGHT, verification_policy, AlwaysReject), - |_, imp_just_tx, client, _, finalizer, justification_request_scheduler| async move { - let block = client.next_block_to_finalize(); + |_, imp_just_tx, backend, _, finalizer, justification_request_scheduler| async move { + let block = backend.next_block_to_finalize(); let message = create_justification_notification_for(block.clone()); imp_just_tx.unbounded_send(message.clone()).unwrap(); @@ -222,8 +222,8 @@ async fn waits_for_verifier_before_finalizing() { async fn keeps_finalizing_block_if_not_finalized_yet() { run_test( prepare_env(FINALIZED_HEIGHT, AlwaysAccept, AlwaysReject), - |auth_just_tx, imp_just_tx, client, _, finalizer, justification_request_scheduler| async move { - let block = client.next_block_to_finalize(); + |auth_just_tx, imp_just_tx, backend, _, finalizer, justification_request_scheduler| async move { + let block = backend.next_block_to_finalize(); let message = create_justification_notification_for(block.clone()); imp_just_tx.unbounded_send(message.clone()).unwrap(); @@ -240,8 +240,8 @@ async fn keeps_finalizing_block_if_not_finalized_yet() { async fn ignores_notifications_for_old_blocks() { run_test( prepare_env(FINALIZED_HEIGHT, AlwaysAccept, AlwaysReject), - |_, imp_just_tx, client, _, finalizer, justification_request_scheduler| async move { - let block = client.get_block(BlockId::Number(1u64)).unwrap(); + |_, imp_just_tx, backend, _, finalizer, justification_request_scheduler| async move { + let block = backend.get_block(BlockId::Number(1u64)).unwrap(); let message = create_justification_notification_for(block); imp_just_tx.unbounded_send(message).unwrap(); expect_not_finalized(&finalizer, &justification_request_scheduler).await; @@ -268,8 +268,8 @@ async fn ignores_notifications_from_future_session() { async fn does_not_buffer_notifications_from_future_session() { run_test( prepare_env((SESSION_PERIOD.0 - 2) as u64, AlwaysAccept, AlwaysReject), - |_, imp_just_tx, client, _, finalizer, justification_request_scheduler| async move { - let current_block = client.next_block_to_finalize(); + |_, imp_just_tx, backend, _, finalizer, justification_request_scheduler| async move { + let current_block = backend.next_block_to_finalize(); let future_block = create_block(current_block.hash(), SESSION_PERIOD.0 as u64); let message = create_justification_notification_for(future_block); @@ -290,8 +290,8 @@ async fn does_not_buffer_notifications_from_future_session() { async fn requests_for_session_ending_justification() { run_test( prepare_env((SESSION_PERIOD.0 - 2) as u64, AlwaysReject, AlwaysAccept), - |_, imp_just_tx, client, requester, _, justification_request_scheduler| async move { - let last_block = client.next_block_to_finalize(); + |_, imp_just_tx, backend, requester, _, justification_request_scheduler| async move { + let last_block = backend.next_block_to_finalize(); // doesn't need any notification passed to keep asking expect_requested( @@ -321,14 +321,14 @@ async fn requests_for_session_ending_justification() { async fn does_not_request_for_session_ending_justification_too_often() { run_test( prepare_env((SESSION_PERIOD.0 - 2) as u64, AlwaysReject, AlwaysReject), - |_, _, client, requester, _, justification_request_scheduler| async move { + |_, _, backend, requester, _, justification_request_scheduler| async move { expect_not_requested(&requester, &justification_request_scheduler).await; justification_request_scheduler.update_policy(AlwaysAccept); expect_requested( &requester, &justification_request_scheduler, - client.next_block_to_finalize(), + backend.next_block_to_finalize(), ) .await; @@ -343,10 +343,10 @@ async fn does_not_request_for_session_ending_justification_too_often() { async fn does_not_request_nor_finalize_when_verifier_is_not_available() { run_test( prepare_env((SESSION_PERIOD.0 - 2) as u64, Unavailable, AlwaysAccept), - |_, imp_just_tx, client, requester, finalizer, justification_request_scheduler| async move { + |_, imp_just_tx, backend, requester, finalizer, justification_request_scheduler| async move { expect_not_requested(&requester, &justification_request_scheduler).await; - let block = client.next_block_to_finalize(); + let block = backend.next_block_to_finalize(); imp_just_tx .unbounded_send(create_justification_notification_for(block)) .unwrap(); diff --git a/finality-aleph/src/testing/mocks/header_backend.rs b/finality-aleph/src/testing/mocks/backend.rs similarity index 72% rename from finality-aleph/src/testing/mocks/header_backend.rs rename to finality-aleph/src/testing/mocks/backend.rs index 4adaf2c8f2..a2365ad7b4 100644 --- a/finality-aleph/src/testing/mocks/header_backend.rs +++ b/finality-aleph/src/testing/mocks/backend.rs @@ -1,11 +1,14 @@ use sp_api::BlockId; -use sp_blockchain::{BlockStatus, HeaderBackend, Info}; +use sp_blockchain::Info; use sp_runtime::traits::Block; -use crate::testing::mocks::{TBlock, THash, THeader, TNumber}; +use crate::{ + testing::mocks::{TBlock, THash, THeader, TNumber}, + BlockchainBackend, +}; #[derive(Clone)] -pub(crate) struct Client { +pub(crate) struct Backend { blocks: Vec, next_block_to_finalize: TBlock, } @@ -25,7 +28,7 @@ pub(crate) fn create_block(parent_hash: THash, number: TNumber) -> TBlock { const GENESIS_HASH: [u8; 32] = [0u8; 32]; -impl Client { +impl Backend { pub(crate) fn new(finalized_height: u64) -> Self { let mut blocks: Vec = vec![]; @@ -40,7 +43,7 @@ impl Client { let next_block_to_finalize = create_block(blocks.last().unwrap().hash(), finalized_height + 1); - Client { + Backend { blocks, next_block_to_finalize, } @@ -70,11 +73,30 @@ impl Client { } } -impl HeaderBackend for Client { +impl BlockchainBackend for Backend { + fn children(&self, parent_hash: THash) -> Vec { + if self.next_block_to_finalize.hash() == parent_hash { + Vec::new() + } else if self + .blocks + .last() + .map(|b| b.hash()) + .unwrap() + .eq(&parent_hash) + { + vec![self.next_block_to_finalize.hash()] + } else { + self.blocks + .windows(2) + .flat_map(<&[TBlock; 2]>::try_from) + .find(|[parent, _]| parent.header.hash().eq(&parent_hash)) + .map(|[_, c]| vec![c.hash()]) + .unwrap_or_default() + } + } fn header(&self, id: BlockId) -> sp_blockchain::Result> { Ok(self.get_block(id).map(|b| b.header)) } - fn info(&self) -> Info { Info { best_hash: self.next_block_to_finalize.hash(), @@ -87,23 +109,8 @@ impl HeaderBackend for Client { block_gap: None, } } - - fn status(&self, id: BlockId) -> sp_blockchain::Result { - Ok(match self.get_block(id) { - Some(_) => BlockStatus::InChain, - _ => BlockStatus::Unknown, - }) - } - - fn number(&self, hash: THash) -> sp_blockchain::Result> { - Ok(self.get_block(BlockId::hash(hash)).map(|b| b.header.number)) - } - - fn hash(&self, number: TNumber) -> sp_blockchain::Result> { - Ok(self.get_block(BlockId::Number(number)).map(|b| b.hash())) - } } -unsafe impl Send for Client {} +unsafe impl Send for Backend {} -unsafe impl Sync for Client {} +unsafe impl Sync for Backend {} diff --git a/finality-aleph/src/testing/mocks/justification_handler_config.rs b/finality-aleph/src/testing/mocks/justification_handler_config.rs index 49dee9543e..6db2ec7f16 100644 --- a/finality-aleph/src/testing/mocks/justification_handler_config.rs +++ b/finality-aleph/src/testing/mocks/justification_handler_config.rs @@ -5,7 +5,7 @@ use std::{ use crate::{ justification::{JustificationHandlerConfig, JustificationRequestScheduler, SchedulerActions}, - testing::mocks::{single_action_mock::SingleActionMock, AcceptancePolicy, TBlock}, + testing::mocks::{single_action_mock::SingleActionMock, AcceptancePolicy}, }; #[derive(Clone)] @@ -58,12 +58,11 @@ impl JustificationRequestScheduler for JustificationRequestSchedulerImpl { const DEFAULT_VERIFIER_TIMEOUT_MS: u64 = 10u64; const DEFAULT_NOTIFICATION_TIMEOUT_MS: u64 = 10u64; -impl JustificationHandlerConfig { +impl JustificationHandlerConfig { pub fn test() -> Self { JustificationHandlerConfig::new( Duration::from_millis(DEFAULT_VERIFIER_TIMEOUT_MS), Duration::from_millis(DEFAULT_NOTIFICATION_TIMEOUT_MS), - 3u32.into(), ) } } diff --git a/finality-aleph/src/testing/mocks/mod.rs b/finality-aleph/src/testing/mocks/mod.rs index 3a95478e8d..dee1ad4e89 100644 --- a/finality-aleph/src/testing/mocks/mod.rs +++ b/finality-aleph/src/testing/mocks/mod.rs @@ -1,7 +1,7 @@ pub(crate) use acceptance_policy::AcceptancePolicy; +pub(crate) use backend::{create_block, Backend}; pub(crate) use block_finalizer::MockedBlockFinalizer; pub(crate) use block_request::MockedBlockRequester; -pub(crate) use header_backend::{create_block, Client}; pub(crate) use justification_handler_config::JustificationRequestSchedulerImpl; pub(crate) use proposal::{ aleph_data_from_blocks, aleph_data_from_headers, unvalidated_proposal_from_headers, @@ -14,9 +14,9 @@ pub(crate) type THash = substrate_test_runtime::Hash; pub(crate) type TNumber = substrate_test_runtime::BlockNumber; mod acceptance_policy; +mod backend; mod block_finalizer; mod block_request; -mod header_backend; mod justification_handler_config; mod proposal; mod session_info;