GRANDPA finality proof draft (#1268)

* grandpa finality proof

* prove GrandpaApi::grandpa_authorities using parent block + some docs

* create justification when consensus data is changed

* generate justifications periodically

* test for ConsensusChanges
This commit is contained in:
Svyatoslav Nikolsky
2019-01-11 21:25:03 +03:00
committed by Gav Wood
parent 677b79765b
commit 616716cb4b
18 changed files with 786 additions and 113 deletions
+84 -17
View File
@@ -25,6 +25,7 @@ use tokio::runtime::current_thread;
use keyring::Keyring;
use client::{
BlockchainEvents, error::Result,
blockchain::Backend as BlockchainBackend,
runtime_api::{Core, RuntimeVersion, ApiExt, ConstructRuntimeApi, CallRuntimeAt},
};
use test_client::{self, runtime::BlockNumber};
@@ -332,22 +333,7 @@ fn make_ids(keys: &[Keyring]) -> Vec<(Ed25519AuthorityId, u64)> {
.collect()
}
#[test]
fn finalize_3_voters_no_observers() {
let peers = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
let voters = make_ids(peers);
let mut net = GrandpaTestNet::new(TestApi::new(voters), 3);
net.peer(0).push_blocks(20, false);
net.sync();
for i in 0..3 {
assert_eq!(net.peer(i).client().info().unwrap().chain.best_number, 20,
"Peer #{} failed to sync", i);
}
let net = Arc::new(Mutex::new(net));
fn run_to_completion(blocks: u64, net: Arc<Mutex<GrandpaTestNet>>, peers: &[Keyring]) {
let mut finality_notifications = Vec::new();
let mut runtime = current_thread::Runtime::new().unwrap();
@@ -363,7 +349,7 @@ fn finalize_3_voters_no_observers() {
};
finality_notifications.push(
client.finality_notification_stream()
.take_while(|n| Ok(n.header.number() < &20))
.take_while(|n| Ok(n.header.number() < &blocks))
.for_each(|_| Ok(()))
);
fn assert_send<T: Send>(_: &T) { }
@@ -371,6 +357,7 @@ fn finalize_3_voters_no_observers() {
let voter = run_grandpa(
Config {
gossip_duration: TEST_GOSSIP_DURATION,
justification_period: 32,
local_key: Some(Arc::new(key.clone().into())),
name: Some(format!("peer#{}", peer_id)),
},
@@ -397,6 +384,28 @@ fn finalize_3_voters_no_observers() {
runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap();
}
#[test]
fn finalize_3_voters_no_observers() {
let peers = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
let voters = make_ids(peers);
let mut net = GrandpaTestNet::new(TestApi::new(voters), 3);
net.peer(0).push_blocks(20, false);
net.sync();
for i in 0..3 {
assert_eq!(net.peer(i).client().info().unwrap().chain.best_number, 20,
"Peer #{} failed to sync", i);
}
let net = Arc::new(Mutex::new(net));
run_to_completion(20, net.clone(), peers);
// normally there's no justification for finalized blocks
assert!(net.lock().peer(0).client().backend().blockchain().justification(BlockId::Number(20)).unwrap().is_none(),
"Extra justification for block#1");
}
#[test]
fn finalize_3_voters_1_observer() {
let peers = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
@@ -432,6 +441,7 @@ fn finalize_3_voters_1_observer() {
let voter = run_grandpa(
Config {
gossip_duration: TEST_GOSSIP_DURATION,
justification_period: 32,
local_key,
name: Some(format!("peer#{}", peer_id)),
},
@@ -589,6 +599,7 @@ fn transition_3_voters_twice_1_observer() {
let voter = run_grandpa(
Config {
gossip_duration: TEST_GOSSIP_DURATION,
justification_period: 32,
local_key,
name: Some(format!("peer#{}", peer_id)),
},
@@ -616,3 +627,59 @@ fn transition_3_voters_twice_1_observer() {
runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap();
}
#[test]
fn justification_is_emitted_when_consensus_data_changes() {
let peers = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 3);
// import block#1 WITH consensus data change
let new_authorities = vec![Ed25519AuthorityId::from([42; 32])];
net.peer(0).push_authorities_change_block(new_authorities);
net.sync();
let net = Arc::new(Mutex::new(net));
run_to_completion(1, net.clone(), peers);
// ... and check that there's no justification for block#1
assert!(net.lock().peer(0).client().backend().blockchain().justification(BlockId::Number(1)).unwrap().is_some(),
"Missing justification for block#1");
}
#[test]
fn justification_is_generated_periodically() {
let peers = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
let voters = make_ids(peers);
let mut net = GrandpaTestNet::new(TestApi::new(voters), 3);
net.peer(0).push_blocks(32, false);
net.sync();
let net = Arc::new(Mutex::new(net));
run_to_completion(32, net.clone(), peers);
// when block#32 (justification_period) is finalized, justification
// is required => generated
for i in 0..3 {
assert!(net.lock().peer(i).client().backend().blockchain()
.justification(BlockId::Number(32)).unwrap().is_some());
}
}
#[test]
fn consensus_changes_works() {
let mut changes = ConsensusChanges::<H256, u64>::empty();
// pending changes are not finalized
changes.note_change((10, 1.into()));
assert_eq!(changes.finalize((5, 5.into()), |_| Ok(None)).unwrap(), (false, false));
// no change is selected from competing pending changes
changes.note_change((1, 1.into()));
changes.note_change((1, 101.into()));
assert_eq!(changes.finalize((10, 10.into()), |_| Ok(Some(1001.into()))).unwrap(), (true, false));
// change is selected from competing pending changes
changes.note_change((1, 1.into()));
changes.note_change((1, 101.into()));
assert_eq!(changes.finalize((10, 10.into()), |_| Ok(Some(1.into()))).unwrap(), (true, true));
}