mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 01:41:03 +00:00
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:
committed by
André Silva
parent
128d164f2b
commit
dfb48a2405
@@ -29,8 +29,7 @@ use client::{
|
||||
runtime_api::{Core, RuntimeVersion, ApiExt},
|
||||
};
|
||||
use test_client::{self, runtime::BlockNumber};
|
||||
use parity_codec::Decode;
|
||||
use consensus_common::{BlockOrigin, ForkChoiceStrategy, ImportBlock, ImportResult};
|
||||
use consensus_common::{BlockOrigin, ForkChoiceStrategy, ImportedAux, ImportBlock, ImportResult};
|
||||
use consensus_common::import_queue::{SharedBlockImport, SharedJustificationImport};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::result;
|
||||
@@ -40,6 +39,7 @@ use runtime_primitives::ExecutionContext;
|
||||
use substrate_primitives::NativeOrEncoded;
|
||||
|
||||
use authorities::AuthoritySet;
|
||||
use consensus_changes::ConsensusChanges;
|
||||
|
||||
type PeerData =
|
||||
Mutex<
|
||||
@@ -240,6 +240,7 @@ impl Network<Block> for MessageRouting {
|
||||
struct TestApi {
|
||||
genesis_authorities: Vec<(Ed25519AuthorityId, u64)>,
|
||||
scheduled_changes: Arc<Mutex<HashMap<Hash, ScheduledChange<BlockNumber>>>>,
|
||||
forced_changes: Arc<Mutex<HashMap<Hash, (BlockNumber, ScheduledChange<BlockNumber>)>>>,
|
||||
}
|
||||
|
||||
impl TestApi {
|
||||
@@ -247,6 +248,7 @@ impl TestApi {
|
||||
TestApi {
|
||||
genesis_authorities,
|
||||
scheduled_changes: Arc::new(Mutex::new(HashMap::new())),
|
||||
forced_changes: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,6 +351,24 @@ impl GrandpaApi<Block> for RuntimeApi {
|
||||
// extrinsics.
|
||||
Ok(self.inner.scheduled_changes.lock().get(&parent_hash).map(|c| c.clone())).map(NativeOrEncoded::Native)
|
||||
}
|
||||
|
||||
fn grandpa_forced_change_runtime_api_impl(
|
||||
&self,
|
||||
at: &BlockId<Block>,
|
||||
_: ExecutionContext,
|
||||
_: Option<(&DigestFor<Block>)>,
|
||||
_: Vec<u8>,
|
||||
)
|
||||
-> Result<NativeOrEncoded<Option<(NumberFor<Block>, ScheduledChange<NumberFor<Block>>)>>> {
|
||||
let parent_hash = match at {
|
||||
&BlockId::Hash(at) => at,
|
||||
_ => panic!("not requested by block hash!!"),
|
||||
};
|
||||
|
||||
// we take only scheduled changes at given block number where there are no
|
||||
// extrinsics.
|
||||
Ok(self.inner.forced_changes.lock().get(&parent_hash).map(|c| c.clone())).map(NativeOrEncoded::Native)
|
||||
}
|
||||
}
|
||||
|
||||
const TEST_GOSSIP_DURATION: Duration = Duration::from_millis(500);
|
||||
@@ -361,7 +381,14 @@ fn make_ids(keys: &[Keyring]) -> Vec<(Ed25519AuthorityId, u64)> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn run_to_completion(blocks: u64, net: Arc<Mutex<GrandpaTestNet>>, peers: &[Keyring]) -> u64 {
|
||||
// run the voters to completion. provide a closure to be invoked after
|
||||
// the voters are spawned but before blocking on them.
|
||||
fn run_to_completion_with<F: FnOnce()>(
|
||||
blocks: u64,
|
||||
net: Arc<Mutex<GrandpaTestNet>>,
|
||||
peers: &[Keyring],
|
||||
before_waiting: F,
|
||||
) -> u64 {
|
||||
use parking_lot::RwLock;
|
||||
|
||||
let mut finality_notifications = Vec::new();
|
||||
@@ -402,6 +429,7 @@ fn run_to_completion(blocks: u64, net: Arc<Mutex<GrandpaTestNet>>, peers: &[Keyr
|
||||
},
|
||||
link,
|
||||
MessageRouting::new(net.clone(), peer_id),
|
||||
InherentDataProviders::new(),
|
||||
futures::empty(),
|
||||
).expect("all in order with client and network");
|
||||
|
||||
@@ -425,6 +453,8 @@ fn run_to_completion(blocks: u64, net: Arc<Mutex<GrandpaTestNet>>, peers: &[Keyr
|
||||
.map(|_| ())
|
||||
.map_err(|_| ());
|
||||
|
||||
(before_waiting)();
|
||||
|
||||
runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap();
|
||||
|
||||
let highest_finalized = *highest_finalized.read();
|
||||
@@ -432,8 +462,13 @@ fn run_to_completion(blocks: u64, net: Arc<Mutex<GrandpaTestNet>>, peers: &[Keyr
|
||||
highest_finalized
|
||||
}
|
||||
|
||||
fn run_to_completion(blocks: u64, net: Arc<Mutex<GrandpaTestNet>>, peers: &[Keyring]) -> u64 {
|
||||
run_to_completion_with(blocks, net, peers, || {})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finalize_3_voters_no_observers() {
|
||||
let _ = env_logger::try_init();
|
||||
let peers = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
|
||||
let voters = make_ids(peers);
|
||||
|
||||
@@ -495,6 +530,7 @@ fn finalize_3_voters_1_observer() {
|
||||
},
|
||||
link,
|
||||
MessageRouting::new(net.clone(), peer_id),
|
||||
InherentDataProviders::new(),
|
||||
futures::empty(),
|
||||
).expect("all in order with client and network");
|
||||
|
||||
@@ -552,8 +588,9 @@ fn transition_3_voters_twice_1_observer() {
|
||||
assert_eq!(peer.client().info().unwrap().chain.best_number, 1,
|
||||
"Peer #{} failed to sync", i);
|
||||
|
||||
let set_raw = peer.client().backend().get_aux(crate::AUTHORITY_SET_KEY).unwrap().unwrap();
|
||||
let set = AuthoritySet::<Hash, BlockNumber>::decode(&mut &set_raw[..]).unwrap();
|
||||
let set: AuthoritySet<Hash, BlockNumber> = crate::aux_schema::load_authorities(
|
||||
&**peer.client().backend()
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(set.current(), (0, make_ids(peers_a).as_slice()));
|
||||
assert_eq!(set.pending_changes().count(), 0);
|
||||
@@ -638,8 +675,9 @@ fn transition_3_voters_twice_1_observer() {
|
||||
.take_while(|n| Ok(n.header.number() < &30))
|
||||
.for_each(move |_| Ok(()))
|
||||
.map(move |()| {
|
||||
let set_raw = client.backend().get_aux(crate::AUTHORITY_SET_KEY).unwrap().unwrap();
|
||||
let set = AuthoritySet::<Hash, BlockNumber>::decode(&mut &set_raw[..]).unwrap();
|
||||
let set: AuthoritySet<Hash, BlockNumber> = crate::aux_schema::load_authorities(
|
||||
&**client.backend()
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(set.current(), (2, make_ids(peers_c).as_slice()));
|
||||
assert_eq!(set.pending_changes().count(), 0);
|
||||
@@ -654,6 +692,7 @@ fn transition_3_voters_twice_1_observer() {
|
||||
},
|
||||
link,
|
||||
MessageRouting::new(net.clone(), peer_id),
|
||||
InherentDataProviders::new(),
|
||||
futures::empty(),
|
||||
).expect("all in order with client and network");
|
||||
|
||||
@@ -784,7 +823,7 @@ fn sync_justifications_on_change_blocks() {
|
||||
|
||||
#[test]
|
||||
fn finalizes_multiple_pending_changes_in_order() {
|
||||
env_logger::init();
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let peers_a = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
|
||||
let peers_b = &[Keyring::Dave, Keyring::Eve, Keyring::Ferdie];
|
||||
@@ -865,6 +904,60 @@ fn doesnt_vote_on_the_tip_of_the_chain() {
|
||||
assert_eq!(highest, 75);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn force_change_to_new_set() {
|
||||
// two of these guys are offline.
|
||||
let genesis_authorities = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie, Keyring::One, Keyring::Two];
|
||||
let peers_a = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
|
||||
let api = TestApi::new(make_ids(genesis_authorities));
|
||||
|
||||
let voters = make_ids(peers_a);
|
||||
let normal_transitions = api.scheduled_changes.clone();
|
||||
let forced_transitions = api.forced_changes.clone();
|
||||
let net = GrandpaTestNet::new(api, 3);
|
||||
let net = Arc::new(Mutex::new(net));
|
||||
|
||||
let runner_net = net.clone();
|
||||
let add_blocks = move || {
|
||||
net.lock().peer(0).push_blocks(1, false);
|
||||
|
||||
{
|
||||
// add a forced transition at block 12.
|
||||
let parent_hash = net.lock().peer(0).client().info().unwrap().chain.best_hash;
|
||||
forced_transitions.lock().insert(parent_hash, (0, ScheduledChange {
|
||||
next_authorities: voters.clone(),
|
||||
delay: 10,
|
||||
}));
|
||||
|
||||
// add a normal transition too to ensure that forced changes take priority.
|
||||
normal_transitions.lock().insert(parent_hash, ScheduledChange {
|
||||
next_authorities: make_ids(genesis_authorities),
|
||||
delay: 5,
|
||||
});
|
||||
}
|
||||
|
||||
net.lock().peer(0).push_blocks(25, false);
|
||||
net.lock().sync();
|
||||
|
||||
for (i, peer) in net.lock().peers().iter().enumerate() {
|
||||
assert_eq!(peer.client().info().unwrap().chain.best_number, 26,
|
||||
"Peer #{} failed to sync", i);
|
||||
|
||||
let set: AuthoritySet<Hash, BlockNumber> = crate::aux_schema::load_authorities(
|
||||
&**peer.client().backend()
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(set.current(), (1, voters.as_slice()));
|
||||
assert_eq!(set.pending_changes().count(), 0);
|
||||
}
|
||||
};
|
||||
|
||||
// it will only finalize if the forced transition happens.
|
||||
// we add_blocks after the voters are spawned because otherwise
|
||||
// the link-halfs have the wrong AuthoritySet
|
||||
run_to_completion_with(25, runner_net, peers_a, add_blocks);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allows_reimporting_change_blocks() {
|
||||
let peers_a = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
|
||||
@@ -899,7 +992,7 @@ fn allows_reimporting_change_blocks() {
|
||||
|
||||
assert_eq!(
|
||||
block_import.import_block(block(), None).unwrap(),
|
||||
ImportResult::NeedsJustification
|
||||
ImportResult::Imported(ImportedAux { needs_justification: true, clear_justification_requests: false }),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user