mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-15 16:11:05 +00:00
babe: verify slots are strictly increasing (#3785)
* babe: re-use code to propose and import test block * babe: add failing test for slot validation * babe: verify slot numbers are strictly increasing
This commit is contained in:
committed by
Robert Habermeier
parent
8108f1decd
commit
984c6ac839
@@ -795,6 +795,31 @@ impl<B, E, Block, I, RA, PRA> BlockImport<Block> for BabeBlockImport<B, E, Block
|
||||
header has been already verified; qed");
|
||||
let slot_number = pre_digest.slot_number();
|
||||
|
||||
let parent_hash = *block.header.parent_hash();
|
||||
let parent_header = self.client.header(&BlockId::Hash(parent_hash))
|
||||
.map_err(|e| ConsensusError::ChainLookup(e.to_string()))?
|
||||
.ok_or_else(|| ConsensusError::ChainLookup(babe_err!(
|
||||
"Parent ({}) of {} unavailable. Cannot import",
|
||||
parent_hash,
|
||||
hash
|
||||
)))?;
|
||||
|
||||
let parent_slot = find_pre_digest::<Block::Header>(&parent_header)
|
||||
.map(|d| d.slot_number())
|
||||
.expect("parent is non-genesis; valid BABE headers contain a pre-digest; \
|
||||
header has already been verified; qed");
|
||||
|
||||
// make sure that slot number is strictly increasing
|
||||
if slot_number <= parent_slot {
|
||||
return Err(
|
||||
ConsensusError::ClientImport(babe_err!(
|
||||
"Slot number must increase: parent slot: {}, this slot: {}",
|
||||
parent_slot,
|
||||
slot_number
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
let mut epoch_changes = self.epoch_changes.lock();
|
||||
|
||||
// check if there's any epoch change expected to happen at this slot.
|
||||
@@ -803,20 +828,6 @@ impl<B, E, Block, I, RA, PRA> BlockImport<Block> for BabeBlockImport<B, E, Block
|
||||
//
|
||||
// also provides the total weight of the chain, including the imported block.
|
||||
let (epoch, first_in_epoch, parent_weight) = {
|
||||
let parent_hash = *block.header.parent_hash();
|
||||
let parent_header = self.client.header(&BlockId::Hash(parent_hash))
|
||||
.map_err(|e| ConsensusError::ChainLookup(e.to_string()))?
|
||||
.ok_or_else(|| ConsensusError::ChainLookup(babe_err!(
|
||||
"Parent ({}) of {} unavailable. Cannot import",
|
||||
parent_hash,
|
||||
hash
|
||||
)))?;
|
||||
|
||||
let parent_slot = find_pre_digest::<Block::Header>(&parent_header)
|
||||
.map(|d| d.slot_number())
|
||||
.expect("parent is non-genesis; valid BABE headers contain a pre-digest; \
|
||||
header has already been verified; qed");
|
||||
|
||||
let parent_weight = if *parent_header.number() == Zero::zero() {
|
||||
0
|
||||
} else {
|
||||
|
||||
@@ -525,35 +525,33 @@ fn can_author_block() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn importing_block_one_sets_genesis_epoch() {
|
||||
let mut net = BabeTestNet::new(1);
|
||||
// Propose and import a new BABE block on top of the given parent.
|
||||
fn propose_and_import_block(
|
||||
parent: &TestHeader,
|
||||
slot_number: Option<SlotNumber>,
|
||||
proposer_factory: &mut DummyFactory,
|
||||
block_import: &mut BoxBlockImport<TestBlock>,
|
||||
) -> H256 {
|
||||
let mut proposer = proposer_factory.init(parent).unwrap();
|
||||
|
||||
let peer = net.peer(0);
|
||||
let data = peer.data.as_ref().expect("babe link set up during initialization");
|
||||
let client = peer.client().as_full().expect("Only full clients are used in tests").clone();
|
||||
|
||||
let mut environ = DummyFactory {
|
||||
client: client.clone(),
|
||||
config: data.link.config.clone(),
|
||||
epoch_changes: data.link.epoch_changes.clone(),
|
||||
mutator: Arc::new(|_, _| ()),
|
||||
};
|
||||
|
||||
let genesis_header = client.header(&BlockId::Number(0)).unwrap().unwrap();
|
||||
|
||||
let mut proposer = environ.init(&genesis_header).unwrap();
|
||||
let babe_claim = Item::babe_pre_digest(babe_primitives::BabePreDigest::Secondary {
|
||||
authority_index: 0,
|
||||
slot_number: 999,
|
||||
let slot_number = slot_number.unwrap_or_else(|| {
|
||||
let parent_pre_digest = find_pre_digest(parent).unwrap();
|
||||
parent_pre_digest.slot_number() + 1
|
||||
});
|
||||
let pre_digest = sr_primitives::generic::Digest { logs: vec![babe_claim] };
|
||||
|
||||
let genesis_epoch = data.link.config.genesis_epoch(999);
|
||||
let pre_digest = sr_primitives::generic::Digest {
|
||||
logs: vec![
|
||||
Item::babe_pre_digest(
|
||||
BabePreDigest::Secondary {
|
||||
authority_index: 0,
|
||||
slot_number,
|
||||
},
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
let mut block = futures::executor::block_on(proposer.propose_with(pre_digest)).unwrap();
|
||||
|
||||
// seal by alice.
|
||||
let seal = {
|
||||
// sign the pre-sealed hash of the block and then
|
||||
// add it to a digest item.
|
||||
@@ -569,18 +567,14 @@ fn importing_block_one_sets_genesis_epoch() {
|
||||
block.header.digest_mut().pop();
|
||||
h
|
||||
};
|
||||
assert_eq!(*block.header.number(), 1);
|
||||
let (header, body) = block.deconstruct();
|
||||
|
||||
let post_digests = vec![seal];
|
||||
let mut block_import = data.block_import.lock().take().expect("import set up during init");
|
||||
block_import.import_block(
|
||||
let import_result = block_import.import_block(
|
||||
BlockImportParams {
|
||||
origin: BlockOrigin::Own,
|
||||
header,
|
||||
header: block.header,
|
||||
justification: None,
|
||||
post_digests,
|
||||
body: Some(body),
|
||||
post_digests: vec![seal],
|
||||
body: Some(block.extrinsics),
|
||||
finalized: false,
|
||||
auxiliary: Vec::new(),
|
||||
fork_choice: ForkChoiceStrategy::LongestChain,
|
||||
@@ -588,14 +582,51 @@ fn importing_block_one_sets_genesis_epoch() {
|
||||
Default::default(),
|
||||
).unwrap();
|
||||
|
||||
match import_result {
|
||||
ImportResult::Imported(_) => {},
|
||||
_ => panic!("expected block to be imported"),
|
||||
}
|
||||
|
||||
post_hash
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn importing_block_one_sets_genesis_epoch() {
|
||||
let mut net = BabeTestNet::new(1);
|
||||
|
||||
let peer = net.peer(0);
|
||||
let data = peer.data.as_ref().expect("babe link set up during initialization");
|
||||
let client = peer.client().as_full().expect("Only full clients are used in tests").clone();
|
||||
|
||||
let mut proposer_factory = DummyFactory {
|
||||
client: client.clone(),
|
||||
config: data.link.config.clone(),
|
||||
epoch_changes: data.link.epoch_changes.clone(),
|
||||
mutator: Arc::new(|_, _| ()),
|
||||
};
|
||||
|
||||
let mut block_import = data.block_import.lock().take().expect("import set up during init");
|
||||
|
||||
let genesis_header = client.header(&BlockId::Number(0)).unwrap().unwrap();
|
||||
|
||||
let block_hash = propose_and_import_block(
|
||||
&genesis_header,
|
||||
Some(999),
|
||||
&mut proposer_factory,
|
||||
&mut block_import,
|
||||
);
|
||||
|
||||
let genesis_epoch = data.link.config.genesis_epoch(999);
|
||||
|
||||
let epoch_changes = data.link.epoch_changes.lock();
|
||||
let epoch_for_second_block = epoch_changes.epoch_for_child_of(
|
||||
descendent_query(&*client),
|
||||
&post_hash,
|
||||
&block_hash,
|
||||
1,
|
||||
1000,
|
||||
|slot| data.link.config.genesis_epoch(slot),
|
||||
).unwrap().unwrap().into_inner();
|
||||
|
||||
assert_eq!(epoch_for_second_block, genesis_epoch);
|
||||
}
|
||||
|
||||
@@ -612,81 +643,28 @@ fn importing_epoch_change_block_prunes_tree() {
|
||||
let mut block_import = data.block_import.lock().take().expect("import set up during init");
|
||||
let epoch_changes = data.link.epoch_changes.clone();
|
||||
|
||||
// This is just boilerplate code for proposing and importing a valid BABE
|
||||
// block that's built on top of the given parent. The proposer takes care
|
||||
// of producing epoch change digests according to the epoch duration (which
|
||||
// is set to 6 slots in the test runtime).
|
||||
let mut propose_and_import_block = |parent_header| {
|
||||
let mut environ = DummyFactory {
|
||||
client: client.clone(),
|
||||
config: data.link.config.clone(),
|
||||
epoch_changes: data.link.epoch_changes.clone(),
|
||||
mutator: Arc::new(|_, _| ()),
|
||||
};
|
||||
|
||||
let mut proposer = environ.init(&parent_header).unwrap();
|
||||
let parent_pre_digest = find_pre_digest(&parent_header).unwrap();
|
||||
|
||||
let pre_digest = sr_primitives::generic::Digest {
|
||||
logs: vec![
|
||||
Item::babe_pre_digest(
|
||||
BabePreDigest::Secondary {
|
||||
authority_index: 0,
|
||||
slot_number: parent_pre_digest.slot_number() + 1,
|
||||
},
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
let mut block = futures::executor::block_on(proposer.propose_with(pre_digest)).unwrap();
|
||||
|
||||
let seal = {
|
||||
// sign the pre-sealed hash of the block and then
|
||||
// add it to a digest item.
|
||||
let pair = AuthorityPair::from_seed(&[1; 32]);
|
||||
let pre_hash = block.header.hash();
|
||||
let signature = pair.sign(pre_hash.as_ref());
|
||||
Item::babe_seal(signature)
|
||||
};
|
||||
|
||||
let post_hash = {
|
||||
block.header.digest_mut().push(seal.clone());
|
||||
let h = block.header.hash();
|
||||
block.header.digest_mut().pop();
|
||||
h
|
||||
};
|
||||
|
||||
let next_epoch_digest =
|
||||
find_next_epoch_digest::<Block>(&block.header).unwrap();
|
||||
|
||||
let import_result = block_import.import_block(
|
||||
BlockImportParams {
|
||||
origin: BlockOrigin::Own,
|
||||
header: block.header,
|
||||
justification: None,
|
||||
post_digests: vec![seal],
|
||||
body: Some(block.extrinsics),
|
||||
finalized: false,
|
||||
auxiliary: Vec::new(),
|
||||
fork_choice: ForkChoiceStrategy::LongestChain,
|
||||
},
|
||||
Default::default(),
|
||||
).unwrap();
|
||||
|
||||
match import_result {
|
||||
ImportResult::Imported(_) => {},
|
||||
_ => panic!("expected block to be imported"),
|
||||
}
|
||||
|
||||
(post_hash, next_epoch_digest)
|
||||
let mut proposer_factory = DummyFactory {
|
||||
client: client.clone(),
|
||||
config: data.link.config.clone(),
|
||||
epoch_changes: data.link.epoch_changes.clone(),
|
||||
mutator: Arc::new(|_, _| ()),
|
||||
};
|
||||
|
||||
// This is just boilerplate code for proposing and importing n valid BABE
|
||||
// blocks that are built on top of the given parent. The proposer takes care
|
||||
// of producing epoch change digests according to the epoch duration (which
|
||||
// is set to 6 slots in the test runtime).
|
||||
let mut propose_and_import_blocks = |parent_id, n| {
|
||||
let mut hashes = Vec::new();
|
||||
let mut parent_header = client.header(&parent_id).unwrap().unwrap();
|
||||
|
||||
for _ in 0..n {
|
||||
let (block_hash, _) = propose_and_import_block(parent_header);
|
||||
let block_hash = propose_and_import_block(
|
||||
&parent_header,
|
||||
None,
|
||||
&mut proposer_factory,
|
||||
&mut block_import,
|
||||
);
|
||||
hashes.push(block_hash);
|
||||
parent_header = client.header(&BlockId::Hash(block_hash)).unwrap().unwrap();
|
||||
}
|
||||
@@ -758,3 +736,43 @@ fn importing_epoch_change_block_prunes_tree() {
|
||||
epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_3.contains(h)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn verify_slots_are_strictly_increasing() {
|
||||
let mut net = BabeTestNet::new(1);
|
||||
|
||||
let peer = net.peer(0);
|
||||
let data = peer.data.as_ref().expect("babe link set up during initialization");
|
||||
|
||||
let client = peer.client().as_full().expect("Only full clients are used in tests").clone();
|
||||
let mut block_import = data.block_import.lock().take().expect("import set up during init");
|
||||
|
||||
let mut proposer_factory = DummyFactory {
|
||||
client: client.clone(),
|
||||
config: data.link.config.clone(),
|
||||
epoch_changes: data.link.epoch_changes.clone(),
|
||||
mutator: Arc::new(|_, _| ()),
|
||||
};
|
||||
|
||||
let genesis_header = client.header(&BlockId::Number(0)).unwrap().unwrap();
|
||||
|
||||
// we should have no issue importing this block
|
||||
let b1 = propose_and_import_block(
|
||||
&genesis_header,
|
||||
Some(999),
|
||||
&mut proposer_factory,
|
||||
&mut block_import,
|
||||
);
|
||||
|
||||
let b1 = client.header(&BlockId::Hash(b1)).unwrap().unwrap();
|
||||
|
||||
// we should fail to import this block since the slot number didn't increase.
|
||||
// we will panic due to the `PanickingBlockImport` defined above.
|
||||
propose_and_import_block(
|
||||
&b1,
|
||||
Some(999),
|
||||
&mut proposer_factory,
|
||||
&mut block_import,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user