Integrate Grandpa Proof Checker into Substrate Pallet (#375)

* Remove the Substrate primitives crate

The types here were only used in one place, the pallet itself. If other
components start using these types we can considering moving them back
into a standalone crate.

* Start trying to integrate justification module

* Make Substrate blocks configurable in Pallet

* WIP: Try and generalize justification test helpers

* Fix tests which use "real" justifications

* Put common test helpers alongside mock code

* Use common helper for creating headers

* Remove usage of UintAuthorityId

This change favours the use of the Ed25519Keyring authorities
in order to keep things consistent with the tests.

* Add documentation around config trait types

* Make test header, hash, and number types consistent

* Update modules/substrate/src/verifier.rs

Co-authored-by: Svyatoslav Nikolsky <svyatonik@gmail.com>

* Update modules/substrate/src/lib.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update modules/substrate/Cargo.toml

Co-authored-by: Svyatoslav Nikolsky <svyatonik@gmail.com>

* Derive `RuntimeDebug` instead of `Debug`

* Add `Paramter` as a trait constraint on config types

Since we use these types as part of the dispatchable functions
we should explicitly require this.

* Enforce that hasher output matches expected hash type

* Accept headers over indexes when making test justifications

* Check that authority sets are valid

* Make Clippy happy

* Apply correct Clippy fix

* Move justification code into primitives module

* Use new module in verifier code

* Add primitives module for Substrate test helpers

* WIP

* Move justification generation into test_helpers

* Revert commits which move `justification` into primitives

This reverts commit 03a381f0bc4a8dbe4785c30d42ab252a06ba876c.

Co-authored-by: Svyatoslav Nikolsky <svyatonik@gmail.com>
Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
This commit is contained in:
Hernando Castano
2020-09-30 04:58:28 -04:00
committed by Bastian Köcher
parent 52c1913fff
commit f9db999a1a
6 changed files with 368 additions and 176 deletions
+82 -25
View File
@@ -31,43 +31,100 @@
// Runtime-generated enums
#![allow(clippy::large_enum_variant)]
use bp_substrate::{AuthoritySet, ImportedHeader, ScheduledChange};
use frame_support::{decl_error, decl_module, decl_storage, dispatch};
use crate::storage::{AuthoritySet, ImportedHeader, ScheduledChange};
use codec::{Codec, EncodeLike};
use frame_support::{
decl_error, decl_module, decl_storage,
dispatch::{DispatchResult, Parameter},
};
use frame_system::ensure_signed;
use sp_runtime::traits::Header as HeaderT;
use sp_std::{marker::PhantomData, prelude::*};
use num_traits::AsPrimitive;
use sp_runtime::traits::{
AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay, MaybeMallocSizeOf, MaybeSerializeDeserialize,
Member, SimpleBitOps,
};
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*, str::FromStr};
mod justification;
mod storage;
mod storage_proof;
mod verifier;
#[cfg(test)]
mod mock;
type Hash<T> = <T as HeaderT>::Hash;
type Number<T> = <T as HeaderT>::Number;
pub trait Trait: frame_system::Trait {
/// A type that fulfills the abstract idea of what a Substrate header is.
// See here for more info:
// https://crates.parity.io/sp_runtime/traits/trait.Header.html
type BridgedHeader: Parameter + HeaderT<Number = Self::BridgedBlockNumber, Hash = Self::BridgedBlockHash>;
pub trait Trait: frame_system::Trait {}
/// A type that fulfills the abstract idea of what a Substrate block number is.
// Constraits come from the associated Number type of `sp_runtime::traits::Header`
// See here for more info:
// https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Number
//
// Note that the `AsPrimitive<usize>` trait is required by the Grandpa justification
// verifier, and is not usually part of a Substrate Header's Number type.
type BridgedBlockNumber: Parameter
+ Member
+ MaybeSerializeDeserialize
+ Debug
+ sp_std::hash::Hash
+ Copy
+ MaybeDisplay
+ AtLeast32BitUnsigned
+ Codec
+ FromStr
+ MaybeMallocSizeOf
+ AsPrimitive<usize>;
/// A type that fulfills the abstract idea of what a Substrate hash is.
// Constraits come from the associated Hash type of `sp_runtime::traits::Header`
// See here for more info:
// https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hash
type BridgedBlockHash: Parameter
+ Member
+ MaybeSerializeDeserialize
+ Debug
+ sp_std::hash::Hash
+ Ord
+ Copy
+ MaybeDisplay
+ Default
+ SimpleBitOps
+ Codec
+ AsRef<[u8]>
+ AsMut<[u8]>
+ MaybeMallocSizeOf
+ EncodeLike;
/// A type that fulfills the abstract idea of what a Substrate hasher (a type
/// that produces hashes) is.
// Constraits come from the associated Hashing type of `sp_runtime::traits::Header`
// See here for more info:
// https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hashing
type BridgedBlockHasher: HashT<Output = Self::BridgedBlockHash>;
}
decl_storage! {
trait Store for Module<T: Trait> as SubstrateBridge {
/// Hash of the best finalized header.
BestFinalized: T::Hash;
BestFinalized: T::BridgedBlockHash;
/// Headers which have been imported into the pallet.
ImportedHeaders: map hasher(identity) T::Hash => Option<ImportedHeader<T::Header>>;
ImportedHeaders: map hasher(identity) T::BridgedBlockHash => Option<ImportedHeader<T::BridgedHeader>>;
/// The current Grandpa Authority set.
CurrentAuthoritySet: AuthoritySet;
/// The next scheduled authority set change.
///
// Grandpa doesn't require there to always be a pending change. In fact, most of the time
// there will be no pending change available.
NextScheduledChange: Option<ScheduledChange<Number<T::Header>>>;
NextScheduledChange: Option<ScheduledChange<T::BridgedBlockNumber>>;
}
add_extra_genesis {
config(initial_header): Option<T::Header>;
config(initial_header): Option<T::BridgedHeader>;
config(initial_authority_list): sp_finality_grandpa::AuthorityList;
config(initial_set_id): sp_finality_grandpa::SetId;
config(first_scheduled_change): Option<ScheduledChange<Number<T::Header>>>;
config(first_scheduled_change): Option<ScheduledChange<T::BridgedBlockNumber>>;
build(|config| {
assert!(
!config.initial_authority_list.is_empty(),
@@ -122,8 +179,8 @@ decl_module! {
#[weight = 0]
pub fn import_signed_header(
origin,
header: T::Header,
) -> dispatch::DispatchResult {
header: T::BridgedHeader,
) -> DispatchResult {
let _ = ensure_signed(origin)?;
frame_support::debug::trace!(target: "sub-bridge", "Got header {:?}", header);
@@ -147,9 +204,9 @@ decl_module! {
#[weight = 0]
pub fn finalize_header(
origin,
hash: Hash<T::Header>,
hash: T::BridgedBlockHash,
finality_proof: Vec<u8>,
) -> dispatch::DispatchResult {
) -> DispatchResult {
let _ = ensure_signed(origin)?;
frame_support::debug::trace!(target: "sub-bridge", "Got header hash {:?}", hash);
@@ -220,28 +277,28 @@ impl<T> PalletStorage<T> {
}
impl<T: Trait> BridgeStorage for PalletStorage<T> {
type Header = T::Header;
type Header = T::BridgedHeader;
fn write_header(&mut self, header: &ImportedHeader<T::Header>) {
fn write_header(&mut self, header: &ImportedHeader<T::BridgedHeader>) {
let hash = header.header.hash();
<ImportedHeaders<T>>::insert(hash, header);
}
fn best_finalized_header(&self) -> ImportedHeader<T::Header> {
fn best_finalized_header(&self) -> ImportedHeader<T::BridgedHeader> {
let hash = <BestFinalized<T>>::get();
self.header_by_hash(hash)
.expect("A finalized header was added at genesis, therefore this must always exist")
}
fn update_best_finalized(&self, hash: Hash<T::Header>) {
fn update_best_finalized(&self, hash: T::BridgedBlockHash) {
<BestFinalized<T>>::put(hash)
}
fn header_exists(&self, hash: Hash<T::Header>) -> bool {
fn header_exists(&self, hash: T::BridgedBlockHash) -> bool {
<ImportedHeaders<T>>::contains_key(hash)
}
fn header_by_hash(&self, hash: Hash<T::Header>) -> Option<ImportedHeader<T::Header>> {
fn header_by_hash(&self, hash: T::BridgedBlockHash) -> Option<ImportedHeader<T::BridgedHeader>> {
<ImportedHeaders<T>>::get(hash)
}
@@ -266,11 +323,11 @@ impl<T: Trait> BridgeStorage for PalletStorage<T> {
}
}
fn scheduled_set_change(&self) -> Option<ScheduledChange<Number<T::Header>>> {
fn scheduled_set_change(&self) -> Option<ScheduledChange<T::BridgedBlockNumber>> {
<NextScheduledChange<T>>::get()
}
fn schedule_next_set_change(&self, next_change: ScheduledChange<Number<T::Header>>) {
fn schedule_next_set_change(&self, next_change: ScheduledChange<T::BridgedBlockNumber>) {
<NextScheduledChange<T>>::put(next_change)
}
}