Offline fallback for GRANDPA (#1619)

Co-authored-by: André Silva <andre.beat@gmail.com>

* skeleton for finality tracker

* dispatch events when nothing finalized for a long time

* begin integrating finality tracker into grandpa

* add delay field to pending change

* add has_api_with function to sr_version for querying APIs

* partially integrate new force changes into grandpa

* implement forced changes

* get srml-grandpa compiling

* Update core/finality-grandpa/src/authorities.rs

Co-Authored-By: rphmeier <rphmeier@gmail.com>

* Update core/finality-grandpa/src/authorities.rs

Co-Authored-By: rphmeier <rphmeier@gmail.com>

* Update core/finality-grandpa/src/authorities.rs

Co-Authored-By: rphmeier <rphmeier@gmail.com>

* remove explicit dependence on CoreApi

* increase node runtime version

* integrate grandpa forced changes into node runtime

* add some tests to finality-tracker

* integrate finality tracking into node-runtime

* test forced-change logic

* test forced changes in the authority-set handler

* kill some unneeded bounds in client

* test forced-changes in finality-grandpa and fix logic

* build wasm and finality-tracker is no-std

* restart voter on forced change

* allow returning custom error type from lock_import_and_run

* extract out most DB logic to aux_schema and use atomic client ops

* unify authority set writing

* implement set pausing

* bump runtime version

* note on DB when we pause.

* core: grandpa: integrate forced changes with multiple pending standard changes

* core: grandpa: fix AuthoritySet tests

* runtime: bump impl_version

* core: clear pending justification requests after forced change import

* srml: finality-tracker: use FinalizedInherentData

* core: log requests for clearing justification requests

* core, node: update runtimes

* core: grandpa: fix tests

* core: grandpa: remove todos and add comments

* core: grandpa: use has_api_with from ApiExt

* core: fix tests

* core: grandpa: remove unnecessary mut modifier

* core: replace PostImportActions bitflags with struct

* core: grandpa: restrict genesis on forced authority set change

* core: grandpa: add more docs

* core: grandpa: prevent safety violations in Environment::finalize_block

* core: grandpa: register finality tracker inherent data provider

* core: grandpa: fix tests

* node: update runtime blobs

* core: grandpa: remove outdated todo

* core: aura: fix typo in log message

* core: grandpa: check re-finalization is on canonical chain

* srml: finality-tracker: fix initialization

* node: update runtime wasm

* srml: finality-tracker: don't re-initialize config keys
This commit is contained in:
Robert Habermeier
2019-03-05 16:41:35 +01:00
committed by André Silva
parent 128d164f2b
commit dfb48a2405
31 changed files with 2217 additions and 516 deletions
@@ -23,19 +23,40 @@ use std::borrow::Cow;
/// Block import result.
#[derive(Debug, PartialEq, Eq)]
pub enum ImportResult {
/// Added to the import queue.
Queued,
/// Already in the import queue.
AlreadyQueued,
/// Block imported.
Imported(ImportedAux),
/// Already in the blockchain.
AlreadyInChain,
/// Block or parent is known to be bad.
KnownBad,
/// Block parent is not in the chain.
UnknownParent,
/// Added to the import queue but must be justified
/// (usually required to safely enact consensus changes).
NeedsJustification,
}
/// Auxiliary data associated with an imported block result.
#[derive(Debug, PartialEq, Eq)]
pub struct ImportedAux {
/// Clear all pending justification requests.
pub clear_justification_requests: bool,
/// Request a justification for the given block.
pub needs_justification: bool,
}
impl Default for ImportedAux {
fn default() -> ImportedAux {
ImportedAux {
clear_justification_requests: false,
needs_justification: false,
}
}
}
impl ImportResult {
/// Returns default value for `ImportResult::Imported` with both
/// `clear_justification_requests` and `needs_justification` set to false.
pub fn imported() -> ImportResult {
ImportResult::Imported(ImportedAux::default())
}
}
/// Block data origin.
@@ -24,7 +24,9 @@
//! The `BasicQueue` and `BasicVerifier` traits allow serial queues to be
//! instantiated simply.
use crate::block_import::{ImportBlock, BlockImport, JustificationImport, ImportResult, BlockOrigin};
use crate::block_import::{
BlockImport, BlockOrigin, ImportBlock, ImportedAux, ImportResult, JustificationImport,
};
use crossbeam_channel::{self as channel, Receiver, Sender};
use std::sync::Arc;
@@ -307,31 +309,37 @@ impl<B: BlockT> BlockImporter<B> {
match result {
Ok(BlockImportResult::ImportedKnown(number)) => link.block_imported(&hash, number),
Ok(BlockImportResult::ImportedUnknown(number)) => {
link.block_imported(&hash, number)
}
Ok(BlockImportResult::ImportedUnjustified(number)) => {
Ok(BlockImportResult::ImportedUnknown(number, aux)) => {
link.block_imported(&hash, number);
link.request_justification(&hash, number);
if aux.clear_justification_requests {
trace!(target: "sync", "Block imported clears all pending justification requests {}: {:?}", number, hash);
link.clear_justification_requests();
}
if aux.needs_justification {
trace!(target: "sync", "Block imported but requires justification {}: {:?}", number, hash);
link.request_justification(&hash, number);
}
},
Err(BlockImportError::IncompleteHeader(who)) => {
if let Some(peer) = who {
link.note_useless_and_restart_sync(peer, "Sent block with incomplete header to import");
}
}
},
Err(BlockImportError::VerificationFailed(who, e)) => {
if let Some(peer) = who {
link.note_useless_and_restart_sync(peer, &format!("Verification failed: {}", e));
}
}
},
Err(BlockImportError::BadBlock(who)) => {
if let Some(peer) = who {
link.note_useless_and_restart_sync(peer, "Sent us a bad block");
}
}
},
Err(BlockImportError::UnknownParent) | Err(BlockImportError::Error) => {
link.restart()
}
link.restart();
},
};
}
if let Some(link) = self.link.as_ref() {
@@ -448,13 +456,15 @@ impl<B: BlockT, V: 'static + Verifier<B>> BlockImportWorker<B, V> {
/// algorithm.
pub trait Link<B: BlockT>: Send {
/// Block imported.
fn block_imported(&self, _hash: &B::Hash, _number: NumberFor<B>) { }
fn block_imported(&self, _hash: &B::Hash, _number: NumberFor<B>) {}
/// Batch of blocks imported, with or without error.
fn blocks_processed(&self, _processed_blocks: Vec<B::Hash>, _has_error: bool) {}
/// Justification import result.
fn justification_imported(&self, _who: Origin, _hash: &B::Hash, _number: NumberFor<B>, _success: bool) { }
fn justification_imported(&self, _who: Origin, _hash: &B::Hash, _number: NumberFor<B>, _success: bool) {}
/// Clear all pending justification requests.
fn clear_justification_requests(&self) {}
/// Request a justification for the given block.
fn request_justification(&self, _hash: &B::Hash, _number: NumberFor<B>) { }
fn request_justification(&self, _hash: &B::Hash, _number: NumberFor<B>) {}
/// Disconnect from peer.
fn useless_peer(&self, _who: Origin, _reason: &str) {}
/// Disconnect from peer and restart sync.
@@ -469,9 +479,7 @@ pub enum BlockImportResult<N: ::std::fmt::Debug + PartialEq> {
/// Imported known block.
ImportedKnown(N),
/// Imported unknown block.
ImportedUnknown(N),
/// Imported unjustified block that requires one.
ImportedUnjustified(N),
ImportedUnknown(N, ImportedAux),
}
/// Block import error.
@@ -520,17 +528,7 @@ pub fn import_single_block<B: BlockT, V: Verifier<B>>(
trace!(target: "sync", "Block already in chain {}: {:?}", number, hash);
Ok(BlockImportResult::ImportedKnown(number))
},
Ok(ImportResult::AlreadyQueued) => {
trace!(target: "sync", "Block already queued {}: {:?}", number, hash);
Ok(BlockImportResult::ImportedKnown(number))
},
Ok(ImportResult::Queued) => {
Ok(BlockImportResult::ImportedUnknown(number))
},
Ok(ImportResult::NeedsJustification) => {
trace!(target: "sync", "Block queued but requires justification {}: {:?}", number, hash);
Ok(BlockImportResult::ImportedUnjustified(number))
},
Ok(ImportResult::Imported(aux)) => Ok(BlockImportResult::ImportedUnknown(number, aux)),
Ok(ImportResult::UnknownParent) => {
debug!(target: "sync", "Block with unknown parent {}: {:?}, parent: {:?}", number, hash, parent);
Err(BlockImportError::UnknownParent)
@@ -547,7 +545,7 @@ pub fn import_single_block<B: BlockT, V: Verifier<B>>(
};
match import_error(import_handle.check_block(hash, parent))? {
BlockImportResult::ImportedUnknown(_) => (),
BlockImportResult::ImportedUnknown { .. } => (),
r @ _ => return Ok(r), // Any other successfull result means that the block is already imported.
}
@@ -629,7 +627,7 @@ mod tests {
assert_eq!(link_port.recv(), Ok(LinkMsg::BlockImported));
// Send an unknown
let results = vec![(Ok(BlockImportResult::ImportedUnknown(Default::default())), Default::default())];
let results = vec![(Ok(BlockImportResult::ImportedUnknown(Default::default(), Default::default())), Default::default())];
let _ = result_sender.send(BlockImportWorkerMsg::Imported(results)).ok().unwrap();
assert_eq!(link_port.recv(), Ok(LinkMsg::BlockImported));
+3 -1
View File
@@ -47,7 +47,9 @@ pub mod evaluation;
const MAX_BLOCK_SIZE: usize = 4 * 1024 * 1024 + 512;
pub use self::error::{Error, ErrorKind};
pub use block_import::{BlockImport, JustificationImport, ImportBlock, BlockOrigin, ImportResult, ForkChoiceStrategy};
pub use block_import::{
BlockImport, BlockOrigin, ForkChoiceStrategy, ImportedAux, ImportBlock, ImportResult, JustificationImport,
};
/// Trait for getting the authorities at a given block.
pub trait Authorities<B: Block> {