Wire Finality Verifier Pallet Into Runtimes (#696)

* Add Finality Verifier pallet to runtimes

* Implement simple ancestry checker

* Use the new checker in runtimes

* Remove unused import warning

* Bump max allowed ancestry proof size

* Add a few optimization suggestions when verifying ancestry

* Use session length as upper bound for ancestry proof size

* Remove unused time units
This commit is contained in:
Hernando Castano
2021-02-03 14:33:49 -05:00
committed by Bastian Köcher
parent d73100cb30
commit 8c7d0ca35d
9 changed files with 183 additions and 25 deletions
+100
View File
@@ -27,7 +27,9 @@ use core::fmt::Debug;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_finality_grandpa::{AuthorityList, SetId};
use sp_runtime::traits::Header as HeaderT;
use sp_runtime::RuntimeDebug;
use sp_std::vec::Vec;
pub mod justification;
@@ -110,3 +112,101 @@ impl<H, P> AncestryChecker<H, P> for () {
true
}
}
/// A simple ancestry checker which verifies ancestry by walking every header between `child` and
/// `ancestor`.
pub struct LinearAncestryChecker;
impl<H: HeaderT> AncestryChecker<H, Vec<H>> for LinearAncestryChecker {
fn are_ancestors(ancestor: &H, child: &H, proof: &Vec<H>) -> bool {
// You can't be your own parent
if proof.len() < 2 {
return false;
}
// Let's make sure that the given headers are actually in the proof
match proof.first() {
Some(first) if first == ancestor => {}
_ => return false,
}
match proof.last() {
Some(last) if last == child => {}
_ => return false,
}
// Now we actually check the proof
for i in 1..proof.len() {
if &proof[i - 1].hash() != proof[i].parent_hash() {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use bp_test_utils::test_header;
use sp_runtime::testing::Header;
#[test]
fn can_verify_ancestry_correctly() {
let ancestor: Header = test_header(1);
let header2: Header = test_header(2);
let header3: Header = test_header(3);
let child: Header = test_header(4);
let ancestry_proof = vec![ancestor.clone(), header2, header3, child.clone()];
assert!(LinearAncestryChecker::are_ancestors(&ancestor, &child, &ancestry_proof));
}
#[test]
fn does_not_verify_invalid_proof() {
let ancestor: Header = test_header(1);
let header2: Header = test_header(2);
let header3: Header = test_header(3);
let child: Header = test_header(4);
let ancestry_proof = vec![ancestor.clone(), header3, header2, child.clone()];
let invalid = !LinearAncestryChecker::are_ancestors(&ancestor, &child, &ancestry_proof);
assert!(invalid);
}
#[test]
fn header_is_not_allowed_to_be_its_own_ancestor() {
let ancestor: Header = test_header(1);
let child: Header = ancestor.clone();
let ancestry_proof = vec![ancestor.clone()];
let invalid = !LinearAncestryChecker::are_ancestors(&ancestor, &child, &ancestry_proof);
assert!(invalid);
}
#[test]
fn proof_is_considered_invalid_if_child_and_ancestor_do_not_match() {
let ancestor: Header = test_header(1);
let header2: Header = test_header(2);
let header3: Header = test_header(3);
let child: Header = test_header(4);
let ancestry_proof = vec![ancestor, header3.clone(), header2.clone(), child];
let invalid = !LinearAncestryChecker::are_ancestors(&header2, &header3, &ancestry_proof);
assert!(invalid);
}
#[test]
fn empty_proof_is_invalid() {
let ancestor: Header = test_header(1);
let child: Header = ancestor.clone();
let ancestry_proof = vec![];
let invalid = !LinearAncestryChecker::are_ancestors(&ancestor, &child, &ancestry_proof);
assert!(invalid);
}
}