From 658e4e9b5c9ca6020155bd13ae7c1d342a312050 Mon Sep 17 00:00:00 2001
From: Hernando Castano <HCastano@users.noreply.github.com>
Date: Fri, 26 Feb 2021 12:57:06 -0500
Subject: [PATCH] Use No-Op Ancestry Checker (#755)

* Use no-op ancestry checker

* Check that current header height is greater than last finalized

* Ensure that incoming headers are strictly greater than last finalized

* Ensure that header numbers always increase in tests
---
 bridges/bin/millau/runtime/src/lib.rs        |  4 +-
 bridges/bin/rialto/runtime/src/lib.rs        |  4 +-
 bridges/modules/finality-verifier/src/lib.rs | 38 ++++++++---------
 bridges/modules/substrate/src/lib.rs         | 44 ++++++++++++++++----
 bridges/primitives/header-chain/src/lib.rs   |  6 ++-
 5 files changed, 64 insertions(+), 32 deletions(-)

diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs
index bee7881f26d..89440184002 100644
--- a/bridges/bin/millau/runtime/src/lib.rs
+++ b/bridges/bin/millau/runtime/src/lib.rs
@@ -313,8 +313,8 @@ parameter_types! {
 impl pallet_finality_verifier::Config for Runtime {
 	type BridgedChain = bp_rialto::Rialto;
 	type HeaderChain = pallet_substrate_bridge::Module<Runtime>;
-	type AncestryProof = Vec<bp_rialto::Header>;
-	type AncestryChecker = bp_header_chain::LinearAncestryChecker;
+	type AncestryProof = ();
+	type AncestryChecker = ();
 	type MaxRequests = MaxRequests;
 }
 
diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs
index af1d7c6c9fa..6bb73ef037a 100644
--- a/bridges/bin/rialto/runtime/src/lib.rs
+++ b/bridges/bin/rialto/runtime/src/lib.rs
@@ -420,8 +420,8 @@ parameter_types! {
 impl pallet_finality_verifier::Config for Runtime {
 	type BridgedChain = bp_millau::Millau;
 	type HeaderChain = pallet_substrate_bridge::Module<Runtime>;
-	type AncestryProof = Vec<bp_millau::Header>;
-	type AncestryChecker = bp_header_chain::LinearAncestryChecker;
+	type AncestryProof = ();
+	type AncestryChecker = ();
 	type MaxRequests = MaxRequests;
 }
 
diff --git a/bridges/modules/finality-verifier/src/lib.rs b/bridges/modules/finality-verifier/src/lib.rs
index 4e05c5754d9..8f3505252ec 100644
--- a/bridges/modules/finality-verifier/src/lib.rs
+++ b/bridges/modules/finality-verifier/src/lib.rs
@@ -142,7 +142,7 @@ pub mod pallet {
 				<Error<T>>::InvalidAncestryProof
 			);
 
-			T::HeaderChain::append_header(finality_target);
+			let _ = T::HeaderChain::append_header(finality_target)?;
 			frame_support::debug::info!("Succesfully imported finalized header with hash {:?}!", hash);
 
 			<RequestCount<T>>::mutate(|count| *count += 1);
@@ -203,9 +203,9 @@ mod tests {
 		));
 	}
 
-	fn submit_finality_proof() -> frame_support::dispatch::DispatchResultWithPostInfo {
-		let child = test_header(1);
-		let header = test_header(2);
+	fn submit_finality_proof(child: u8, header: u8) -> frame_support::dispatch::DispatchResultWithPostInfo {
+		let child = test_header(child.into());
+		let header = test_header(header.into());
 
 		let set_id = 1;
 		let grandpa_round = 1;
@@ -228,7 +228,7 @@ mod tests {
 		run_test(|| {
 			initialize_substrate_bridge();
 
-			assert_ok!(submit_finality_proof());
+			assert_ok!(submit_finality_proof(1, 2));
 
 			let header = test_header(2);
 			assert_eq!(
@@ -337,9 +337,9 @@ mod tests {
 	fn disallows_imports_once_limit_is_hit_in_single_block() {
 		run_test(|| {
 			initialize_substrate_bridge();
-			assert_ok!(submit_finality_proof());
-			assert_ok!(submit_finality_proof());
-			assert_err!(submit_finality_proof(), <Error<TestRuntime>>::TooManyRequests);
+			assert_ok!(submit_finality_proof(1, 2));
+			assert_ok!(submit_finality_proof(3, 4));
+			assert_err!(submit_finality_proof(5, 6), <Error<TestRuntime>>::TooManyRequests);
 		})
 	}
 
@@ -379,11 +379,11 @@ mod tests {
 	fn allows_request_after_new_block_has_started() {
 		run_test(|| {
 			initialize_substrate_bridge();
-			assert_ok!(submit_finality_proof());
-			assert_ok!(submit_finality_proof());
+			assert_ok!(submit_finality_proof(1, 2));
+			assert_ok!(submit_finality_proof(3, 4));
 
 			next_block();
-			assert_ok!(submit_finality_proof());
+			assert_ok!(submit_finality_proof(5, 6));
 		})
 	}
 
@@ -391,12 +391,12 @@ mod tests {
 	fn disallows_imports_once_limit_is_hit_across_different_blocks() {
 		run_test(|| {
 			initialize_substrate_bridge();
-			assert_ok!(submit_finality_proof());
-			assert_ok!(submit_finality_proof());
+			assert_ok!(submit_finality_proof(1, 2));
+			assert_ok!(submit_finality_proof(3, 4));
 
 			next_block();
-			assert_ok!(submit_finality_proof());
-			assert_err!(submit_finality_proof(), <Error<TestRuntime>>::TooManyRequests);
+			assert_ok!(submit_finality_proof(5, 6));
+			assert_err!(submit_finality_proof(7, 8), <Error<TestRuntime>>::TooManyRequests);
 		})
 	}
 
@@ -404,15 +404,15 @@ mod tests {
 	fn allows_max_requests_after_long_time_with_no_activity() {
 		run_test(|| {
 			initialize_substrate_bridge();
-			assert_ok!(submit_finality_proof());
-			assert_ok!(submit_finality_proof());
+			assert_ok!(submit_finality_proof(1, 2));
+			assert_ok!(submit_finality_proof(3, 4));
 
 			next_block();
 			next_block();
 
 			next_block();
-			assert_ok!(submit_finality_proof());
-			assert_ok!(submit_finality_proof());
+			assert_ok!(submit_finality_proof(5, 6));
+			assert_ok!(submit_finality_proof(7, 8));
 		})
 	}
 }
diff --git a/bridges/modules/substrate/src/lib.rs b/bridges/modules/substrate/src/lib.rs
index c14db8596f8..1ae23fdf91b 100644
--- a/bridges/modules/substrate/src/lib.rs
+++ b/bridges/modules/substrate/src/lib.rs
@@ -155,6 +155,11 @@ decl_error! {
 		AlreadyInitialized,
 		/// The given header is not a descendant of a particular header.
 		NotDescendant,
+		/// The header being imported is on a fork which is incompatible with the current chain.
+		///
+		/// This can happen if we try and import a finalized header at a lower height than our
+		/// current `best_finalized` header.
+		ConflictingFork,
 	}
 }
 
@@ -375,8 +380,14 @@ impl<T: Config> bp_header_chain::HeaderChain<BridgedHeader<T>, sp_runtime::Dispa
 		PalletStorage::<T>::new().current_authority_set()
 	}
 
-	fn append_header(header: BridgedHeader<T>) {
+	fn append_header(header: BridgedHeader<T>) -> Result<(), sp_runtime::DispatchError> {
+		// We do a quick check here to ensure that our header chain is making progress and isn't
+		// "travelling back in time" (which would be indicative of something bad, e.g a hard-fork).
+		let best_finalized = PalletStorage::<T>::new().best_finalized_header().header;
+		ensure!(best_finalized.number() < header.number(), <Error<T>>::ConflictingFork);
 		import_header_unchecked::<_, T>(&mut PalletStorage::<T>::new(), header);
+
+		Ok(())
 	}
 }
 
@@ -714,7 +725,7 @@ mod tests {
 	use crate::mock::{run_test, test_header, unfinalized_header, Origin, TestHeader, TestRuntime};
 	use bp_header_chain::HeaderChain;
 	use bp_test_utils::{alice, authority_list, bob};
-	use frame_support::{assert_noop, assert_ok};
+	use frame_support::{assert_err, assert_noop, assert_ok};
 	use sp_runtime::DispatchError;
 
 	fn init_with_origin(origin: Origin) -> Result<InitializationData<TestHeader>, DispatchError> {
@@ -907,7 +918,7 @@ mod tests {
 			let storage = PalletStorage::<TestRuntime>::new();
 
 			let header = test_header(2);
-			Module::<TestRuntime>::append_header(header.clone());
+			assert_ok!(Module::<TestRuntime>::append_header(header.clone()));
 
 			assert!(storage.header_by_hash(header.hash()).unwrap().is_finalized);
 			assert_eq!(storage.best_finalized_header().header, header);
@@ -915,6 +926,25 @@ mod tests {
 		})
 	}
 
+	#[test]
+	fn importing_unchecked_header_ensures_that_chain_is_extended() {
+		run_test(|| {
+			init_with_origin(Origin::root()).unwrap();
+
+			let header = test_header(3);
+			assert_ok!(Module::<TestRuntime>::append_header(header));
+
+			let header = test_header(2);
+			assert_err!(
+				Module::<TestRuntime>::append_header(header),
+				Error::<TestRuntime>::ConflictingFork,
+			);
+
+			let header = test_header(4);
+			assert_ok!(Module::<TestRuntime>::append_header(header));
+		})
+	}
+
 	#[test]
 	fn importing_unchecked_headers_enacts_new_authority_set() {
 		run_test(|| {
@@ -930,7 +960,7 @@ mod tests {
 			header.digest = fork_tests::change_log(0);
 
 			// Let's import our test header
-			Module::<TestRuntime>::append_header(header.clone());
+			assert_ok!(Module::<TestRuntime>::append_header(header.clone()));
 
 			// Make sure that our header is the best finalized
 			assert_eq!(storage.best_finalized_header().header, header);
@@ -960,8 +990,8 @@ mod tests {
 			let header = test_header(3);
 
 			// Let's import our test headers
-			Module::<TestRuntime>::append_header(schedules_change);
-			Module::<TestRuntime>::append_header(header.clone());
+			assert_ok!(Module::<TestRuntime>::append_header(schedules_change));
+			assert_ok!(Module::<TestRuntime>::append_header(header.clone()));
 
 			// Make sure that our header is the best finalized
 			assert_eq!(storage.best_finalized_header().header, header);
@@ -1001,7 +1031,7 @@ mod tests {
 
 			// We are expecting an authority set change at height 2, so this header should enact
 			// that upon being imported.
-			Module::<TestRuntime>::append_header(test_header(2));
+			assert_ok!(Module::<TestRuntime>::append_header(test_header(2)));
 
 			// Make sure that the authority set actually changed upon importing our header
 			assert_eq!(
diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs
index 16637176460..a2d8574b6dc 100644
--- a/bridges/primitives/header-chain/src/lib.rs
+++ b/bridges/primitives/header-chain/src/lib.rs
@@ -78,7 +78,7 @@ pub trait HeaderChain<H, E> {
 	fn authority_set() -> AuthoritySet;
 
 	/// Write a header finalized by GRANDPA to the underlying pallet storage.
-	fn append_header(header: H);
+	fn append_header(header: H) -> Result<(), E>;
 }
 
 impl<H: Default, E> HeaderChain<H, E> for () {
@@ -90,7 +90,9 @@ impl<H: Default, E> HeaderChain<H, E> for () {
 		AuthoritySet::default()
 	}
 
-	fn append_header(_header: H) {}
+	fn append_header(_header: H) -> Result<(), E> {
+		Ok(())
+	}
 }
 
 /// A trait for checking if a given child header is a direct descendant of an ancestor.
-- 
GitLab