diff --git a/.github/workflows/e2e-tests-main-devnet.yml b/.github/workflows/e2e-tests-main-devnet.yml index 2c4bf8d040..95dc16e129 100644 --- a/.github/workflows/e2e-tests-main-devnet.yml +++ b/.github/workflows/e2e-tests-main-devnet.yml @@ -454,7 +454,7 @@ jobs: run-e2e-rewards-stake-change, run-e2e-rewards-change-stake-force-new-era, run-e2e-rewards-points-basic, - run-e2e-authorities-are-staking, +# run-e2e-authorities-are-staking, run-e2e-ban-automatic, run-e2e-version-upgrade, ] diff --git a/Cargo.lock b/Cargo.lock index 53185e7405..d07508120c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -227,7 +227,7 @@ dependencies = [ [[package]] name = "aleph-node" -version = "0.8.0+mainnet" +version = "0.8.2+mainnet" dependencies = [ "aleph-runtime", "clap", @@ -249,6 +249,7 @@ dependencies = [ "sc-client-api", "sc-consensus", "sc-consensus-aura", + "sc-consensus-slots", "sc-executor", "sc-keystore", "sc-network", @@ -262,6 +263,7 @@ dependencies = [ "serde_json", "sp-api", "sp-application-crypto", + "sp-arithmetic", "sp-block-builder", "sp-blockchain", "sp-consensus", @@ -529,9 +531,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.58" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" dependencies = [ "proc-macro2", "quote", @@ -1880,7 +1882,7 @@ dependencies = [ [[package]] name = "finality-aleph" -version = "0.5.0" +version = "0.5.3" dependencies = [ "aggregator 0.1.0", "aggregator 0.2.0", @@ -5302,7 +5304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.4", + "parking_lot_core 0.9.5", ] [[package]] @@ -5321,9 +5323,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" dependencies = [ "cfg-if", "libc", diff --git a/bin/node/Cargo.toml b/bin/node/Cargo.toml index 9076473500..94fc45d1d6 100644 --- a/bin/node/Cargo.toml +++ b/bin/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aleph-node" -version = "0.8.0+mainnet" +version = "0.8.2+mainnet" authors = ["Cardinal Cryptography"] description = "Aleph node binary" edition = "2021" @@ -43,6 +43,8 @@ sc-network = { git = "https://github.com/Cardinal-Cryptography/substrate.git", b sc-transaction-pool = { git = "https://github.com/Cardinal-Cryptography/substrate.git", branch = "aleph-v0.9.28" } sp-transaction-pool = { git = "https://github.com/Cardinal-Cryptography/substrate.git", branch = "aleph-v0.9.28" } sc-transaction-pool-api = { git = "https://github.com/Cardinal-Cryptography/substrate.git", branch = "aleph-v0.9.28" } +sp-arithmetic = { git = "https://github.com/Cardinal-Cryptography/substrate.git", branch = "aleph-v0.9.28" } +sc-consensus-slots = { git = "https://github.com/Cardinal-Cryptography/substrate.git", branch = "aleph-v0.9.28" } sc-consensus-aura = { git = "https://github.com/Cardinal-Cryptography/substrate.git", branch = "aleph-v0.9.28" } sp-consensus-aura = { git = "https://github.com/Cardinal-Cryptography/substrate.git", branch = "aleph-v0.9.28" } sp-consensus = { git = "https://github.com/Cardinal-Cryptography/substrate.git", branch = "aleph-v0.9.28" } diff --git a/bin/node/src/aleph_cli.rs b/bin/node/src/aleph_cli.rs index c43c3e336f..f2324c14b2 100644 --- a/bin/node/src/aleph_cli.rs +++ b/bin/node/src/aleph_cli.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use aleph_primitives::DEFAULT_UNIT_CREATION_DELAY; use clap::{ArgGroup, Parser}; use finality_aleph::UnitCreationDelay; +use log::warn; #[derive(Debug, Parser, Clone)] #[clap(group(ArgGroup::new("backup")))] @@ -33,6 +34,12 @@ pub struct AlephCli { /// with `--no-backup`, but note that that limits crash recoverability. #[clap(long, value_name = "PATH", group = "backup")] backup_path: Option, + + /// The maximum number of nonfinalized blocks, after which block production should be locally + /// stopped. DO NOT CHANGE THIS, PRODUCING MORE OR FEWER BLOCKS MIGHT BE CONSIDERED MALICIOUS + /// BEHAVIOUR AND PUNISHED ACCORDINGLY! + #[clap(long, default_value_t = 20)] + max_nonfinalized_blocks: u32, } impl AlephCli { @@ -55,4 +62,11 @@ impl AlephCli { pub fn no_backup(&self) -> bool { self.no_backup } + + pub fn max_nonfinalized_blocks(&self) -> u32 { + if self.max_nonfinalized_blocks != 20 { + warn!("Running block production with a value of max-nonfinalized-blocks {}, which is not the default of 20. THIS MIGHT BE CONSIDERED MALICIOUS BEHAVIOUR AND RESULT IN PENALTIES!", self.max_nonfinalized_blocks); + } + self.max_nonfinalized_blocks + } } diff --git a/bin/node/src/service.rs b/bin/node/src/service.rs index 4d0ad4ed2f..7fdf2e2d4a 100644 --- a/bin/node/src/service.rs +++ b/bin/node/src/service.rs @@ -13,8 +13,9 @@ use finality_aleph::{ }; use futures::channel::mpsc; use log::warn; -use sc_client_api::ExecutorProvider; +use sc_client_api::{Backend, ExecutorProvider, HeaderBackend}; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; +use sc_consensus_slots::BackoffAuthoringBlocksStrategy; use sc_network::NetworkService; use sc_service::{ error::Error as ServiceError, Configuration, KeystoreContainer, NetworkStarter, RpcHandlers, @@ -22,7 +23,9 @@ use sc_service::{ }; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_api::ProvideRuntimeApi; -use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; +use sp_arithmetic::traits::BaseArithmetic; +use sp_blockchain::Backend as _; +use sp_consensus_aura::{sr25519::AuthorityPair as AuraPair, Slot}; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, Header as HeaderT, Zero}, @@ -34,7 +37,31 @@ 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 { +struct LimitNonfinalized(u32); + +impl BackoffAuthoringBlocksStrategy for LimitNonfinalized { + fn should_backoff( + &self, + chain_head_number: N, + _chain_head_slot: Slot, + finalized_number: N, + _slow_now: Slot, + _logging_target: &str, + ) -> bool { + let nonfinalized_blocks: u32 = chain_head_number + .saturating_sub(finalized_number) + .unique_saturated_into(); + match nonfinalized_blocks >= self.0 { + true => { + warn!("We have {} nonfinalized blocks, with the limit being {}, delaying block production.", nonfinalized_blocks, self.0); + true + } + false => false, + } + } +} + +fn backup_path(aleph_config: &AlephCli, base_path: &Path) -> Option { if aleph_config.no_backup() { return None; } @@ -264,7 +291,7 @@ pub fn new_authority( .extra_sets .push(finality_aleph::peers_set_config(Protocol::Validator)); - let backup_path = get_backup_path( + let backup_path = backup_path( &aleph_config, config .base_path @@ -288,12 +315,12 @@ pub fn new_authority( ); let force_authoring = config.force_authoring; - let backoff_authoring_blocks: Option<()> = None; + let backoff_authoring_blocks = Some(LimitNonfinalized(aleph_config.max_nonfinalized_blocks())); let prometheus_registry = config.prometheus_registry().cloned(); let (_rpc_handlers, network, network_starter) = setup( config, - backend, + backend.clone(), &keystore_container, import_queue, transaction_pool.clone(), @@ -353,9 +380,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, @@ -393,7 +422,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 @@ -404,7 +433,7 @@ pub fn new_full( let (_rpc_handlers, network, network_starter) = setup( config, - backend, + backend.clone(), &keystore_container, import_queue, transaction_pool, @@ -428,9 +457,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, @@ -453,3 +484,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 de6c62df72..40a081f647 100644 --- a/finality-aleph/Cargo.toml +++ b/finality-aleph/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "finality-aleph" -version = "0.5.0" +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 8326fa7bc6..16ed7360bd 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::{ExHashT, NetworkService}; use sc_service::SpawnTaskHandle; @@ -250,9 +250,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, @@ -265,3 +266,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 fb36090eea..0d53d23136 100644 --- a/finality-aleph/src/nodes/mod.rs +++ b/finality-aleph/src/nodes/mod.rs @@ -26,7 +26,7 @@ use crate::{ mpsc::UnboundedSender, session_id_from_block_num, session_map::ReadOnlySessionMap, - JustificationNotification, Metrics, MillisecsPerBlock, SessionPeriod, + BlockchainBackend, JustificationNotification, Metrics, MillisecsPerBlock, SessionPeriod, }; /// Max amount of tries we can not update a finalized block number before we will clear requests queue @@ -78,9 +78,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, @@ -121,8 +122,8 @@ impl SessionInfoProvider for SessionInfoProv } } -fn setup_justification_handler( - just_params: JustificationParams, +fn setup_justification_handler( + just_params: JustificationParams, ) -> ( UnboundedSender>, impl Future, @@ -133,10 +134,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, @@ -147,7 +150,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 e70c03f6b3..9fc076bfd9 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 53697bbccf..daaf3f2813 100644 --- a/finality-aleph/src/nodes/validator_node.rs +++ b/finality-aleph/src/nodes/validator_node.rs @@ -23,21 +23,23 @@ use crate::{ session_map::{AuthorityProviderImpl, FinalityNotificatorImpl, SessionMapUpdater}, tcp_network::new_tcp_network, validator_network::{Service, KEY_TYPE}, - AlephConfig, + AlephConfig, BlockchainBackend, }; -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, @@ -101,6 +103,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;