Skip to content
Snippets Groups Projects
Commit 8c7d0ca3 authored by Hernando Castano's avatar Hernando Castano Committed by Bastian Köcher
Browse files

Wire Finality Verifier Pallet Into Runtimes (#696)

* Add Finality Verifier pallet to runtimes

* Implement simple ancestry checker

* Use the new checker in runtimes

* Remove unused import warning

* Bump max allowed ancestry proof size

* Add a few optimization suggestions when verifying ancestry

* Use session length as upper bound for ancestry proof size

* Remove unused time units
parent d73100cb
Branches
No related merge requests found
......@@ -14,12 +14,14 @@ serde = { version = "1.0.121", optional = true, features = ["derive"] }
# Bridge dependencies
bp-header-chain = { path = "../../../primitives/header-chain", default-features = false }
bp-message-lane = { path = "../../../primitives/message-lane", default-features = false }
bp-millau = { path = "../../../primitives/millau", default-features = false }
bp-rialto = { path = "../../../primitives/rialto", default-features = false }
bp-runtime = { path = "../../../primitives/runtime", default-features = false }
bridge-runtime-common = { path = "../../runtime-common", default-features = false }
pallet-bridge-call-dispatch = { path = "../../../modules/call-dispatch", default-features = false }
pallet-finality-verifier = { path = "../../../modules/finality-verifier", default-features = false }
pallet-message-lane = { path = "../../../modules/message-lane", default-features = false }
pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false }
pallet-substrate-bridge = { path = "../../../modules/substrate", default-features = false }
......@@ -58,6 +60,7 @@ wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "2.
[features]
default = ["std"]
std = [
"bp-header-chain/std",
"bp-message-lane/std",
"bp-millau/std",
"bp-rialto/std",
......@@ -71,6 +74,7 @@ std = [
"pallet-aura/std",
"pallet-balances/std",
"pallet-bridge-call-dispatch/std",
"pallet-finality-verifier/std",
"pallet-grandpa/std",
"pallet-message-lane/std",
"pallet-randomness-collective-flip/std",
......
......@@ -132,15 +132,6 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
transaction_version: 1,
};
pub const MILLISECS_PER_BLOCK: u64 = 6000;
pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
// These time units are defined in number of blocks.
pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
pub const HOURS: BlockNumber = MINUTES * 60;
pub const DAYS: BlockNumber = HOURS * 24;
/// The version information used to identify this runtime when compiled natively.
#[cfg(feature = "std")]
pub fn native_version() -> NativeVersion {
......@@ -236,7 +227,7 @@ impl pallet_grandpa::Config for Runtime {
}
parameter_types! {
pub const MinimumPeriod: u64 = SLOT_DURATION / 2;
pub const MinimumPeriod: u64 = bp_millau::SLOT_DURATION / 2;
}
impl pallet_timestamp::Config for Runtime {
......@@ -287,7 +278,7 @@ impl pallet_sudo::Config for Runtime {
parameter_types! {
/// Authorities are changing every 5 minutes.
pub const Period: BlockNumber = 5 * MINUTES;
pub const Period: BlockNumber = bp_millau::SESSION_LENGTH;
pub const Offset: BlockNumber = 0;
}
......@@ -309,6 +300,19 @@ impl pallet_substrate_bridge::Config for Runtime {
type BridgedChain = bp_rialto::Rialto;
}
parameter_types! {
// We'll use the length of a session on the bridged chain as our bound since GRANDPA is
// guaranteed to produce a justification every session.
pub const MaxHeadersInSingleProof: bp_rialto::BlockNumber = bp_rialto::SESSION_LENGTH;
}
impl pallet_finality_verifier::Config for Runtime {
type BridgedChain = bp_rialto::Rialto;
type HeaderChain = pallet_substrate_bridge::Module<Runtime>;
type AncestryChecker = bp_header_chain::LinearAncestryChecker;
type MaxHeadersInSingleProof = MaxHeadersInSingleProof;
}
impl pallet_shift_session_manager::Config for Runtime {}
parameter_types! {
......@@ -362,6 +366,7 @@ construct_runtime!(
BridgeRialto: pallet_substrate_bridge::{Module, Call, Storage, Config<T>},
BridgeRialtoMessageLane: pallet_message_lane::{Module, Call, Storage, Event<T>},
BridgeCallDispatch: pallet_bridge_call_dispatch::{Module, Event<T>},
BridgeFinalityVerifier: pallet_finality_verifier::{Module, Call},
System: frame_system::{Module, Call, Config, Storage, Event<T>},
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},
Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent},
......
......@@ -27,6 +27,7 @@ bridge-runtime-common = { path = "../../runtime-common", default-features = fals
pallet-bridge-eth-poa = { path = "../../../modules/ethereum", default-features = false }
pallet-bridge-call-dispatch = { path = "../../../modules/call-dispatch", default-features = false }
pallet-bridge-currency-exchange = { path = "../../../modules/currency-exchange", default-features = false }
pallet-finality-verifier = { path = "../../../modules/finality-verifier", default-features = false }
pallet-substrate-bridge = { path = "../../../modules/substrate", default-features = false }
pallet-message-lane = { path = "../../../modules/message-lane", default-features = false }
pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false }
......@@ -90,6 +91,7 @@ std = [
"pallet-bridge-eth-poa/std",
"pallet-bridge-call-dispatch/std",
"pallet-bridge-currency-exchange/std",
"pallet-finality-verifier/std",
"pallet-grandpa/std",
"pallet-message-lane/std",
"pallet-randomness-collective-flip/std",
......
......@@ -140,15 +140,6 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
transaction_version: 1,
};
pub const MILLISECS_PER_BLOCK: u64 = 6000;
pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
// These time units are defined in number of blocks.
pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
pub const HOURS: BlockNumber = MINUTES * 60;
pub const DAYS: BlockNumber = HOURS * 24;
/// The version information used to identify this runtime when compiled natively.
#[cfg(feature = "std")]
pub fn native_version() -> NativeVersion {
......@@ -344,7 +335,7 @@ impl pallet_grandpa::Config for Runtime {
}
parameter_types! {
pub const MinimumPeriod: u64 = SLOT_DURATION / 2;
pub const MinimumPeriod: u64 = bp_rialto::SLOT_DURATION / 2;
}
impl pallet_timestamp::Config for Runtime {
......@@ -394,7 +385,7 @@ impl pallet_sudo::Config for Runtime {
}
parameter_types! {
pub const Period: BlockNumber = 4;
pub const Period: BlockNumber = bp_rialto::SESSION_LENGTH;
pub const Offset: BlockNumber = 0;
}
......@@ -416,6 +407,19 @@ impl pallet_substrate_bridge::Config for Runtime {
type BridgedChain = bp_millau::Millau;
}
parameter_types! {
// We'll use the length of a session on the bridged chain as our bound since GRANDPA is
// guaranteed to produce a justification every session.
pub const MaxHeadersInSingleProof: bp_millau::BlockNumber = bp_millau::SESSION_LENGTH;
}
impl pallet_finality_verifier::Config for Runtime {
type BridgedChain = bp_millau::Millau;
type HeaderChain = pallet_substrate_bridge::Module<Runtime>;
type AncestryChecker = bp_header_chain::LinearAncestryChecker;
type MaxHeadersInSingleProof = MaxHeadersInSingleProof;
}
impl pallet_shift_session_manager::Config for Runtime {}
parameter_types! {
......@@ -471,6 +475,7 @@ construct_runtime!(
BridgeRialtoCurrencyExchange: pallet_bridge_currency_exchange::<Instance1>::{Module, Call},
BridgeKovanCurrencyExchange: pallet_bridge_currency_exchange::<Instance2>::{Module, Call},
BridgeMillau: pallet_substrate_bridge::{Module, Call, Storage, Config<T>},
BridgeFinalityVerifier: pallet_finality_verifier::{Module, Call},
BridgeCallDispatch: pallet_bridge_call_dispatch::{Module, Event<T>},
BridgeMillauMessageLane: pallet_message_lane::{Module, Call, Storage, Event<T>},
System: frame_system::{Module, Call, Config, Storage, Event<T>},
......
......@@ -11,6 +11,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false }
finality-grandpa = { version = "0.12.3", default-features = false }
serde = { version = "1.0", optional = true }
num-traits = { version = "0.2", default-features = false }
# Bridge Dependencies
......@@ -39,6 +40,7 @@ std = [
"finality-grandpa/std",
"frame-support/std",
"frame-system/std",
"num-traits/std",
"serde",
"sp-runtime/std",
"sp-std/std",
......
......@@ -37,11 +37,16 @@ use bp_runtime::{Chain, HeaderOf};
use finality_grandpa::voter_set::VoterSet;
use frame_support::{dispatch::DispatchError, ensure, traits::Get};
use frame_system::ensure_signed;
use num_traits::AsPrimitive;
use sp_runtime::traits::Header as HeaderT;
use sp_std::vec::Vec;
#[cfg(test)]
mod mock;
// Re-export in crate namespace for `construct_runtime!`
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use super::*;
......@@ -69,7 +74,7 @@ pub mod pallet {
/// The maximum length of headers we can have in a single ancestry proof. This prevents
/// unbounded iteration when verifying proofs.
#[pallet::constant]
type MaxHeadersInSingleProof: Get<u8>;
type MaxHeadersInSingleProof: Get<<Self::BridgedChain as Chain>::BlockNumber>;
}
#[pallet::pallet]
......@@ -100,7 +105,7 @@ pub mod pallet {
let _ = ensure_signed(origin)?;
ensure!(
ancestry_proof.len() <= T::MaxHeadersInSingleProof::get() as usize,
ancestry_proof.len() <= T::MaxHeadersInSingleProof::get().as_(),
<Error<T>>::OversizedAncestryProof
);
......@@ -149,7 +154,6 @@ pub mod pallet {
#[cfg(test)]
mod tests {
use super::pallet::*;
use super::*;
use crate::mock::{run_test, test_header, Origin, TestRuntime};
use bp_test_utils::{authority_list, make_justification_for_header};
......
......@@ -27,7 +27,9 @@ use core::fmt::Debug;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_finality_grandpa::{AuthorityList, SetId};
use sp_runtime::traits::Header as HeaderT;
use sp_runtime::RuntimeDebug;
use sp_std::vec::Vec;
pub mod justification;
......@@ -110,3 +112,101 @@ impl<H, P> AncestryChecker<H, P> for () {
true
}
}
/// A simple ancestry checker which verifies ancestry by walking every header between `child` and
/// `ancestor`.
pub struct LinearAncestryChecker;
impl<H: HeaderT> AncestryChecker<H, Vec<H>> for LinearAncestryChecker {
fn are_ancestors(ancestor: &H, child: &H, proof: &Vec<H>) -> bool {
// You can't be your own parent
if proof.len() < 2 {
return false;
}
// Let's make sure that the given headers are actually in the proof
match proof.first() {
Some(first) if first == ancestor => {}
_ => return false,
}
match proof.last() {
Some(last) if last == child => {}
_ => return false,
}
// Now we actually check the proof
for i in 1..proof.len() {
if &proof[i - 1].hash() != proof[i].parent_hash() {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use bp_test_utils::test_header;
use sp_runtime::testing::Header;
#[test]
fn can_verify_ancestry_correctly() {
let ancestor: Header = test_header(1);
let header2: Header = test_header(2);
let header3: Header = test_header(3);
let child: Header = test_header(4);
let ancestry_proof = vec![ancestor.clone(), header2, header3, child.clone()];
assert!(LinearAncestryChecker::are_ancestors(&ancestor, &child, &ancestry_proof));
}
#[test]
fn does_not_verify_invalid_proof() {
let ancestor: Header = test_header(1);
let header2: Header = test_header(2);
let header3: Header = test_header(3);
let child: Header = test_header(4);
let ancestry_proof = vec![ancestor.clone(), header3, header2, child.clone()];
let invalid = !LinearAncestryChecker::are_ancestors(&ancestor, &child, &ancestry_proof);
assert!(invalid);
}
#[test]
fn header_is_not_allowed_to_be_its_own_ancestor() {
let ancestor: Header = test_header(1);
let child: Header = ancestor.clone();
let ancestry_proof = vec![ancestor.clone()];
let invalid = !LinearAncestryChecker::are_ancestors(&ancestor, &child, &ancestry_proof);
assert!(invalid);
}
#[test]
fn proof_is_considered_invalid_if_child_and_ancestor_do_not_match() {
let ancestor: Header = test_header(1);
let header2: Header = test_header(2);
let header3: Header = test_header(3);
let child: Header = test_header(4);
let ancestry_proof = vec![ancestor, header3.clone(), header2.clone(), child];
let invalid = !LinearAncestryChecker::are_ancestors(&header2, &header3, &ancestry_proof);
assert!(invalid);
}
#[test]
fn empty_proof_is_invalid() {
let ancestor: Header = test_header(1);
let child: Header = ancestor.clone();
let ancestry_proof = vec![];
let invalid = !LinearAncestryChecker::are_ancestors(&ancestor, &child, &ancestry_proof);
assert!(invalid);
}
}
......@@ -83,6 +83,24 @@ pub const MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_500_000_000;
/// runtime upgrades.
pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000_000;
/// The length of a session (how often authorities change) on Millau measured in of number of blocks.
pub const SESSION_LENGTH: BlockNumber = 5 * time_units::MINUTES;
/// Re-export `time_units` to make usage easier.
pub use time_units::*;
/// Human readable time units defined in terms of number of blocks.
pub mod time_units {
use super::BlockNumber;
pub const MILLISECS_PER_BLOCK: u64 = 6000;
pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
pub const HOURS: BlockNumber = MINUTES * 60;
pub const DAYS: BlockNumber = HOURS * 24;
}
/// Block number type used in Millau.
pub type BlockNumber = u64;
......
......@@ -74,6 +74,24 @@ pub const MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_500_000_000;
/// runtime upgrades.
pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000_000;
/// The length of a session (how often authorities change) on Rialto measured in of number of blocks.
pub const SESSION_LENGTH: BlockNumber = 4;
/// Re-export `time_units` to make usage easier.
pub use time_units::*;
/// Human readable time units defined in terms of number of blocks.
pub mod time_units {
use super::BlockNumber;
pub const MILLISECS_PER_BLOCK: u64 = 6000;
pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
pub const HOURS: BlockNumber = MINUTES * 60;
pub const DAYS: BlockNumber = HOURS * 24;
}
/// Block number type used in Rialto.
pub type BlockNumber = u32;
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment