grandpa: fix warp sync proof on missing data (#8795)

* grandpa: check for missing data when iterating through authority set changes

* grandpa-warp-sync: handle missing data
This commit is contained in:
André Silva
2021-05-22 18:12:42 +01:00
committed by GitHub
parent 77eba1351a
commit 00328dca24
3 changed files with 39 additions and 19 deletions
@@ -173,4 +173,6 @@ pub enum HandleRequestError {
InvalidProof(String),
#[display(fmt = "Failed to send response.")]
SendResponse,
#[display(fmt = "Missing required data to be able to answer request.")]
MissingData,
}
@@ -91,7 +91,10 @@ impl<Block: BlockT> WarpSyncProof<Block> {
let mut proofs_encoded_len = 0;
let mut proof_limit_reached = false;
for (_, last_block) in set_changes.iter_from(begin_number) {
let set_changes = set_changes.iter_from(begin_number)
.ok_or(HandleRequestError::MissingData)?;
for (_, last_block) in set_changes {
let header = blockchain.header(BlockId::Number(*last_block))?.expect(
"header number comes from previously applied set changes; must exist in db; qed.",
);
@@ -730,18 +730,12 @@ impl<N: Ord + Clone> AuthoritySetChanges<N> {
if idx < self.0.len() {
let (set_id, block_number) = self.0[idx].clone();
// To make sure we have the right set we need to check that the one before it also exists.
if idx > 0 {
let (prev_set_id, _) = self.0[idx - 1usize];
if set_id != prev_set_id + 1u64 {
// Without the preceding set_id we don't have a well-defined start.
return AuthoritySetChangeId::Unknown;
}
} else if set_id != 0 {
// If this is the first index, yet not the first set id then it's not well-defined
// that we are in the right set id.
// if this is the first index but not the first set id then we are missing data.
if idx == 0 && set_id != 0 {
return AuthoritySetChangeId::Unknown;
}
AuthoritySetChangeId::Set(set_id, block_number)
} else {
AuthoritySetChangeId::Unknown
@@ -751,14 +745,23 @@ impl<N: Ord + Clone> AuthoritySetChanges<N> {
/// Returns an iterator over all historical authority set changes starting at the given block
/// number (excluded). The iterator yields a tuple representing the set id and the block number
/// of the last block in that set.
pub fn iter_from(&self, block_number: N) -> impl Iterator<Item = &(u64, N)> {
pub fn iter_from(&self, block_number: N) -> Option<impl Iterator<Item = &(u64, N)>> {
let idx = self.0.binary_search_by_key(&block_number, |(_, n)| n.clone())
// if there was a change at the given block number then we should start on the next
// index since we want to exclude the current block number
.map(|n| n + 1)
.unwrap_or_else(|b| b);
self.0[idx..].iter()
if idx < self.0.len() {
let (set_id, _) = self.0[idx].clone();
// if this is the first index but not the first set id then we are missing data.
if idx == 0 && set_id != 0 {
return None;
}
}
Some(self.0[idx..].iter())
}
}
@@ -1710,26 +1713,38 @@ mod tests {
let mut authority_set_changes = AuthoritySetChanges::empty();
authority_set_changes.append(1, 41);
authority_set_changes.append(2, 81);
// we are missing the data for the first set, therefore we should return `None`
assert_eq!(
None,
authority_set_changes.iter_from(40).map(|it| it.collect::<Vec<_>>()),
);
// after adding the data for the first set the same query should work
let mut authority_set_changes = AuthoritySetChanges::empty();
authority_set_changes.append(0, 21);
authority_set_changes.append(1, 41);
authority_set_changes.append(2, 81);
authority_set_changes.append(3, 121);
assert_eq!(
vec![(1, 41), (2, 81), (3, 121)],
authority_set_changes.iter_from(40).cloned().collect::<Vec<_>>(),
Some(vec![(1, 41), (2, 81), (3, 121)]),
authority_set_changes.iter_from(40).map(|it| it.cloned().collect::<Vec<_>>()),
);
assert_eq!(
vec![(2, 81), (3, 121)],
authority_set_changes.iter_from(41).cloned().collect::<Vec<_>>(),
Some(vec![(2, 81), (3, 121)]),
authority_set_changes.iter_from(41).map(|it| it.cloned().collect::<Vec<_>>()),
);
assert_eq!(
0,
authority_set_changes.iter_from(121).count(),
authority_set_changes.iter_from(121).unwrap().count(),
);
assert_eq!(
0,
authority_set_changes.iter_from(200).count(),
authority_set_changes.iter_from(200).unwrap().count(),
);
}
}