Adding Bridges code as git subtree. (#2515)

* Add instructions.

* Squashed 'bridges/' content from commit 345e84a21

git-subtree-dir: bridges
git-subtree-split: 345e84a2146b56628e9888c9f5e129cb40e868a9

* Remove bridges workspace file to avoid confusing Cargo.

* Add some bridges primitives to Polkadot workspace.

* Improve docs.
This commit is contained in:
Tomasz Drwięga
2021-03-01 22:33:16 +01:00
committed by GitHub
parent 7a2c7aa3fe
commit 5169155f94
291 changed files with 64249 additions and 0 deletions
@@ -0,0 +1,53 @@
[package]
name = "pallet-substrate-bridge"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
finality-grandpa = { version = "0.14.0", default-features = false }
hash-db = { version = "0.15.2", default-features = false }
serde = { version = "1.0", optional = true }
# Bridge Dependencies
bp-header-chain = { path = "../../primitives/header-chain", default-features = false }
bp-runtime = { path = "../../primitives/runtime", default-features = false }
# Substrate Dependencies
frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "master" , default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate.git", branch = "master" , default-features = false }
sp-finality-grandpa = { git = "https://github.com/paritytech/substrate.git", branch = "master" , default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "master" , default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "master" , default-features = false }
sp-trie = { git = "https://github.com/paritytech/substrate.git", branch = "master" , default-features = false }
[dev-dependencies]
bp-test-utils = {path = "../../primitives/test-utils" }
sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "master" }
sp-state-machine = { git = "https://github.com/paritytech/substrate.git", branch = "master" }
[features]
default = ["std"]
std = [
"bp-header-chain/std",
"bp-runtime/std",
"bp-header-chain/std",
"codec/std",
"finality-grandpa/std",
"frame-support/std",
"frame-system/std",
"hash-db/std",
"serde",
"sp-finality-grandpa/std",
"sp-runtime/std",
"sp-std/std",
"sp-trie/std",
]
runtime-benchmarks = []
@@ -0,0 +1,515 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Tests for checking that behaviour of importing headers and finality proofs works correctly.
//!
//! The tests are built around the idea that we will be importing headers on different forks and we
//! should be able to check that we're correctly importing headers, scheduling changes, and
//! finalizing headers across different forks.
//!
//! Each test is depicted using beautiful ASCII art. The symbols used in the tests are the
//! following:
//!
//! - S|N: Schedules change in N blocks
//! - E: Enacts change
//! - F: Finalized
//! - FN: Finality proof imported for header N
//!
//! Each diagram also comes with an import order. This is important since we expect things to fail
//! when headers or proofs are imported in a certain order.
//!
//! Tests can be read as follows:
//!
//! ## Example Import 1
//!
//! (Type::Header(2, 1, None, None), Ok(()))
//!
//! Import header 2 on fork 1. This does not create a fork, or schedule an authority set change. We
//! expect this header import to be succesful.
//!
//! ## Example Import 2
//!
//! (Type::Header(4, 2, Some((3, 1)), Some(0)), Ok(()))
//!
//! Import header 4 on fork 2. This header starts a new fork from header 3 on fork 1. It also
//! schedules a change with a delay of 0 blocks. It should be succesfully imported.
//!
//! ## Example Import 3
//!
//! (Type::Finality(2, 1), Err(FinalizationError::OldHeader.into()))
//!
//! Import a finality proof for header 2 on fork 1. This finalty proof should fail to be imported
//! because the header is an old header.
use crate::mock::*;
use crate::storage::ImportedHeader;
use crate::verifier::*;
use crate::{BestFinalized, BestHeight, BridgeStorage, NextScheduledChange, PalletStorage};
use bp_header_chain::AuthoritySet;
use bp_test_utils::{alice, authority_list, bob, make_justification_for_header};
use codec::Encode;
use frame_support::{IterableStorageMap, StorageValue};
use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID};
use sp_runtime::{Digest, DigestItem};
use std::collections::BTreeMap;
type ForkId = u64;
type Delay = u64;
// Indicates when to start a new fork. The first item in the tuple
// will be the parent header of the header starting this fork.
type ForksAt = Option<(TestNumber, ForkId)>;
type ScheduledChangeAt = Option<Delay>;
#[derive(Debug)]
enum Type {
Header(TestNumber, ForkId, ForksAt, ScheduledChangeAt),
Finality(TestNumber, ForkId),
}
// Order: 1, 2, 2', 3, 3''
//
// / [3'']
// / [2']
// [1] <- [2] <- [3]
#[test]
fn fork_can_import_headers_on_different_forks() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut chain = vec![
(Type::Header(1, 1, None, None), Ok(())),
(Type::Header(2, 1, None, None), Ok(())),
(Type::Header(2, 2, Some((1, 1)), None), Ok(())),
(Type::Header(3, 1, None, None), Ok(())),
(Type::Header(3, 3, Some((2, 2)), None), Ok(())),
];
create_chain(&mut storage, &mut chain);
let best_headers = storage.best_headers();
assert_eq!(best_headers.len(), 2);
assert_eq!(<BestHeight<TestRuntime>>::get(), 3);
})
}
// Order: 1, 2, 2', F2, F2'
//
// [1] <- [2: F]
// \ [2']
//
// Not allowed to finalize 2'
#[test]
fn fork_does_not_allow_competing_finality_proofs() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut chain = vec![
(Type::Header(1, 1, None, None), Ok(())),
(Type::Header(2, 1, None, None), Ok(())),
(Type::Header(2, 2, Some((1, 1)), None), Ok(())),
(Type::Finality(2, 1), Ok(())),
(Type::Finality(2, 2), Err(FinalizationError::OldHeader.into())),
];
create_chain(&mut storage, &mut chain);
})
}
// Order: 1, 2, 3, F2, 3
//
// [1] <- [2: S|0] <- [3]
//
// Not allowed to import 3 until we get F2
//
// Note: GRANDPA would technically allow 3 to be imported as long as it didn't try and enact an
// authority set change. However, since we expect finality proofs to be imported quickly we've
// decided to simplify our import process and disallow header imports until we get a finality proof.
#[test]
fn fork_waits_for_finality_proof_before_importing_header_past_one_which_enacts_a_change() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut chain = vec![
(Type::Header(1, 1, None, None), Ok(())),
(Type::Header(2, 1, None, Some(0)), Ok(())),
(
Type::Header(3, 1, None, None),
Err(ImportError::AwaitingFinalityProof.into()),
),
(Type::Finality(2, 1), Ok(())),
(Type::Header(3, 1, None, None), Ok(())),
];
create_chain(&mut storage, &mut chain);
})
}
// Order: 1, 2, F2, 3
//
// [1] <- [2: S|1] <- [3: S|0]
//
// GRANDPA can have multiple authority set changes pending on the same fork. However, we've decided
// to introduce a limit of _one_ pending authority set change per fork in order to simplify pallet
// logic and to prevent DoS attacks if GRANDPA finality were to temporarily stall for a long time
// (we'd have to perform a lot of expensive ancestry checks to catch back up).
#[test]
fn fork_does_not_allow_multiple_scheduled_changes_on_the_same_fork() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut chain = vec![
(Type::Header(1, 1, None, None), Ok(())),
(Type::Header(2, 1, None, Some(1)), Ok(())),
(
Type::Header(3, 1, None, Some(0)),
Err(ImportError::PendingAuthoritySetChange.into()),
),
(Type::Finality(2, 1), Ok(())),
(Type::Header(3, 1, None, Some(0)), Ok(())),
];
create_chain(&mut storage, &mut chain);
})
}
// Order: 1, 2, 2'
//
// / [2': S|0]
// [1] <- [2: S|0]
//
// Both 2 and 2' should be marked as needing justifications since they enact changes.
#[test]
fn fork_correctly_tracks_which_headers_require_finality_proofs() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut chain = vec![
(Type::Header(1, 1, None, None), Ok(())),
(Type::Header(2, 1, None, Some(0)), Ok(())),
(Type::Header(2, 2, Some((1, 1)), Some(0)), Ok(())),
];
create_chain(&mut storage, &mut chain);
let header_ids = storage.missing_justifications();
assert_eq!(header_ids.len(), 2);
assert!(header_ids[0].hash != header_ids[1].hash);
assert_eq!(header_ids[0].number, 2);
assert_eq!(header_ids[1].number, 2);
})
}
// Order: 1, 2, 2', 3', F2, 3, 4'
//
// / [2': S|1] <- [3'] <- [4']
// [1] <- [2: S|0] <- [3]
//
//
// Not allowed to import 3 or 4'
// Can only import 3 after we get the finality proof for 2
#[test]
fn fork_does_not_allow_importing_past_header_that_enacts_changes_on_forks() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut chain = vec![
(Type::Header(1, 1, None, None), Ok(())),
(Type::Header(2, 1, None, Some(0)), Ok(())),
(Type::Header(2, 2, Some((1, 1)), Some(1)), Ok(())),
(
Type::Header(3, 1, None, None),
Err(ImportError::AwaitingFinalityProof.into()),
),
(Type::Header(3, 2, None, None), Ok(())),
(Type::Finality(2, 1), Ok(())),
(Type::Header(3, 1, None, None), Ok(())),
(
Type::Header(4, 2, None, None),
Err(ImportError::AwaitingFinalityProof.into()),
),
];
create_chain(&mut storage, &mut chain);
// Since we can't query the map directly to check if we applied the right authority set
// change (we don't know the header hash of 2) we need to get a little clever.
let mut next_change = <NextScheduledChange<TestRuntime>>::iter();
let (_, scheduled_change_on_fork) = next_change.next().unwrap();
assert_eq!(scheduled_change_on_fork.height, 3);
// Sanity check to make sure we enacted the change on the canonical change
assert_eq!(next_change.next(), None);
})
}
// Order: 1, 2, 3, 2', 3'
//
// / [2'] <- [3']
// [1] <- [2: S|0] <- [3]
//
// Not allowed to import 3
// Fine to import 2' and 3'
#[test]
fn fork_allows_importing_on_different_fork_while_waiting_for_finality_proof() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut chain = vec![
(Type::Header(1, 1, None, None), Ok(())),
(Type::Header(2, 1, None, Some(0)), Ok(())),
(
Type::Header(3, 1, None, None),
Err(ImportError::AwaitingFinalityProof.into()),
),
(Type::Header(2, 2, Some((1, 1)), None), Ok(())),
(Type::Header(3, 2, None, None), Ok(())),
];
create_chain(&mut storage, &mut chain);
})
}
// Order: 1, 2, 2', F2, 3, 3'
//
// / [2'] <- [3']
// [1] <- [2: F] <- [3]
//
// In our current implementation we're allowed to keep building on fork 2 for as long as our hearts'
// content. However, we'll never be able to finalize anything on that fork. We'd have to check for
// ancestry with `best_finalized` on every import which will get expensive.
//
// I think this is fine as long as we run pruning every so often to clean up these dead forks.
#[test]
fn fork_allows_importing_on_different_fork_past_finalized_header() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut chain = vec![
(Type::Header(1, 1, None, None), Ok(())),
(Type::Header(2, 1, None, Some(0)), Ok(())),
(Type::Header(2, 2, Some((1, 1)), None), Ok(())),
(Type::Finality(2, 1), Ok(())),
(Type::Header(3, 1, None, None), Ok(())),
(Type::Header(3, 2, None, None), Ok(())),
];
create_chain(&mut storage, &mut chain);
})
}
// Order: 1, 2, 3, 4, 3', 4'
//
// / [3': E] <- [4']
// [1] <- [2: S|1] <- [3: E] <- [4]
//
// Not allowed to import {4|4'}
#[test]
fn fork_can_track_scheduled_changes_across_forks() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut chain = vec![
(Type::Header(1, 1, None, None), Ok(())),
(Type::Header(2, 1, None, Some(1)), Ok(())),
(Type::Header(3, 1, None, None), Ok(())),
(
Type::Header(4, 1, None, None),
Err(ImportError::AwaitingFinalityProof.into()),
),
(Type::Header(3, 2, Some((2, 1)), None), Ok(())),
(
Type::Header(4, 2, None, None),
Err(ImportError::AwaitingFinalityProof.into()),
),
];
create_chain(&mut storage, &mut chain);
})
}
#[derive(Debug, PartialEq)]
enum TestError {
Import(ImportError),
Finality(FinalizationError),
}
impl From<ImportError> for TestError {
fn from(e: ImportError) -> Self {
TestError::Import(e)
}
}
impl From<FinalizationError> for TestError {
fn from(e: FinalizationError) -> Self {
TestError::Finality(e)
}
}
// Builds a fork-aware representation of a blockchain given a list of headers.
//
// Takes a list of headers and finality proof operations which will be applied in order. The
// expected outcome for each operation is also required.
//
// The first header in the list will be used as the genesis header and will be manually imported
// into storage.
fn create_chain<S>(storage: &mut S, chain: &mut Vec<(Type, Result<(), TestError>)>)
where
S: BridgeStorage<Header = TestHeader> + Clone,
{
let mut map = BTreeMap::new();
let mut verifier = Verifier {
storage: storage.clone(),
};
initialize_genesis(storage, &mut map, chain.remove(0).0);
for h in chain {
match h {
(Type::Header(num, fork_id, does_fork, schedules_change), expected_result) => {
// If we've never seen this fork before
if !map.contains_key(&fork_id) {
// Let's get the info about where to start the fork
if let Some((parent_num, forked_from_id)) = does_fork {
let fork = &*map.get(&forked_from_id).unwrap();
let parent = fork
.iter()
.find(|h| h.number == *parent_num)
.expect("Trying to fork on a parent which doesn't exist");
let mut header = test_header(*num);
header.parent_hash = parent.hash();
header.state_root = [*fork_id as u8; 32].into();
if let Some(delay) = schedules_change {
header.digest = change_log(*delay);
}
// Try and import into storage
let res = verifier
.import_header(header.hash(), header.clone())
.map_err(TestError::Import);
assert_eq!(
res, *expected_result,
"Expected {:?} while importing header ({}, {}), got {:?}",
*expected_result, *num, *fork_id, res,
);
// Let's mark the header down in a new fork
if res.is_ok() {
map.insert(*fork_id, vec![header]);
}
}
} else {
// We've seen this fork before so let's append our new header to it
let parent_hash = {
let fork = &*map.get(&fork_id).unwrap();
fork.last().unwrap().hash()
};
let mut header = test_header(*num);
header.parent_hash = parent_hash;
// Doing this to make sure headers at the same height but on
// different forks have different hashes
header.state_root = [*fork_id as u8; 32].into();
if let Some(delay) = schedules_change {
header.digest = change_log(*delay);
}
let res = verifier
.import_header(header.hash(), header.clone())
.map_err(TestError::Import);
assert_eq!(
res, *expected_result,
"Expected {:?} while importing header ({}, {}), got {:?}",
*expected_result, *num, *fork_id, res,
);
if res.is_ok() {
map.get_mut(&fork_id).unwrap().push(header);
}
}
}
(Type::Finality(num, fork_id), expected_result) => {
let header = map[fork_id]
.iter()
.find(|h| h.number == *num)
.expect("Trying to finalize block that doesn't exist");
// This is technically equivocating (accepting the same justification on the same
// `grandpa_round`).
//
// See for more: https://github.com/paritytech/parity-bridges-common/issues/430
let grandpa_round = 1;
let set_id = 1;
let authorities = authority_list();
let justification = make_justification_for_header(header, grandpa_round, set_id, &authorities).encode();
let res = verifier
.import_finality_proof(header.hash(), justification.into())
.map_err(TestError::Finality);
assert_eq!(
res, *expected_result,
"Expected {:?} while importing finality proof for header ({}, {}), got {:?}",
*expected_result, *num, *fork_id, res,
);
}
}
}
for (key, value) in map.iter() {
println!("{}: {:#?}", key, value);
}
}
fn initialize_genesis<S>(storage: &mut S, map: &mut BTreeMap<TestNumber, Vec<TestHeader>>, genesis: Type)
where
S: BridgeStorage<Header = TestHeader>,
{
if let Type::Header(num, fork_id, None, None) = genesis {
let genesis = test_header(num);
map.insert(fork_id, vec![genesis.clone()]);
let genesis = ImportedHeader {
header: genesis,
requires_justification: false,
is_finalized: true,
signal_hash: None,
};
<BestFinalized<TestRuntime>>::put(genesis.hash());
storage.write_header(&genesis);
} else {
panic!("Unexpected genesis block format {:#?}", genesis)
}
let set_id = 1;
let authorities = authority_list();
let authority_set = AuthoritySet::new(authorities, set_id);
storage.update_current_authority_set(authority_set);
}
pub(crate) fn change_log(delay: u64) -> Digest<TestHash> {
let consensus_log = ConsensusLog::<TestNumber>::ScheduledChange(sp_finality_grandpa::ScheduledChange {
next_authorities: vec![(alice(), 1), (bob(), 1)],
delay,
});
Digest::<TestHash> {
logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())],
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,117 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Mock Runtime for Substrate Pallet Testing.
//!
//! Includes some useful testing types and functions.
#![cfg(test)]
// From construct_runtime macro
#![allow(clippy::from_over_into)]
use crate::{BridgedBlockHash, BridgedBlockNumber, BridgedHeader, Config};
use bp_runtime::Chain;
use frame_support::{parameter_types, weights::Weight};
use sp_runtime::{
testing::{Header, H256},
traits::{BlakeTwo256, IdentityLookup},
Perbill,
};
pub type AccountId = u64;
pub type TestHeader = BridgedHeader<TestRuntime>;
pub type TestNumber = BridgedBlockNumber<TestRuntime>;
pub type TestHash = BridgedBlockHash<TestRuntime>;
type Block = frame_system::mocking::MockBlock<TestRuntime>;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<TestRuntime>;
use crate as pallet_substrate;
frame_support::construct_runtime! {
pub enum TestRuntime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Module, Call, Config, Storage, Event<T>},
Substrate: pallet_substrate::{Module, Call},
}
}
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: Weight = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
pub const AvailableBlockRatio: Perbill = Perbill::one();
}
impl frame_system::Config for TestRuntime {
type Origin = Origin;
type Index = u64;
type Call = Call;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = ();
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type BaseCallFilter = ();
type SystemWeightInfo = ();
type DbWeight = ();
type BlockWeights = ();
type BlockLength = ();
type SS58Prefix = ();
}
impl Config for TestRuntime {
type BridgedChain = TestBridgedChain;
}
#[derive(Debug)]
pub struct TestBridgedChain;
impl Chain for TestBridgedChain {
type BlockNumber = <TestRuntime as frame_system::Config>::BlockNumber;
type Hash = <TestRuntime as frame_system::Config>::Hash;
type Hasher = <TestRuntime as frame_system::Config>::Hashing;
type Header = <TestRuntime as frame_system::Config>::Header;
}
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
sp_io::TestExternalities::new(Default::default()).execute_with(test)
}
pub fn test_header(num: TestNumber) -> TestHeader {
// We wrap the call to avoid explicit type annotations in our tests
bp_test_utils::test_header(num)
}
pub fn unfinalized_header(num: u64) -> crate::storage::ImportedHeader<TestHeader> {
crate::storage::ImportedHeader {
header: test_header(num),
requires_justification: false,
is_finalized: false,
signal_hash: None,
}
}
@@ -0,0 +1,80 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Storage primitives for the Substrate light client (a.k.a bridge) pallet.
use bp_header_chain::AuthoritySet;
use codec::{Decode, Encode};
use core::default::Default;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_finality_grandpa::{AuthorityList, SetId};
use sp_runtime::traits::Header as HeaderT;
use sp_runtime::RuntimeDebug;
/// Data required for initializing the bridge pallet.
///
/// The bridge needs to know where to start its sync from, and this provides that initial context.
#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct InitializationData<H: HeaderT> {
/// The header from which we should start syncing.
pub header: H,
/// The initial authorities of the pallet.
pub authority_list: AuthorityList,
/// The ID of the initial authority set.
pub set_id: SetId,
/// The first scheduled authority set change of the pallet.
pub scheduled_change: Option<ScheduledChange<H::Number>>,
/// Should the pallet block transaction immediately after initialization.
pub is_halted: bool,
}
/// Keeps track of when the next GRANDPA authority set change will occur.
#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct ScheduledChange<N> {
/// The authority set that will be used once this change is enacted.
pub authority_set: AuthoritySet,
/// The block height at which the authority set should be enacted.
///
/// Note: It will only be enacted once a header at this height is finalized.
pub height: N,
}
/// A more useful representation of a header for storage purposes.
#[derive(Default, Encode, Decode, Clone, RuntimeDebug, PartialEq)]
pub struct ImportedHeader<H: HeaderT> {
/// A plain Substrate header.
pub header: H,
/// Does this header enact a new authority set change. If it does
/// then it will require a justification.
pub requires_justification: bool,
/// Has this header been finalized, either explicitly via a justification,
/// or implicitly via one of its children getting finalized.
pub is_finalized: bool,
/// The hash of the header which scheduled a change on this fork. If there are currently
/// not pending changes on this fork this will be empty.
pub signal_hash: Option<H::Hash>,
}
impl<H: HeaderT> core::ops::Deref for ImportedHeader<H> {
type Target = H;
fn deref(&self) -> &H {
&self.header
}
}
@@ -0,0 +1,122 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
// TODO: remove on actual use
#![allow(dead_code)]
//! Logic for checking Substrate storage proofs.
use hash_db::{HashDB, Hasher, EMPTY_PREFIX};
use sp_runtime::RuntimeDebug;
use sp_std::vec::Vec;
use sp_trie::{read_trie_value, Layout, MemoryDB, StorageProof};
/// This struct is used to read storage values from a subset of a Merklized database. The "proof"
/// is a subset of the nodes in the Merkle structure of the database, so that it provides
/// authentication against a known Merkle root as well as the values in the database themselves.
pub struct StorageProofChecker<H>
where
H: Hasher,
{
root: H::Out,
db: MemoryDB<H>,
}
impl<H> StorageProofChecker<H>
where
H: Hasher,
{
/// Constructs a new storage proof checker.
///
/// This returns an error if the given proof is invalid with respect to the given root.
pub fn new(root: H::Out, proof: StorageProof) -> Result<Self, Error> {
let db = proof.into_memory_db();
if !db.contains(&root, EMPTY_PREFIX) {
return Err(Error::StorageRootMismatch);
}
let checker = StorageProofChecker { root, db };
Ok(checker)
}
/// Reads a value from the available subset of storage. If the value cannot be read due to an
/// incomplete or otherwise invalid proof, this returns an error.
pub fn read_value(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
read_trie_value::<Layout<H>, _>(&self.db, &self.root, key).map_err(|_| Error::StorageValueUnavailable)
}
}
#[derive(RuntimeDebug, PartialEq)]
pub enum Error {
StorageRootMismatch,
StorageValueUnavailable,
}
impl<T: crate::Config> From<Error> for crate::Error<T> {
fn from(error: Error) -> Self {
match error {
Error::StorageRootMismatch => crate::Error::StorageRootMismatch,
Error::StorageValueUnavailable => crate::Error::StorageValueUnavailable,
}
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use sp_core::{Blake2Hasher, H256};
use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend};
/// Return valid storage proof and state root.
pub fn craft_valid_storage_proof() -> (H256, StorageProof) {
// construct storage proof
let backend = <InMemoryBackend<Blake2Hasher>>::from(vec![
(None, vec![(b"key1".to_vec(), Some(b"value1".to_vec()))]),
(None, vec![(b"key2".to_vec(), Some(b"value2".to_vec()))]),
(None, vec![(b"key3".to_vec(), Some(b"value3".to_vec()))]),
// Value is too big to fit in a branch node
(None, vec![(b"key11".to_vec(), Some(vec![0u8; 32]))]),
]);
let root = backend.storage_root(std::iter::empty()).0;
let proof = StorageProof::new(
prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key22"[..]])
.unwrap()
.iter_nodes()
.collect(),
);
(root, proof)
}
#[test]
fn storage_proof_check() {
let (root, proof) = craft_valid_storage_proof();
// check proof in runtime
let checker = <StorageProofChecker<Blake2Hasher>>::new(root, proof.clone()).unwrap();
assert_eq!(checker.read_value(b"key1"), Ok(Some(b"value1".to_vec())));
assert_eq!(checker.read_value(b"key2"), Ok(Some(b"value2".to_vec())));
assert_eq!(checker.read_value(b"key11111"), Err(Error::StorageValueUnavailable));
assert_eq!(checker.read_value(b"key22"), Ok(None));
// checking proof against invalid commitment fails
assert_eq!(
<StorageProofChecker<Blake2Hasher>>::new(H256::random(), proof).err(),
Some(Error::StorageRootMismatch)
);
}
}
@@ -0,0 +1,871 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! The verifier's role is to check the validity of headers being imported, and also determine if
//! they can be finalized.
//!
//! When importing headers it performs checks to ensure that no invariants are broken (like
//! importing the same header twice). When it imports finality proofs it will ensure that the proof
//! has been signed off by the correct GRANDPA authorities, and also enact any authority set changes
//! if required.
use crate::storage::{ImportedHeader, ScheduledChange};
use crate::BridgeStorage;
use bp_header_chain::{justification::verify_justification, AuthoritySet};
use finality_grandpa::voter_set::VoterSet;
use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID};
use sp_runtime::generic::OpaqueDigestItemId;
use sp_runtime::traits::{CheckedAdd, Header as HeaderT, One};
use sp_runtime::RuntimeDebug;
use sp_std::{prelude::Vec, vec};
/// The finality proof used by the pallet.
///
/// For a Substrate based chain using GRANDPA this will
/// be an encoded GRANDPA Justification.
#[derive(RuntimeDebug)]
pub struct FinalityProof(Vec<u8>);
impl From<&[u8]> for FinalityProof {
fn from(proof: &[u8]) -> Self {
Self(proof.to_vec())
}
}
impl From<Vec<u8>> for FinalityProof {
fn from(proof: Vec<u8>) -> Self {
Self(proof)
}
}
/// Errors which can happen while importing a header.
#[derive(RuntimeDebug, PartialEq)]
pub enum ImportError {
/// This header is at the same height or older than our latest finalized block, thus not useful.
OldHeader,
/// This header has already been imported by the pallet.
HeaderAlreadyExists,
/// We're missing a parent for this header.
MissingParent,
/// The number of the header does not follow its parent's number.
InvalidChildNumber,
/// The height of the next authority set change overflowed.
ScheduledHeightOverflow,
/// Received an authority set which was invalid in some way, such as
/// the authority weights being empty or overflowing the `AuthorityWeight`
/// type.
InvalidAuthoritySet,
/// This header is not allowed to be imported since an ancestor requires a finality proof.
///
/// This can happen if an ancestor is supposed to enact an authority set change.
AwaitingFinalityProof,
/// This header schedules an authority set change even though we're still waiting
/// for an old authority set change to be enacted on this fork.
PendingAuthoritySetChange,
}
/// Errors which can happen while verifying a headers finality.
#[derive(RuntimeDebug, PartialEq)]
pub enum FinalizationError {
/// This header has never been imported by the pallet.
UnknownHeader,
/// Trying to prematurely import a justification
PrematureJustification,
/// We failed to verify this header's ancestry.
AncestryCheckFailed,
/// This header is at the same height or older than our latest finalized block, thus not useful.
OldHeader,
/// The given justification was not able to finalize the given header.
///
/// There are several reasons why this might happen, such as the justification being
/// signed by the wrong authority set, being given alongside an unexpected header,
/// or failing ancestry checks.
InvalidJustification,
}
/// Used to verify imported headers and their finality status.
#[derive(RuntimeDebug)]
pub struct Verifier<S> {
pub storage: S,
}
impl<S, H> Verifier<S>
where
S: BridgeStorage<Header = H>,
H: HeaderT,
H::Number: finality_grandpa::BlockNumberOps,
{
/// Import a header to the pallet.
///
/// Will perform some basic checks to make sure that this header doesn't break any assumptions
/// such as being on a different finalized fork.
pub fn import_header(&mut self, hash: H::Hash, header: H) -> Result<(), ImportError> {
let best_finalized = self.storage.best_finalized_header();
if header.number() <= best_finalized.number() {
return Err(ImportError::OldHeader);
}
if self.storage.header_exists(hash) {
return Err(ImportError::HeaderAlreadyExists);
}
let parent_header = self
.storage
.header_by_hash(*header.parent_hash())
.ok_or(ImportError::MissingParent)?;
let parent_number = *parent_header.number();
if parent_number + One::one() != *header.number() {
return Err(ImportError::InvalidChildNumber);
}
// A header requires a justification if it enacts an authority set change. We don't
// need to act on it right away (we'll update the set once the header gets finalized), but
// we need to make a note of it.
//
// Note: This assumes that we can only have one authority set change pending per fork at a
// time. While this is not strictly true of GRANDPA (it can have multiple pending changes,
// even across forks), this assumption simplifies our tracking of authority set changes.
let mut signal_hash = parent_header.signal_hash;
let scheduled_change = find_scheduled_change(&header);
// Check if our fork is expecting an authority set change
let requires_justification = if let Some(hash) = signal_hash {
const PROOF: &str = "If the header has a signal hash it means there's an accompanying set
change in storage, therefore this must always be valid.";
let pending_change = self.storage.scheduled_set_change(hash).expect(PROOF);
if scheduled_change.is_some() {
return Err(ImportError::PendingAuthoritySetChange);
}
if *header.number() > pending_change.height {
return Err(ImportError::AwaitingFinalityProof);
}
pending_change.height == *header.number()
} else {
// Since we don't currently have a pending authority set change let's check if the header
// contains a log indicating when the next change should be.
if let Some(change) = scheduled_change {
let mut total_weight = 0u64;
for (_id, weight) in &change.next_authorities {
total_weight = total_weight
.checked_add(*weight)
.ok_or(ImportError::InvalidAuthoritySet)?;
}
// If none of the authorities have a weight associated with them the
// set is essentially empty. We don't want that.
if total_weight == 0 {
return Err(ImportError::InvalidAuthoritySet);
}
let next_set = AuthoritySet {
authorities: change.next_authorities,
set_id: self.storage.current_authority_set().set_id + 1,
};
let height = (*header.number())
.checked_add(&change.delay)
.ok_or(ImportError::ScheduledHeightOverflow)?;
let scheduled_change = ScheduledChange {
authority_set: next_set,
height,
};
// Note: It's important that the signal hash is updated if a header schedules a
// change or else we end up with inconsistencies in other places.
signal_hash = Some(hash);
self.storage.schedule_next_set_change(hash, scheduled_change);
// If the delay is 0 this header will enact the change it signaled
height == *header.number()
} else {
false
}
};
self.storage.write_header(&ImportedHeader {
header,
requires_justification,
is_finalized: false,
signal_hash,
});
Ok(())
}
/// Verify that a previously imported header can be finalized with the given GRANDPA finality
/// proof. If the header enacts an authority set change the change will be applied once the
/// header has been finalized.
pub fn import_finality_proof(&mut self, hash: H::Hash, proof: FinalityProof) -> Result<(), FinalizationError> {
// Make sure that we've previously imported this header
let header = self
.storage
.header_by_hash(hash)
.ok_or(FinalizationError::UnknownHeader)?;
// We don't want to finalize an ancestor of an already finalized
// header, this would be inconsistent
let last_finalized = self.storage.best_finalized_header();
if header.number() <= last_finalized.number() {
return Err(FinalizationError::OldHeader);
}
let current_authority_set = self.storage.current_authority_set();
let voter_set = VoterSet::new(current_authority_set.authorities).expect(
"We verified the correctness of the authority list during header import,
before writing them to storage. This must always be valid.",
);
verify_justification::<H>(
(hash, *header.number()),
current_authority_set.set_id,
voter_set,
&proof.0,
)
.map_err(|_| FinalizationError::InvalidJustification)?;
frame_support::debug::trace!("Received valid justification for {:?}", header);
frame_support::debug::trace!(
"Checking ancestry for headers between {:?} and {:?}",
last_finalized,
header
);
let mut finalized_headers =
if let Some(ancestors) = headers_between(&self.storage, last_finalized, header.clone()) {
// Since we only try and finalize headers with a height strictly greater
// than `best_finalized` if `headers_between` returns Some we must have
// at least one element. If we don't something's gone wrong, so best
// to die before we write to storage.
assert_eq!(
ancestors.is_empty(),
false,
"Empty ancestry list returned from `headers_between()`",
);
// Check if any of our ancestors `requires_justification` a.k.a schedule authority
// set changes. If they're still waiting to be finalized we must reject this
// justification. We don't include our current header in this check.
//
// We do this because it is important to to import justifications _in order_,
// otherwise we risk finalizing headers on competing chains.
let requires_justification = ancestors.iter().skip(1).find(|h| h.requires_justification);
if requires_justification.is_some() {
return Err(FinalizationError::PrematureJustification);
}
ancestors
} else {
return Err(FinalizationError::AncestryCheckFailed);
};
// If the current header was marked as `requires_justification` it means that it enacts a
// new authority set change. When we finalize the header we need to update the current
// authority set.
if header.requires_justification {
const SIGNAL_HASH_PROOF: &str = "When we import a header we only mark it as
`requires_justification` if we have checked that it contains a signal hash. Therefore
this must always be valid.";
const ENACT_SET_PROOF: &str =
"Headers must only be marked as `requires_justification` if there's a scheduled change in storage.";
// If we are unable to enact an authority set it means our storage entry for scheduled
// changes is missing. Best to crash since this is likely a bug.
let _ = self
.storage
.enact_authority_set(header.signal_hash.expect(SIGNAL_HASH_PROOF))
.expect(ENACT_SET_PROOF);
}
for header in finalized_headers.iter_mut() {
header.is_finalized = true;
header.requires_justification = false;
header.signal_hash = None;
self.storage.write_header(header);
}
self.storage.update_best_finalized(hash);
Ok(())
}
}
/// Returns the lineage of headers between [child, ancestor)
fn headers_between<S, H>(
storage: &S,
ancestor: ImportedHeader<H>,
child: ImportedHeader<H>,
) -> Option<Vec<ImportedHeader<H>>>
where
S: BridgeStorage<Header = H>,
H: HeaderT,
{
let mut ancestors = vec![];
let mut current_header = child;
while ancestor.hash() != current_header.hash() {
// We've gotten to the same height and we're not related
if ancestor.number() >= current_header.number() {
return None;
}
let parent = storage.header_by_hash(*current_header.parent_hash());
ancestors.push(current_header);
current_header = match parent {
Some(h) => h,
None => return None,
}
}
Some(ancestors)
}
pub(crate) fn find_scheduled_change<H: HeaderT>(header: &H) -> Option<sp_finality_grandpa::ScheduledChange<H::Number>> {
let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID);
let filter_log = |log: ConsensusLog<H::Number>| match log {
ConsensusLog::ScheduledChange(change) => Some(change),
_ => None,
};
// find the first consensus digest with the right ID which converts to
// the right kind of consensus log.
header.digest().convert_first(|l| l.try_to(id).and_then(filter_log))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::*;
use crate::{BestFinalized, BestHeight, HeaderId, ImportedHeaders, PalletStorage};
use bp_test_utils::{alice, authority_list, bob, make_justification_for_header};
use codec::Encode;
use frame_support::{assert_err, assert_ok};
use frame_support::{StorageMap, StorageValue};
use sp_finality_grandpa::{AuthorityId, SetId};
use sp_runtime::{Digest, DigestItem};
fn schedule_next_change(
authorities: Vec<AuthorityId>,
set_id: SetId,
height: TestNumber,
) -> ScheduledChange<TestNumber> {
let authorities = authorities.into_iter().map(|id| (id, 1u64)).collect();
let authority_set = AuthoritySet::new(authorities, set_id);
ScheduledChange { authority_set, height }
}
// Useful for quickly writing a chain of headers to storage
// Input is expected in the form: vec![(num, requires_justification, is_finalized)]
fn write_headers<S: BridgeStorage<Header = TestHeader>>(
storage: &mut S,
headers: Vec<(u64, bool, bool)>,
) -> Vec<ImportedHeader<TestHeader>> {
let mut imported_headers = vec![];
let genesis = ImportedHeader {
header: test_header(0),
requires_justification: false,
is_finalized: true,
signal_hash: None,
};
<BestFinalized<TestRuntime>>::put(genesis.hash());
storage.write_header(&genesis);
imported_headers.push(genesis);
for (num, requires_justification, is_finalized) in headers {
let header = ImportedHeader {
header: test_header(num),
requires_justification,
is_finalized,
signal_hash: None,
};
storage.write_header(&header);
imported_headers.push(header);
}
imported_headers
}
// Given a block number will generate a chain of headers which don't require justification and
// are not considered to be finalized.
fn write_default_headers<S: BridgeStorage<Header = TestHeader>>(
storage: &mut S,
headers: Vec<u64>,
) -> Vec<ImportedHeader<TestHeader>> {
let headers = headers.iter().map(|num| (*num, false, false)).collect();
write_headers(storage, headers)
}
#[test]
fn fails_to_import_old_header() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let parent = unfinalized_header(5);
storage.write_header(&parent);
storage.update_best_finalized(parent.hash());
let header = test_header(1);
let mut verifier = Verifier { storage };
assert_err!(verifier.import_header(header.hash(), header), ImportError::OldHeader);
})
}
#[test]
fn fails_to_import_header_without_parent() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let parent = unfinalized_header(1);
storage.write_header(&parent);
storage.update_best_finalized(parent.hash());
// By default the parent is `0x00`
let header = TestHeader::new_from_number(2);
let mut verifier = Verifier { storage };
assert_err!(
verifier.import_header(header.hash(), header),
ImportError::MissingParent
);
})
}
#[test]
fn fails_to_import_header_twice() {
run_test(|| {
let storage = PalletStorage::<TestRuntime>::new();
let header = test_header(1);
<BestFinalized<TestRuntime>>::put(header.hash());
let imported_header = ImportedHeader {
header: header.clone(),
requires_justification: false,
is_finalized: false,
signal_hash: None,
};
<ImportedHeaders<TestRuntime>>::insert(header.hash(), &imported_header);
let mut verifier = Verifier { storage };
assert_err!(verifier.import_header(header.hash(), header), ImportError::OldHeader);
})
}
#[test]
fn succesfully_imports_valid_but_unfinalized_header() {
run_test(|| {
let storage = PalletStorage::<TestRuntime>::new();
let parent = test_header(1);
let parent_hash = parent.hash();
<BestFinalized<TestRuntime>>::put(parent.hash());
let imported_header = ImportedHeader {
header: parent,
requires_justification: false,
is_finalized: true,
signal_hash: None,
};
<ImportedHeaders<TestRuntime>>::insert(parent_hash, &imported_header);
let header = test_header(2);
let mut verifier = Verifier {
storage: storage.clone(),
};
assert_ok!(verifier.import_header(header.hash(), header.clone()));
let stored_header = storage
.header_by_hash(header.hash())
.expect("Should have been imported successfully");
assert_eq!(stored_header.is_finalized, false);
assert_eq!(stored_header.hash(), storage.best_headers()[0].hash);
})
}
#[test]
fn successfully_imports_two_different_headers_at_same_height() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
// We want to write the genesis header to storage
let _ = write_headers(&mut storage, vec![]);
// Both of these headers have the genesis header as their parent
let header_on_fork1 = test_header(1);
let mut header_on_fork2 = test_header(1);
// We need to change _something_ to make it a different header
header_on_fork2.state_root = [1; 32].into();
let mut verifier = Verifier {
storage: storage.clone(),
};
// It should be fine to import both
assert_ok!(verifier.import_header(header_on_fork1.hash(), header_on_fork1.clone()));
assert_ok!(verifier.import_header(header_on_fork2.hash(), header_on_fork2.clone()));
// We should have two headers marked as being the best since they're
// both at the same height
let best_headers = storage.best_headers();
assert_eq!(best_headers.len(), 2);
assert_eq!(
best_headers[0],
HeaderId {
number: *header_on_fork1.number(),
hash: header_on_fork1.hash()
}
);
assert_eq!(
best_headers[1],
HeaderId {
number: *header_on_fork2.number(),
hash: header_on_fork2.hash()
}
);
assert_eq!(<BestHeight<TestRuntime>>::get(), 1);
})
}
#[test]
fn correctly_updates_the_best_header_given_a_better_header() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
// We want to write the genesis header to storage
let _ = write_headers(&mut storage, vec![]);
// Write two headers at the same height to storage.
let best_header = test_header(1);
let mut also_best_header = test_header(1);
// We need to change _something_ to make it a different header
also_best_header.state_root = [1; 32].into();
let mut verifier = Verifier {
storage: storage.clone(),
};
// It should be fine to import both
assert_ok!(verifier.import_header(best_header.hash(), best_header.clone()));
assert_ok!(verifier.import_header(also_best_header.hash(), also_best_header));
// The headers we manually imported should have been marked as the best
// upon writing to storage. Let's confirm that.
assert_eq!(storage.best_headers().len(), 2);
assert_eq!(<BestHeight<TestRuntime>>::get(), 1);
// Now let's build something at a better height.
let mut better_header = test_header(2);
better_header.parent_hash = best_header.hash();
assert_ok!(verifier.import_header(better_header.hash(), better_header.clone()));
// Since `better_header` is the only one at height = 2 we should only have
// a single "best header" now.
let best_headers = storage.best_headers();
assert_eq!(best_headers.len(), 1);
assert_eq!(
best_headers[0],
HeaderId {
number: *better_header.number(),
hash: better_header.hash()
}
);
assert_eq!(<BestHeight<TestRuntime>>::get(), 2);
})
}
#[test]
fn doesnt_write_best_header_twice_upon_finalization() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let _imported_headers = write_default_headers(&mut storage, vec![1]);
let set_id = 1;
let authorities = authority_list();
let initial_authority_set = AuthoritySet::new(authorities.clone(), set_id);
storage.update_current_authority_set(initial_authority_set);
// Let's import our header
let header = test_header(2);
let mut verifier = Verifier {
storage: storage.clone(),
};
assert_ok!(verifier.import_header(header.hash(), header.clone()));
// Our header should be the only best header we have
assert_eq!(storage.best_headers()[0].hash, header.hash());
assert_eq!(storage.best_headers().len(), 1);
// Now lets finalize our best header
let grandpa_round = 1;
let justification = make_justification_for_header(&header, grandpa_round, set_id, &authorities).encode();
assert_ok!(verifier.import_finality_proof(header.hash(), justification.into()));
// Our best header should only appear once in the list of best headers
assert_eq!(storage.best_headers()[0].hash, header.hash());
assert_eq!(storage.best_headers().len(), 1);
})
}
#[test]
fn related_headers_are_ancestors() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut imported_headers = write_default_headers(&mut storage, vec![1, 2, 3]);
for header in imported_headers.iter() {
assert!(storage.header_exists(header.hash()));
}
let ancestor = imported_headers.remove(0);
let child = imported_headers.pop().unwrap();
let ancestors = headers_between(&storage, ancestor, child);
assert!(ancestors.is_some());
assert_eq!(ancestors.unwrap().len(), 3);
})
}
#[test]
fn unrelated_headers_are_not_ancestors() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut imported_headers = write_default_headers(&mut storage, vec![1, 2, 3]);
for header in imported_headers.iter() {
assert!(storage.header_exists(header.hash()));
}
// Need to give it a different parent_hash or else it'll be
// related to our test genesis header
let mut bad_ancestor = test_header(0);
bad_ancestor.parent_hash = [1u8; 32].into();
let bad_ancestor = ImportedHeader {
header: bad_ancestor,
requires_justification: false,
is_finalized: false,
signal_hash: None,
};
let child = imported_headers.pop().unwrap();
let ancestors = headers_between(&storage, bad_ancestor, child);
assert!(ancestors.is_none());
})
}
#[test]
fn ancestor_newer_than_child_is_not_related() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let mut imported_headers = write_default_headers(&mut storage, vec![1, 2, 3]);
for header in imported_headers.iter() {
assert!(storage.header_exists(header.hash()));
}
// What if we have an "ancestor" that's newer than child?
let new_ancestor = test_header(5);
let new_ancestor = ImportedHeader {
header: new_ancestor,
requires_justification: false,
is_finalized: false,
signal_hash: None,
};
let child = imported_headers.pop().unwrap();
let ancestors = headers_between(&storage, new_ancestor, child);
assert!(ancestors.is_none());
})
}
#[test]
fn doesnt_import_header_which_schedules_change_with_invalid_authority_set() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let _imported_headers = write_default_headers(&mut storage, vec![1]);
let mut header = test_header(2);
// This is an *invalid* authority set because the combined weight of the
// authorities is greater than `u64::MAX`
let consensus_log = ConsensusLog::<TestNumber>::ScheduledChange(sp_finality_grandpa::ScheduledChange {
next_authorities: vec![(alice(), u64::MAX), (bob(), u64::MAX)],
delay: 0,
});
header.digest = Digest::<TestHash> {
logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())],
};
let mut verifier = Verifier { storage };
assert_eq!(
verifier.import_header(header.hash(), header).unwrap_err(),
ImportError::InvalidAuthoritySet
);
})
}
#[test]
fn finalizes_header_which_doesnt_enact_or_schedule_a_new_authority_set() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let _imported_headers = write_default_headers(&mut storage, vec![1]);
// Nothing special about this header, yet GRANDPA may have created a justification
// for it since it does that periodically
let header = test_header(2);
let set_id = 1;
let authorities = authority_list();
let authority_set = AuthoritySet::new(authorities.clone(), set_id);
storage.update_current_authority_set(authority_set);
// We'll need this justification to finalize the header
let grandpa_round = 1;
let justification = make_justification_for_header(&header, grandpa_round, set_id, &authorities).encode();
let mut verifier = Verifier {
storage: storage.clone(),
};
assert_ok!(verifier.import_header(header.hash(), header.clone()));
assert_ok!(verifier.import_finality_proof(header.hash(), justification.into()));
assert_eq!(storage.best_finalized_header().header, header);
})
}
#[test]
fn correctly_verifies_and_finalizes_chain_of_headers() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let imported_headers = write_default_headers(&mut storage, vec![1, 2]);
let header = test_header(3);
let set_id = 1;
let authorities = authority_list();
let authority_set = AuthoritySet {
authorities: authorities.clone(),
set_id,
};
storage.update_current_authority_set(authority_set);
let grandpa_round = 1;
let justification = make_justification_for_header(&header, grandpa_round, set_id, &authorities).encode();
let mut verifier = Verifier {
storage: storage.clone(),
};
assert!(verifier.import_header(header.hash(), header.clone()).is_ok());
assert!(verifier
.import_finality_proof(header.hash(), justification.into())
.is_ok());
// Make sure we marked the our headers as finalized
assert!(storage.header_by_hash(imported_headers[1].hash()).unwrap().is_finalized);
assert!(storage.header_by_hash(imported_headers[2].hash()).unwrap().is_finalized);
assert!(storage.header_by_hash(header.hash()).unwrap().is_finalized);
// Make sure the header at the highest height is the best finalized
assert_eq!(storage.best_finalized_header().header, header);
});
}
#[test]
fn updates_authority_set_upon_finalizing_header_which_enacts_change() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let genesis_hash = write_headers(&mut storage, vec![])[0].hash();
// We want this header to indicate that there's an upcoming set change on this fork
let parent = ImportedHeader {
header: test_header(1),
requires_justification: false,
is_finalized: false,
signal_hash: Some(genesis_hash),
};
storage.write_header(&parent);
let set_id = 1;
let authorities = authority_list();
let initial_authority_set = AuthoritySet::new(authorities.clone(), set_id);
storage.update_current_authority_set(initial_authority_set);
// This header enacts an authority set change upon finalization
let header = test_header(2);
let grandpa_round = 1;
let justification = make_justification_for_header(&header, grandpa_round, set_id, &authorities).encode();
// Schedule a change at the height of our header
let set_id = 2;
let height = *header.number();
let authorities = vec![alice()];
let change = schedule_next_change(authorities, set_id, height);
storage.schedule_next_set_change(genesis_hash, change.clone());
let mut verifier = Verifier {
storage: storage.clone(),
};
assert_ok!(verifier.import_header(header.hash(), header.clone()));
assert_eq!(storage.missing_justifications().len(), 1);
assert_eq!(storage.missing_justifications()[0].hash, header.hash());
assert_ok!(verifier.import_finality_proof(header.hash(), justification.into()));
assert_eq!(storage.best_finalized_header().header, header);
// Make sure that we have updated the set now that we've finalized our header
assert_eq!(storage.current_authority_set(), change.authority_set);
assert!(storage.missing_justifications().is_empty());
})
}
#[test]
fn importing_finality_proof_for_already_finalized_header_doesnt_work() {
run_test(|| {
let mut storage = PalletStorage::<TestRuntime>::new();
let genesis = test_header(0);
let genesis = ImportedHeader {
header: genesis,
requires_justification: false,
is_finalized: true,
signal_hash: None,
};
// Make sure that genesis is the best finalized header
<BestFinalized<TestRuntime>>::put(genesis.hash());
storage.write_header(&genesis);
let mut verifier = Verifier { storage };
// Now we want to try and import it again to see what happens
assert_eq!(
verifier
.import_finality_proof(genesis.hash(), vec![4, 2].into())
.unwrap_err(),
FinalizationError::OldHeader
);
});
}
}