Import multiple authority set change blocks (#1808)

* core: implement logic for tracking dag of possible pending changes

* core: move pending justifications dag to its own crate

* core: remove unnecessary clone bounds on dag

* core: request justifications in-order from the dag

* core: dag: rename changes variables to node

* core: dag: allow finalizing blocks not part of dag

* core: dag: track best finalized number

* core: dag: add more tests

* core: sync: clean up pending justifications dag

* core: dag: derive codec decode encode

* core: dag: better error support

* core: dag: add finalization guarded by predicate

* core: grandpa: track multiple authority set changes in dag

* core: dag: add pre-order iterator

* core: grandpa: request justifications on startup

* core: dag: rearrange order of definitions

* core: rename util/dag to util/fork_tree

* core: fork_tree: add docs

* core: fork_tree: add more tests

* core: fork_tree: fix issues found in tests

* core: grandpa: fix authorities tests

* core: grandpa: add docs for is_descendent_of

* core: sync: add docs for PendingJustifications

* core: sync: add test for justification requests across forks

* core: sync: don't resend import or finality notifications in tests

* core: grandpa: add test for importing multiple change blocks

* core: grandpa: fix logic for checking if a block enacts a change

* core: grandpa: fix authorities tests
This commit is contained in:
André Silva
2019-02-19 23:08:43 +00:00
committed by Gav Wood
parent c5d3da32f2
commit 21779b8cf2
16 changed files with 1322 additions and 273 deletions
+38 -12
View File
@@ -124,6 +124,8 @@ pub struct Peer<D> {
pub import_queue: Box<ImportQueue<Block>>,
network_sender: NetworkChan<Block>,
pub data: D,
best_hash: Mutex<Option<H256>>,
finalized_hash: Mutex<Option<H256>>,
}
impl<D> Peer<D> {
@@ -143,6 +145,8 @@ impl<D> Peer<D> {
network_sender,
network_port,
data,
best_hash: Mutex::new(None),
finalized_hash: Mutex::new(None),
}
}
/// Called after blockchain has been populated to updated current state.
@@ -222,19 +226,39 @@ impl<D> Peer<D> {
/// Send block import notifications.
fn send_import_notifications(&self) {
let info = self.client.info().expect("In-mem client does not fail");
let mut best_hash = self.best_hash.lock();
match *best_hash {
None => {},
Some(hash) if hash != info.chain.best_hash => {},
_ => return,
}
let header = self.client.header(&BlockId::Hash(info.chain.best_hash)).unwrap().unwrap();
let _ = self
.protocol_sender
.send(ProtocolMsg::BlockImported(info.chain.best_hash, header));
*best_hash = Some(info.chain.best_hash);
}
/// Send block finalization notifications.
pub fn send_finality_notifications(&self) {
let info = self.client.info().expect("In-mem client does not fail");
let mut finalized_hash = self.finalized_hash.lock();
match *finalized_hash {
None => {},
Some(hash) if hash != info.chain.finalized_hash => {},
_ => return,
}
let header = self.client.header(&BlockId::Hash(info.chain.finalized_hash)).unwrap().unwrap();
let _ = self
.protocol_sender
.send(ProtocolMsg::BlockFinalized(info.chain.finalized_hash, header.clone()));
*finalized_hash = Some(info.chain.finalized_hash);
}
/// Restart sync for a peer.
@@ -296,7 +320,7 @@ impl<D> Peer<D> {
}
/// Add blocks to the peer -- edit the block before adding
pub fn generate_blocks<F>(&self, count: usize, origin: BlockOrigin, edit_block: F)
pub fn generate_blocks<F>(&self, count: usize, origin: BlockOrigin, edit_block: F) -> H256
where F: FnMut(BlockBuilder<Block, PeersClient>) -> Block
{
let best_hash = self.client.info().unwrap().chain.best_hash;
@@ -305,11 +329,12 @@ impl<D> Peer<D> {
/// Add blocks to the peer -- edit the block before adding. The chain will
/// start at the given block iD.
pub fn generate_blocks_at<F>(&self, mut at: BlockId<Block>, count: usize, origin: BlockOrigin, mut edit_block: F)
pub fn generate_blocks_at<F>(&self, at: BlockId<Block>, count: usize, origin: BlockOrigin, mut edit_block: F) -> H256
where F: FnMut(BlockBuilder<Block, PeersClient>) -> Block
{
let mut at = self.client.header(&at).unwrap().unwrap().hash();
for _ in 0..count {
let builder = self.client.new_block_at(&at).unwrap();
let builder = self.client.new_block_at(&BlockId::Hash(at)).unwrap();
let block = edit_block(builder);
let hash = block.header.hash();
trace!(
@@ -319,7 +344,7 @@ impl<D> Peer<D> {
block.header.parent_hash
);
let header = block.header.clone();
at = BlockId::Hash(hash);
at = hash;
self.import_queue.import_blocks(
origin,
@@ -336,17 +361,18 @@ impl<D> Peer<D> {
thread::sleep(Duration::from_millis(20));
}
}
at
}
/// Push blocks to the peer (simplified: with or without a TX)
pub fn push_blocks(&self, count: usize, with_tx: bool) {
pub fn push_blocks(&self, count: usize, with_tx: bool) -> H256 {
let best_hash = self.client.info().unwrap().chain.best_hash;
self.push_blocks_at(BlockId::Hash(best_hash), count, with_tx);
self.push_blocks_at(BlockId::Hash(best_hash), count, with_tx)
}
/// Push blocks to the peer (simplified: with or without a TX) starting from
/// given hash.
pub fn push_blocks_at(&self, at: BlockId<Block>, count: usize, with_tx: bool) {
pub fn push_blocks_at(&self, at: BlockId<Block>, count: usize, with_tx: bool) -> H256 {
let mut nonce = 0;
if with_tx {
self.generate_blocks_at(at, count, BlockOrigin::File, |mut builder| {
@@ -360,17 +386,17 @@ impl<D> Peer<D> {
builder.push(Extrinsic::Transfer(transfer, signature)).unwrap();
nonce = nonce + 1;
builder.bake().unwrap()
});
})
} else {
self.generate_blocks_at(at, count, BlockOrigin::File, |builder| builder.bake().unwrap());
self.generate_blocks_at(at, count, BlockOrigin::File, |builder| builder.bake().unwrap())
}
}
pub fn push_authorities_change_block(&self, new_authorities: Vec<Ed25519AuthorityId>) {
pub fn push_authorities_change_block(&self, new_authorities: Vec<Ed25519AuthorityId>) -> H256 {
self.generate_blocks(1, BlockOrigin::File, |mut builder| {
builder.push(Extrinsic::AuthoritiesChange(new_authorities.clone())).unwrap();
builder.bake().unwrap()
});
})
}
/// Get a reference to the client.
@@ -571,7 +597,7 @@ pub trait TestNetFactory: Sized {
let mut done = 0;
loop {
if done > 10 { break; }
if done > 3 { break; }
if self.done() {
done += 1;
} else {
+38 -3
View File
@@ -80,11 +80,46 @@ fn sync_justifications() {
assert_eq!(net.peer(0).client().justification(&BlockId::Number(10)).unwrap(), None);
assert_eq!(net.peer(1).client().justification(&BlockId::Number(10)).unwrap(), None);
// we finalize block #10 for peer 0 with a justification
// we finalize block #10, #15 and #20 for peer 0 with a justification
net.peer(0).client().finalize_block(BlockId::Number(10), Some(Vec::new()), true).unwrap();
net.peer(0).client().finalize_block(BlockId::Number(15), Some(Vec::new()), true).unwrap();
net.peer(0).client().finalize_block(BlockId::Number(20), Some(Vec::new()), true).unwrap();
let header = net.peer(1).client().header(&BlockId::Number(10)).unwrap().unwrap();
net.peer(1).request_justification(&header.hash().into(), 10);
let h1 = net.peer(1).client().header(&BlockId::Number(10)).unwrap().unwrap();
let h2 = net.peer(1).client().header(&BlockId::Number(15)).unwrap().unwrap();
let h3 = net.peer(1).client().header(&BlockId::Number(20)).unwrap().unwrap();
// peer 1 should get the justifications from the network
net.peer(1).request_justification(&h1.hash().into(), 10);
net.peer(1).request_justification(&h2.hash().into(), 15);
net.peer(1).request_justification(&h3.hash().into(), 20);
net.sync();
for height in (10..21).step_by(5) {
assert_eq!(net.peer(0).client().justification(&BlockId::Number(height)).unwrap(), Some(Vec::new()));
assert_eq!(net.peer(1).client().justification(&BlockId::Number(height)).unwrap(), Some(Vec::new()));
}
}
#[test]
fn sync_justifications_across_forks() {
let _ = ::env_logger::try_init();
let mut net = JustificationTestNet::new(3);
// we push 5 blocks
net.peer(0).push_blocks(5, false);
// and then two forks 5 and 6 blocks long
let f1_best = net.peer(0).push_blocks_at(BlockId::Number(5), 5, false);
let f2_best = net.peer(0).push_blocks_at(BlockId::Number(5), 6, false);
// peer 1 will only see the longer fork. but we'll request justifications
// for both and finalize the small fork instead.
net.sync();
net.peer(0).client().finalize_block(BlockId::Hash(f1_best), Some(Vec::new()), true).unwrap();
net.peer(1).request_justification(&f1_best, 10);
net.peer(1).request_justification(&f2_best, 11);
net.sync();