From 2995f1c9bec69caf72fc3bab6c67017a5a39f3f0 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Fri, 15 Jan 2021 14:00:35 -0500 Subject: [PATCH] Move justification code to primitives crate (#640) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move justification module to header-chain primitives crate * Get justification module compiling in new location * Get justification module tests compiling * Use justification code from `header-chain` crate Mostly compiles, having issues with std/test feature flags across crates. * Move some code around * Move justification tests to integration testing crate * Add `test-utils` crate * Remove tests and test-helper module from justification code * Use `test-utils` in Substrate bridge pallet tests * Remove `sp-keyring` related code from `pallet-substrate-bridge` * Remove `helpers` module from `pallet-substrate-bridge` * Add some documentation * Add more documentation * Fix typo Co-authored-by: Tomasz Drwięga Co-authored-by: Tomasz Drwięga --- bridges/modules/substrate/Cargo.toml | 4 +- bridges/modules/substrate/src/fork_tests.rs | 7 +- bridges/modules/substrate/src/lib.rs | 6 +- bridges/modules/substrate/src/mock.rs | 77 ++------ bridges/modules/substrate/src/verifier.rs | 5 +- bridges/primitives/header-chain/Cargo.toml | 26 ++- .../header-chain}/src/justification.rs | 168 +----------------- bridges/primitives/header-chain/src/lib.rs | 4 +- .../header-chain/tests/justification.rs | 114 ++++++++++++ bridges/primitives/test-utils/Cargo.toml | 13 ++ bridges/primitives/test-utils/src/lib.rs | 151 ++++++++++++++++ bridges/relays/substrate/Cargo.toml | 1 + .../relays/substrate/src/headers_maintain.rs | 2 +- 13 files changed, 338 insertions(+), 240 deletions(-) rename bridges/{modules/substrate => primitives/header-chain}/src/justification.rs (58%) create mode 100644 bridges/primitives/header-chain/tests/justification.rs create mode 100644 bridges/primitives/test-utils/Cargo.toml create mode 100644 bridges/primitives/test-utils/src/lib.rs diff --git a/bridges/modules/substrate/Cargo.toml b/bridges/modules/substrate/Cargo.toml index 44022d6779b07..8eb253606101d 100644 --- a/bridges/modules/substrate/Cargo.toml +++ b/bridges/modules/substrate/Cargo.toml @@ -15,6 +15,7 @@ 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 @@ -27,14 +28,15 @@ sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "master 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-keyring = { 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", "codec/std", "finality-grandpa/std", diff --git a/bridges/modules/substrate/src/fork_tests.rs b/bridges/modules/substrate/src/fork_tests.rs index c71ed3938e1bb..a2529dec13d5e 100644 --- a/bridges/modules/substrate/src/fork_tests.rs +++ b/bridges/modules/substrate/src/fork_tests.rs @@ -54,11 +54,11 @@ //! 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::justification::tests::*; -use crate::mock::{helpers::*, *}; +use crate::mock::*; use crate::storage::{AuthoritySet, ImportedHeader}; use crate::verifier::*; use crate::{BestFinalized, BestHeight, BridgeStorage, NextScheduledChange, PalletStorage}; +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}; @@ -456,8 +456,7 @@ where 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 justification = make_justification_for_header(header, grandpa_round, set_id, &authorities).encode(); let res = verifier .import_finality_proof(header.hash(), justification.into()) diff --git a/bridges/modules/substrate/src/lib.rs b/bridges/modules/substrate/src/lib.rs index fbd6cab95734e..2129f0004979d 100644 --- a/bridges/modules/substrate/src/lib.rs +++ b/bridges/modules/substrate/src/lib.rs @@ -45,10 +45,8 @@ use sp_trie::StorageProof; // Re-export since the node uses these when configuring genesis pub use storage::{AuthoritySet, InitializationData, ScheduledChange}; -pub use justification::decode_justification_target; pub use storage_proof::StorageProofChecker; -mod justification; mod storage; mod storage_proof; mod verifier; @@ -622,8 +620,8 @@ impl BridgeStorage for PalletStorage { #[cfg(test)] mod tests { use super::*; - use crate::mock::helpers::{authority_list, test_header, unfinalized_header}; - use crate::mock::{run_test, Origin, TestRuntime}; + use crate::mock::{run_test, test_header, unfinalized_header, Origin, TestRuntime}; + use bp_test_utils::authority_list; use frame_support::{assert_noop, assert_ok}; use sp_runtime::DispatchError; diff --git a/bridges/modules/substrate/src/mock.rs b/bridges/modules/substrate/src/mock.rs index e6415aae8e411..e5b0c26c13ab2 100644 --- a/bridges/modules/substrate/src/mock.rs +++ b/bridges/modules/substrate/src/mock.rs @@ -16,11 +16,11 @@ //! Mock Runtime for Substrate Pallet Testing. //! -//! Includes some useful testing utilities in the `helpers` module. +//! Includes some useful testing types and functions. #![cfg(test)] -use crate::Config; +use crate::{BridgedBlockHash, BridgedBlockNumber, BridgedHeader, Config}; use bp_runtime::Chain; use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; use sp_runtime::{ @@ -30,6 +30,9 @@ use sp_runtime::{ }; pub type AccountId = u64; +pub type TestHeader = BridgedHeader; +pub type TestNumber = BridgedBlockNumber; +pub type TestHash = BridgedBlockHash; #[derive(Clone, Eq, PartialEq, Debug)] pub struct TestRuntime; @@ -88,66 +91,16 @@ pub fn run_test(test: impl FnOnce() -> T) -> T { sp_io::TestExternalities::new(Default::default()).execute_with(test) } -pub mod helpers { - use super::*; - use crate::storage::ImportedHeader; - use crate::{BridgedBlockHash, BridgedBlockNumber, BridgedHeader}; - use finality_grandpa::voter_set::VoterSet; - use sp_finality_grandpa::{AuthorityId, AuthorityList}; - use sp_keyring::Ed25519Keyring; - - pub type TestHeader = BridgedHeader; - pub type TestNumber = BridgedBlockNumber; - pub type TestHash = BridgedBlockHash; - pub type HeaderId = (TestHash, TestNumber); - - pub fn test_header(num: TestNumber) -> TestHeader { - let mut header = TestHeader::new_from_number(num); - header.parent_hash = if num == 0 { - Default::default() - } else { - test_header(num - 1).hash() - }; - - header - } - - pub fn unfinalized_header(num: u64) -> ImportedHeader { - ImportedHeader { - header: test_header(num), - requires_justification: false, - is_finalized: false, - signal_hash: None, - } - } - - pub fn header_id(index: u8) -> HeaderId { - (test_header(index.into()).hash(), index as _) - } - - pub fn extract_keyring(id: &AuthorityId) -> Ed25519Keyring { - let mut raw_public = [0; 32]; - raw_public.copy_from_slice(id.as_ref()); - Ed25519Keyring::from_raw_public(raw_public).unwrap() - } - - pub fn voter_set() -> VoterSet { - VoterSet::new(authority_list()).unwrap() - } - - pub fn authority_list() -> AuthorityList { - vec![(alice(), 1), (bob(), 1), (charlie(), 1)] - } - - pub fn alice() -> AuthorityId { - Ed25519Keyring::Alice.public().into() - } - - pub fn bob() -> AuthorityId { - Ed25519Keyring::Bob.public().into() - } +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 charlie() -> AuthorityId { - Ed25519Keyring::Charlie.public().into() +pub fn unfinalized_header(num: u64) -> crate::storage::ImportedHeader { + crate::storage::ImportedHeader { + header: test_header(num), + requires_justification: false, + is_finalized: false, + signal_hash: None, } } diff --git a/bridges/modules/substrate/src/verifier.rs b/bridges/modules/substrate/src/verifier.rs index 41139cfebeb0c..edbc77d54a746 100644 --- a/bridges/modules/substrate/src/verifier.rs +++ b/bridges/modules/substrate/src/verifier.rs @@ -22,9 +22,9 @@ //! has been signed off by the correct GRANDPA authorities, and also enact any authority set changes //! if required. -use crate::justification::verify_justification; use crate::storage::{AuthoritySet, ImportedHeader, ScheduledChange}; use crate::BridgeStorage; +use bp_header_chain::justification::verify_justification; use finality_grandpa::voter_set::VoterSet; use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; use sp_runtime::generic::OpaqueDigestItemId; @@ -350,10 +350,9 @@ fn find_scheduled_change(header: &H) -> Option"] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -[dependencies.parity-scale-codec] -version = "1.3.1" -default-features = false -features = ["derive"] +[dependencies] +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } +finality-grandpa = { version = "0.12.3", default-features = false } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "master" , default-features = false } +sp-core = { 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 } + +[dev-dependencies] +bp-test-utils = { path = "../test-utils" } +sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "master" } [features] default = ["std"] std = [ - "parity-scale-codec/std", + "codec/std", + "finality-grandpa/std", + "frame-support/std", + "sp-core/std", + "sp-finality-grandpa/std", + "sp-std/std", ] diff --git a/bridges/modules/substrate/src/justification.rs b/bridges/primitives/header-chain/src/justification.rs similarity index 58% rename from bridges/modules/substrate/src/justification.rs rename to bridges/primitives/header-chain/src/justification.rs index c285dd34b808d..54a44d9b7ad04 100644 --- a/bridges/modules/substrate/src/justification.rs +++ b/bridges/primitives/header-chain/src/justification.rs @@ -19,7 +19,7 @@ //! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin //! will ever be moved to the sp_finality_grandpa, we should reuse that implementation. -use codec::Decode; +use codec::{Decode, Encode}; use finality_grandpa::{voter_set::VoterSet, Chain, Error as GrandpaError}; use frame_support::RuntimeDebug; use sp_finality_grandpa::{AuthorityId, AuthoritySignature, SetId}; @@ -128,12 +128,14 @@ where /// /// This particular proof is used to prove that headers on a bridged chain /// (so not our chain) have been finalized correctly. -#[derive(Decode, RuntimeDebug)] -#[cfg_attr(test, derive(codec::Encode))] -pub(crate) struct GrandpaJustification { - round: u64, - commit: finality_grandpa::Commit, - votes_ancestries: Vec
, +#[derive(Encode, Decode, RuntimeDebug)] +pub struct GrandpaJustification { + /// The round (voting period) this justification is valid for. + pub round: u64, + /// The set of votes for the chain which is to be finalized. + pub commit: finality_grandpa::Commit, + /// A proof that the chain of blocks in the commit are related to each other. + pub votes_ancestries: Vec
, } /// A utility trait implementing `finality_grandpa::Chain` using a given set of headers. @@ -181,155 +183,3 @@ where unreachable!("is only used during voting; qed") } } - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use crate::mock::helpers::*; - use codec::Encode; - use sp_core::H256; - use sp_finality_grandpa::{AuthorityId, AuthorityWeight}; - use sp_keyring::Ed25519Keyring; - - const TEST_GRANDPA_ROUND: u64 = 1; - const TEST_GRANDPA_SET_ID: SetId = 1; - - pub(crate) fn signed_precommit( - signer: Ed25519Keyring, - target: HeaderId, - round: u64, - set_id: SetId, - ) -> finality_grandpa::SignedPrecommit { - let precommit = finality_grandpa::Precommit { - target_hash: target.0, - target_number: target.1, - }; - let encoded = sp_finality_grandpa::localized_payload( - round, - set_id, - &finality_grandpa::Message::Precommit(precommit.clone()), - ); - let signature = signer.sign(&encoded[..]).into(); - finality_grandpa::SignedPrecommit { - precommit, - signature, - id: signer.public().into(), - } - } - - pub(crate) fn make_justification_for_header( - header: &TestHeader, - round: u64, - set_id: SetId, - authorities: &[(AuthorityId, AuthorityWeight)], - ) -> GrandpaJustification { - let (target_hash, target_number) = (header.hash(), *header.number()); - let mut precommits = vec![]; - let mut votes_ancestries = vec![]; - - // We want to make sure that the header included in the vote ancestries - // is actually related to our target header - let mut precommit_header = test_header(target_number + 1); - precommit_header.parent_hash = target_hash; - - // I'm using the same header for all the voters since it doesn't matter as long - // as they all vote on blocks _ahead_ of the one we're interested in finalizing - for (id, _weight) in authorities.iter() { - let signer = extract_keyring(&id); - let precommit = signed_precommit( - signer, - (precommit_header.hash(), *precommit_header.number()), - round, - set_id, - ); - precommits.push(precommit); - votes_ancestries.push(precommit_header.clone()); - } - - GrandpaJustification { - round, - commit: finality_grandpa::Commit { - target_hash, - target_number, - precommits, - }, - votes_ancestries, - } - } - - pub(crate) fn make_justification_for_header_1() -> GrandpaJustification { - make_justification_for_header( - &test_header(1), - TEST_GRANDPA_ROUND, - TEST_GRANDPA_SET_ID, - &authority_list(), - ) - } - - #[test] - fn justification_with_invalid_encoding_rejected() { - assert_eq!( - verify_justification::(header_id(1), TEST_GRANDPA_SET_ID, voter_set(), &[],), - Err(Error::JustificationDecode), - ); - } - - #[test] - fn justification_with_invalid_target_rejected() { - assert_eq!( - verify_justification::( - header_id(2), - TEST_GRANDPA_SET_ID, - voter_set(), - &make_justification_for_header_1().encode(), - ), - Err(Error::InvalidJustificationTarget), - ); - } - - #[test] - fn justification_with_invalid_commit_rejected() { - let mut justification = make_justification_for_header_1(); - justification.commit.precommits.clear(); - - assert_eq!( - verify_justification::(header_id(1), TEST_GRANDPA_SET_ID, voter_set(), &justification.encode(),), - Err(Error::InvalidJustificationCommit), - ); - } - - #[test] - fn justification_with_invalid_authority_signature_rejected() { - let mut justification = make_justification_for_header_1(); - justification.commit.precommits[0].signature = Default::default(); - - assert_eq!( - verify_justification::(header_id(1), TEST_GRANDPA_SET_ID, voter_set(), &justification.encode(),), - Err(Error::InvalidAuthoritySignature), - ); - } - - #[test] - fn justification_with_invalid_precommit_ancestry() { - let mut justification = make_justification_for_header_1(); - justification.votes_ancestries.push(test_header(10)); - - assert_eq!( - verify_justification::(header_id(1), TEST_GRANDPA_SET_ID, voter_set(), &justification.encode(),), - Err(Error::InvalidPrecommitAncestries), - ); - } - - #[test] - fn valid_justification_accepted() { - assert_eq!( - verify_justification::( - header_id(1), - TEST_GRANDPA_SET_ID, - voter_set(), - &make_justification_for_header_1().encode(), - ), - Ok(()), - ); - } -} diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs index 7e5fa058cfe8c..00978d8081d1d 100644 --- a/bridges/primitives/header-chain/src/lib.rs +++ b/bridges/primitives/header-chain/src/lib.rs @@ -19,10 +19,12 @@ #![cfg_attr(not(feature = "std"), no_std)] +use codec::{Codec, EncodeLike}; use core::clone::Clone; use core::cmp::Eq; use core::fmt::Debug; -use parity_scale_codec::{Codec, EncodeLike}; + +pub mod justification; /// A type that can be used as a parameter in a dispatchable function. /// diff --git a/bridges/primitives/header-chain/tests/justification.rs b/bridges/primitives/header-chain/tests/justification.rs new file mode 100644 index 0000000000000..81bd83b1ad3b4 --- /dev/null +++ b/bridges/primitives/header-chain/tests/justification.rs @@ -0,0 +1,114 @@ +// 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 . + +//! Tests for Grandpa Justification code. + +use bp_header_chain::justification::{verify_justification, Error, GrandpaJustification}; +use bp_test_utils::*; +use codec::Encode; + +type TestHeader = sp_runtime::testing::Header; + +fn make_justification_for_header_1() -> GrandpaJustification { + make_justification_for_header( + &test_header(1), + TEST_GRANDPA_ROUND, + TEST_GRANDPA_SET_ID, + &authority_list(), + ) +} + +#[test] +fn justification_with_invalid_encoding_rejected() { + assert_eq!( + verify_justification::(header_id::(1), TEST_GRANDPA_SET_ID, voter_set(), &[],), + Err(Error::JustificationDecode), + ); +} + +#[test] +fn justification_with_invalid_target_rejected() { + assert_eq!( + verify_justification::( + header_id::(2), + TEST_GRANDPA_SET_ID, + voter_set(), + &make_justification_for_header_1().encode(), + ), + Err(Error::InvalidJustificationTarget), + ); +} + +#[test] +fn justification_with_invalid_commit_rejected() { + let mut justification = make_justification_for_header_1(); + justification.commit.precommits.clear(); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + voter_set(), + &justification.encode(), + ), + Err(Error::InvalidJustificationCommit), + ); +} + +#[test] +fn justification_with_invalid_authority_signature_rejected() { + let mut justification = make_justification_for_header_1(); + justification.commit.precommits[0].signature = Default::default(); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + voter_set(), + &justification.encode(), + ), + Err(Error::InvalidAuthoritySignature), + ); +} + +#[test] +fn justification_with_invalid_precommit_ancestry() { + let mut justification = make_justification_for_header_1(); + justification.votes_ancestries.push(test_header(10)); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + voter_set(), + &justification.encode(), + ), + Err(Error::InvalidPrecommitAncestries), + ); +} + +#[test] +fn valid_justification_accepted() { + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + voter_set(), + &make_justification_for_header_1().encode(), + ), + Ok(()), + ); +} diff --git a/bridges/primitives/test-utils/Cargo.toml b/bridges/primitives/test-utils/Cargo.toml new file mode 100644 index 0000000000000..aa330d4521f4b --- /dev/null +++ b/bridges/primitives/test-utils/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "bp-test-utils" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +finality-grandpa = { version = "0.12.3" } +bp-header-chain = { path = "../header-chain" } +sp-finality-grandpa = { git = "https://github.com/paritytech/substrate.git", branch = "master" } +sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "master" } diff --git a/bridges/primitives/test-utils/src/lib.rs b/bridges/primitives/test-utils/src/lib.rs new file mode 100644 index 0000000000000..182eb2cb796b7 --- /dev/null +++ b/bridges/primitives/test-utils/src/lib.rs @@ -0,0 +1,151 @@ +// Copyright 2021 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 . + +//! Utilities for testing runtime code. +//! +//! Unlike other crates in the `primitives` folder, this crate does *not* need to compile in a +//! `no_std` environment. This is fine because this code should only be used, as the name implies, +//! in tests. + +use bp_header_chain::justification::GrandpaJustification; +use finality_grandpa::voter_set::VoterSet; +use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight}; +use sp_finality_grandpa::{AuthoritySignature, SetId}; +use sp_keyring::Ed25519Keyring; +use sp_runtime::traits::Header as HeaderT; +use sp_runtime::traits::{One, Zero}; + +pub const TEST_GRANDPA_ROUND: u64 = 1; +pub const TEST_GRANDPA_SET_ID: SetId = 1; + +/// Get a valid Grandpa justification for a header given a Grandpa round, authority set ID, and +/// authority list. +pub fn make_justification_for_header( + header: &H, + round: u64, + set_id: SetId, + authorities: &[(AuthorityId, AuthorityWeight)], +) -> GrandpaJustification { + let (target_hash, target_number) = (header.hash(), *header.number()); + let mut precommits = vec![]; + let mut votes_ancestries = vec![]; + + // We want to make sure that the header included in the vote ancestries + // is actually related to our target header + let mut precommit_header = test_header::(target_number + One::one()); + precommit_header.set_parent_hash(target_hash); + + // I'm using the same header for all the voters since it doesn't matter as long + // as they all vote on blocks _ahead_ of the one we're interested in finalizing + for (id, _weight) in authorities.iter() { + let signer = extract_keyring(&id); + let precommit = signed_precommit::( + signer, + (precommit_header.hash(), *precommit_header.number()), + round, + set_id, + ); + precommits.push(precommit); + votes_ancestries.push(precommit_header.clone()); + } + + GrandpaJustification { + round, + commit: finality_grandpa::Commit { + target_hash, + target_number, + precommits, + }, + votes_ancestries, + } +} + +fn signed_precommit( + signer: Ed25519Keyring, + target: (H::Hash, H::Number), + round: u64, + set_id: SetId, +) -> finality_grandpa::SignedPrecommit { + let precommit = finality_grandpa::Precommit { + target_hash: target.0, + target_number: target.1, + }; + let encoded = + sp_finality_grandpa::localized_payload(round, set_id, &finality_grandpa::Message::Precommit(precommit.clone())); + let signature = signer.sign(&encoded[..]).into(); + finality_grandpa::SignedPrecommit { + precommit, + signature, + id: signer.public().into(), + } +} + +/// Get a header for testing. +/// +/// The correct parent hash will be used if given a non-zero header. +pub fn test_header(number: H::Number) -> H { + let mut header = H::new( + number, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ); + + if number != Zero::zero() { + let parent_hash = test_header::(number - One::one()).hash(); + header.set_parent_hash(parent_hash); + } + + header +} + +/// Convenience function for generating a Header ID at a given block number. +pub fn header_id(index: u8) -> (H::Hash, H::Number) { + (test_header::(index.into()).hash(), index.into()) +} + +/// Get the identity of a test account given an ED25519 Public key. +pub fn extract_keyring(id: &AuthorityId) -> Ed25519Keyring { + let mut raw_public = [0; 32]; + raw_public.copy_from_slice(id.as_ref()); + Ed25519Keyring::from_raw_public(raw_public).unwrap() +} + +/// Get a valid set of voters for a Grandpa round. +pub fn voter_set() -> VoterSet { + VoterSet::new(authority_list()).unwrap() +} + +/// Convenience function to get a list of Grandpa authorities. +pub fn authority_list() -> AuthorityList { + vec![(alice(), 1), (bob(), 1), (charlie(), 1)] +} + +/// Get the Public key of the Alice test account. +pub fn alice() -> AuthorityId { + Ed25519Keyring::Alice.public().into() +} + +/// Get the Public key of the Bob test account. +pub fn bob() -> AuthorityId { + Ed25519Keyring::Bob.public().into() +} + +/// Get the Public key of the Charlie test account. +pub fn charlie() -> AuthorityId { + Ed25519Keyring::Charlie.public().into() +} diff --git a/bridges/relays/substrate/Cargo.toml b/bridges/relays/substrate/Cargo.toml index 41b7d5ad7e344..1853b19b9524b 100644 --- a/bridges/relays/substrate/Cargo.toml +++ b/bridges/relays/substrate/Cargo.toml @@ -18,6 +18,7 @@ structopt = "0.3" # Bridge dependencies +bp-header-chain = { path = "../../primitives/header-chain" } bp-kusama = { path = "../../primitives/kusama" } bp-message-lane = { path = "../../primitives/message-lane" } bp-millau = { path = "../../primitives/millau" } diff --git a/bridges/relays/substrate/src/headers_maintain.rs b/bridges/relays/substrate/src/headers_maintain.rs index 0dac3cafb492b..d2996083b95ac 100644 --- a/bridges/relays/substrate/src/headers_maintain.rs +++ b/bridges/relays/substrate/src/headers_maintain.rs @@ -190,7 +190,7 @@ where }; // decode justification target - let target = pallet_substrate_bridge::decode_justification_target::(&justification); + let target = bp_header_chain::justification::decode_justification_target::(&justification); let target = match target { Ok((target_hash, target_number)) => HeaderId(target_number.into(), target_hash.into()), Err(error) => {