grandpa: round catchup messages (#2801)

* grandpa: initial structure for catch up messages

* grandpa: answer catch up requests

* grandpa: inject catch up messages into global stream

* grandpa: keep track of pending catch up request

* grandpa: block catchup until all referenced blocks are imported

* grandpa: unify catch up and commit streams

* grandpa: simplify communication stream/sink types

* grandpa: note gossip validator on catch up message import

* grandpa: fix cost on catch up message validation

* grandpa: check signatures on catch up messages

* grandpa: clean up catch up request handling state

* grandpa: adjust costs on invalid catch up requests

* grandpa: release lock before pushing catch up message

* grandpa: validate catch up request against peer view

* grandpa: catch up docs

* grandpa: fix tests

* grandpa: until_imported: add tests for catch up messages

* grandpa: add tests for catch up message gossip validation

* grandpa: integrate HistoricalVotes changes

* grandpa: add test for neighbor packet triggering catch up

* grandpa: add test for full voter catch up

* grandpa: depend on finality-grandpa 0.8 from crates

* granda: use finality-grandpa test helpers

* grandpa: add PSM cost for answering catch up requests

* grandpa: code style fixes

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

* grandpa: more trailing commas

* grandpa: lower cost of invalid catch up requests near set change

* grandpa: process catch up sending on import of neighbor message

* grandpa: add comments on HistoricalVotes

* grandpa: use finality-grandpa v0.8.1 from crates.io

* grandpa: fix test compilation
This commit is contained in:
André Silva
2019-07-04 20:40:16 +01:00
committed by GitHub
parent 90f214f000
commit d5bc7325b9
11 changed files with 1381 additions and 240 deletions
+118 -1
View File
@@ -1181,6 +1181,7 @@ fn voter_persists_its_votes() {
// sender is dropped the voter is stopped.
{
let net = net.clone();
let client = client.clone();
let voter = future::loop_fn(voter_rx, move |rx| {
let (_block_import, _, _, _, link) = net.lock().make_block_import(client.clone());
@@ -1244,11 +1245,19 @@ fn voter_persists_its_votes() {
local_key: Some(Arc::new(peers[1].clone().into())),
name: Some(format!("peer#{}", 1)),
};
let set_state = {
let (_, _, _, _, link) = net.lock().make_block_import(client);
let LinkHalf { persistent_data, .. } = link.lock().take().unwrap();
let PersistentData { set_state, .. } = persistent_data;
set_state
};
let routing = MessageRouting::new(net.clone(), 1);
let (network, routing_work) = communication::NetworkBridge::new(
routing,
config.clone(),
None,
set_state,
Exit,
);
runtime.block_on(routing_work).unwrap();
@@ -1480,3 +1489,111 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ
if FORCE_CHANGE { 0 } else { 10 },
);
}
#[test]
fn voter_catches_up_to_latest_round_when_behind() {
let _ = env_logger::try_init();
let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob];
let voters = make_ids(peers);
let mut net = GrandpaTestNet::new(TestApi::new(voters), 3);
net.peer(0).push_blocks(50, false);
net.sync();
let net = Arc::new(Mutex::new(net));
let mut finality_notifications = Vec::new();
let mut runtime = current_thread::Runtime::new().unwrap();
let voter = |local_key, peer_id, link, net| -> Box<dyn Future<Item=(), Error=()> + Send> {
let grandpa_params = GrandpaParams {
config: Config {
gossip_duration: TEST_GOSSIP_DURATION,
justification_period: 32,
local_key,
name: Some(format!("peer#{}", peer_id)),
},
link: link,
network: MessageRouting::new(net, peer_id),
inherent_data_providers: InherentDataProviders::new(),
on_exit: Exit,
telemetry_on_connect: None,
};
Box::new(run_grandpa_voter(grandpa_params).expect("all in order with client and network"))
};
// spawn authorities
for (peer_id, key) in peers.iter().enumerate() {
let (client, link) = {
let net = net.lock();
let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
(
net.peers[peer_id].client().clone(),
link,
)
};
finality_notifications.push(
client.finality_notification_stream()
.take_while(|n| Ok(n.header.number() < &50))
.for_each(move |_| Ok(()))
);
let voter = voter(Some(Arc::new((*key).into())), peer_id, link, net.clone());
runtime.spawn(voter);
}
// wait for them to finalize block 50. since they'll vote on 3/4 of the
// unfinalized chain it will take at least 4 rounds to do it.
let wait_for_finality = ::futures::future::join_all(finality_notifications)
.map(|_| ())
.map_err(|_| ());
// spawn a new voter, it should be behind by at least 4 rounds and should be
// able to catch up to the latest round
let test = {
let net = net.clone();
let runtime = runtime.handle();
wait_for_finality.and_then(move |_| {
let peer_id = 2;
let (client, link) = {
let net = net.lock();
let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
(
net.peers[peer_id].client().clone(),
link,
)
};
let set_state = link.persistent_data.set_state.clone();
let wait = client.finality_notification_stream()
.take_while(|n| Ok(n.header.number() < &50))
.collect()
.map(|_| set_state);
let voter = voter(None, peer_id, link, net);
runtime.spawn(voter).unwrap();
wait
})
.and_then(|set_state| {
// the last completed round in the new voter is higher than 4
// which means it caught up to the voters
assert!(set_state.read().last_completed_round().number >= 4);
Ok(())
})
};
let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL)
.for_each(move |_| { net.lock().sync_without_disconnects(); Ok(()) })
.map(|_| ())
.map_err(|_| ());
let _ = runtime.block_on(test.select(drive_to_completion).map_err(|_| ())).unwrap();
}