mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-01 17:07:24 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user