Unverified Commit e8050450 authored by Tomasz Drwięga's avatar Tomasz Drwięga Committed by GitHub
Browse files

Merkle Mountain Range & BEEFY integration (#2101)

* Switch branch.

* Implement basic MMR leaf.

* Revert "Switch branch."

This reverts commit 7f4d41c6

.

* Bump substrate.

* Integrate BEEFY.

Bump all.

Fix missing imports.

* Use beefy pallet to get authorities.

* Bump BEEFY repo.

* Use next authority set instead of the current one.

* Start BEEFY service.

* Fix BEEFY start up.

* Cache BEEFY authority set.

* Add BEEFY ValidatorSetId to MMR

* Fix code.

* Apply suggestions from code review
Co-authored-by: default avatarAndré Silva <123550+andresilva@users.noreply.github.com>
Co-authored-by: default avatarHernando Castano <HCastano@users.noreply.github.com>

* Review grumbles.

* Update beefy repo.

* Work-around missing protocol.

* Revert "Work-around missing protocol."

This reverts commit 0a6257a8

.

* Add beefy peers set config.

* Expose storage of BEEFY.

* Uncompress BEEFY keys for merkle tree.

* Update ordering.

* Switch to branch.

* Bump deps.

* Switch to custom beefy.

* Add MMR RuntimeApi and custom rpc.

* Add set length details.

* Fix compilation.

* Expose MmrLeaf storage.

* Expose MmrLeaf storage.

* Don't use session handler, and rather compute & cache beefy details on call.

* Don't use session handler, and rather compute & cache beefy details on call.

* Fixes.

* Update Cargo.lock.

* Switch back to master.

* Update lockfile.

* Fix xcm print issue.

* Cargo.lock.

* Use master branch.

* Remove extra dep.

* Fix tests.

* Update Cargo.lock

* Add BEEFY & MMR to westend.

* Implement session keys migration.

* Update testnet script.

* start BEEFY for all node types

* Update Cargo.lock

* fix Cargo.toml

* resolve another merge conflict

* add Westend BEEFY keys

* Apply suggestions from code review
Co-authored-by: default avatarHernando Castano <HCastano@users.noreply.github.com>

* Update BEEFY.

* Add Rococo BEEFY keys

* resolve merge issue

* fix pallet indices

* fix Westend OldSessionKey

* remove unused imports in Westend runtime

* Fix compilation for Westend.

* address review

* start BEEFY gadget conditionally

* address review again

* fix typo

* remove duplicate

* remove another duplicate

* well

* add missing stuff

* cleanup Cargo.toml files

- revert unnecessary changes
- add missing /std dependencies
- remove unused dependencies

* runtime: remove unused structs from rococo runtime

* node: cleanup service
Co-authored-by: default avatarAndré Silva <123550+andresilva@users.noreply.github.com>
Co-authored-by: default avatarHernando Castano <HCastano@users.noreply.github.com>
Co-authored-by: default avataradoerr <0xad@gmx.net>
Co-authored-by: default avatarAndré Silva <andrerfosilva@gmail.com>
parent 55c9e96d
Pipeline #132235 failed with stages
in 28 minutes and 10 seconds
This diff is collapsed.
......@@ -8,6 +8,8 @@ edition = "2018"
# Substrate Client
sc-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" }
babe = { package = "sc-consensus-babe", git = "https://github.com/paritytech/substrate", branch = "master" }
beefy-primitives = { git = "https://github.com/paritytech/grandpa-bridge-gadget", branch = "master" }
beefy-gadget = { git = "https://github.com/paritytech/grandpa-bridge-gadget", branch = "master" }
grandpa = { package = "sc-finality-grandpa", git = "https://github.com/paritytech/substrate", branch = "master" }
sc-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" }
......@@ -48,6 +50,7 @@ pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "maste
pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" }
# Substrate Other
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" }
......
This diff is collapsed.
......@@ -17,6 +17,7 @@
//! Polkadot Client meta trait
use std::sync::Arc;
use beefy_primitives::ecdsa::AuthorityId as BeefyId;
use sp_api::{ProvideRuntimeApi, CallApiAt, NumberFor};
use sp_blockchain::HeaderBackend;
use sp_runtime::{
......@@ -36,11 +37,13 @@ pub trait RuntimeApiCollection:
+ ParachainHost<Block>
+ sp_block_builder::BlockBuilder<Block>
+ frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce>
+ pallet_mmr_primitives::MmrApi<Block, <Block as BlockT>::Hash>
+ pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance>
+ sp_api::Metadata<Block>
+ sp_offchain::OffchainWorkerApi<Block>
+ sp_session::SessionKeys<Block>
+ sp_authority_discovery::AuthorityDiscoveryApi<Block>
+ beefy_primitives::BeefyApi<Block, BeefyId>
where
<Self as sp_api::ApiExt<Block>>::StateBackend: sp_api::StateBackend<BlakeTwo256>,
{}
......@@ -54,11 +57,13 @@ where
+ ParachainHost<Block>
+ sp_block_builder::BlockBuilder<Block>
+ frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce>
+ pallet_mmr_primitives::MmrApi<Block, <Block as BlockT>::Hash>
+ pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance>
+ sp_api::Metadata<Block>
+ sp_offchain::OffchainWorkerApi<Block>
+ sp_session::SessionKeys<Block>
+ sp_authority_discovery::AuthorityDiscoveryApi<Block>,
+ sp_authority_discovery::AuthorityDiscoveryApi<Block>
+ beefy_primitives::BeefyApi<Block, BeefyId>,
<Self as sp_api::ApiExt<Block>>::StateBackend: sp_api::StateBackend<BlakeTwo256>,
{}
......
......@@ -40,6 +40,7 @@ use {
sc_keystore::LocalKeystore,
babe_primitives::BabeApi,
grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider},
beefy_primitives::ecdsa::AuthoritySignature as BeefySignature,
sp_runtime::traits::Header as HeaderT,
};
#[cfg(feature = "real-overseer")]
......@@ -243,7 +244,8 @@ fn new_partial<RuntimeApi, Executor>(
Block, FullClient<RuntimeApi, Executor>, FullGrandpaBlockImport<RuntimeApi, Executor>
>,
grandpa::LinkHalf<Block, FullClient<RuntimeApi, Executor>, FullSelectChain>,
babe::BabeLink<Block>
babe::BabeLink<Block>,
beefy_gadget::notification::BeefySignedCommitmentSender<Block, BeefySignature>,
),
grandpa::SharedVoterState,
std::time::Duration, // slot-duration
......@@ -342,6 +344,9 @@ fn new_partial<RuntimeApi, Executor>(
telemetry.as_ref().map(|x| x.handle()),
)?;
let (beefy_link, beefy_commitment_stream) =
beefy_gadget::notification::BeefySignedCommitmentStream::channel();
let justification_stream = grandpa_link.justification_stream();
let shared_authority_set = grandpa_link.shared_authority_set().clone();
let shared_voter_state = grandpa::SharedVoterState::empty();
......@@ -350,7 +355,7 @@ fn new_partial<RuntimeApi, Executor>(
Some(shared_authority_set.clone()),
);
let import_setup = (block_import.clone(), grandpa_link, babe_link.clone());
let import_setup = (block_import.clone(), grandpa_link, babe_link.clone(), beefy_link);
let rpc_setup = shared_voter_state.clone();
let shared_epoch_changes = babe_link.epoch_changes().clone();
......@@ -363,7 +368,9 @@ fn new_partial<RuntimeApi, Executor>(
let select_chain = select_chain.clone();
let chain_spec = config.chain_spec.cloned_box();
move |deny_unsafe, subscription_executor| -> polkadot_rpc::RpcExtension {
move |deny_unsafe, subscription_executor: polkadot_rpc::SubscriptionTaskExecutor|
-> polkadot_rpc::RpcExtension
{
let deps = polkadot_rpc::FullDeps {
client: client.clone(),
pool: transaction_pool.clone(),
......@@ -379,9 +386,13 @@ fn new_partial<RuntimeApi, Executor>(
shared_voter_state: shared_voter_state.clone(),
shared_authority_set: shared_authority_set.clone(),
justification_stream: justification_stream.clone(),
subscription_executor,
subscription_executor: subscription_executor.clone(),
finality_provider: finality_proof_provider.clone(),
},
beefy: polkadot_rpc::BeefyDeps {
beefy_commitment_stream: beefy_commitment_stream.clone(),
subscription_executor,
},
};
polkadot_rpc::create_full(deps)
......@@ -731,6 +742,11 @@ pub fn new_full<RuntimeApi, Executor>(
// anything in terms of behaviour, but makes the logs more consistent with the other
// Substrate nodes.
config.network.extra_sets.push(grandpa::grandpa_peers_set_config());
if config.chain_spec.is_westend() || config.chain_spec.is_rococo() {
config.network.extra_sets.push(beefy_gadget::beefy_peers_set_config());
}
#[cfg(feature = "real-overseer")]
{
use polkadot_network_bridge::{peer_sets_info, IsAuthority};
......@@ -824,6 +840,7 @@ pub fn new_full<RuntimeApi, Executor>(
}
let availability_config = config.database.clone().try_into().map_err(Error::Availability)?;
let chain_spec = config.chain_spec.cloned_box();
let approval_voting_config = ApprovalVotingConfig {
path: config.database.path()
......@@ -849,7 +866,7 @@ pub fn new_full<RuntimeApi, Executor>(
telemetry: telemetry.as_mut(),
})?;
let (block_import, link_half, babe_link) = import_setup;
let (block_import, link_half, babe_link, beefy_link) = import_setup;
let overseer_client = client.clone();
let spawner = task_manager.spawn_handle();
......@@ -967,6 +984,29 @@ pub fn new_full<RuntimeApi, Executor>(
task_manager.spawn_essential_handle().spawn_blocking("babe", babe);
}
// We currently only run the BEEFY gadget on Rococo and Westend test
// networks. On Rococo we start the BEEFY gadget as a normal (non-essential)
// task for now, since BEEFY is still experimental and we don't want a
// failure to bring down the whole node. Westend test network is less used
// than Rococo and therefore a failure there will be less problematic, this
// will be the main testing target for BEEFY for now.
if chain_spec.is_westend() || chain_spec.is_rococo() {
let gadget = beefy_gadget::start_beefy_gadget::<_, beefy_primitives::ecdsa::AuthorityPair, _, _, _, _>(
client.clone(),
keystore_container.sync_keystore(),
network.clone(),
beefy_link,
network.clone(),
prometheus_registry.clone()
);
if chain_spec.is_westend() {
task_manager.spawn_essential_handle().spawn_blocking("beefy-gadget", gadget);
} else {
task_manager.spawn_handle().spawn_blocking("beefy-gadget", gadget);
}
}
// if the node isn't actively participating in consensus then it doesn't
// need a keystore, regardless of which protocol we use below.
let keystore_opt = if role.is_authority() {
......
......@@ -7,24 +7,27 @@ edition = "2018"
[dependencies]
jsonrpc-core = "15.1.0"
polkadot-primitives = { path = "../primitives" }
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master"}
sc-consensus-babe-rpc = { git = "https://github.com/paritytech/substrate", branch = "master"}
sc-consensus-epochs = { git = "https://github.com/paritytech/substrate", branch = "master"}
sc-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-consensus-babe-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-consensus-epochs = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-finality-grandpa-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master"}
sc-sync-state-rpc = { git = "https://github.com/paritytech/substrate", branch = "master"}
sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-sync-state-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" }
txpool-api = { package = "sp-transaction-pool", git = "https://github.com/paritytech/substrate", branch = "master" }
frame-rpc-system = { package = "substrate-frame-rpc-system", git = "https://github.com/paritytech/substrate", branch = "master" }
frame-rpc-system = { package = "substrate-frame-rpc-system", git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-mmr-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" }
parity-scale-codec = { version = "2.0.0", default-features = false }
sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
beefy-gadget = { git = "https://github.com/paritytech/grandpa-bridge-gadget", branch = "master" }
beefy-gadget-rpc = { git = "https://github.com/paritytech/grandpa-bridge-gadget", branch = "master" }
......@@ -74,8 +74,16 @@ pub struct GrandpaDeps<B> {
pub finality_provider: Arc<FinalityProofProvider<B, Block>>,
}
/// Dependencies for BEEFY
pub struct BeefyDeps<BeefySignature> {
/// Receives notifications about signed commitment events from BEEFY.
pub beefy_commitment_stream: beefy_gadget::notification::BeefySignedCommitmentStream<Block, BeefySignature>,
/// Executor to drive the subscription manager in the BEEFY RPC handler.
pub subscription_executor: sc_rpc::SubscriptionTaskExecutor,
}
/// Full client dependencies
pub struct FullDeps<C, P, SC, B> {
pub struct FullDeps<C, P, SC, B, BS> {
/// The client instance to use.
pub client: Arc<C>,
/// Transaction pool instance.
......@@ -90,13 +98,16 @@ pub struct FullDeps<C, P, SC, B> {
pub babe: BabeDeps,
/// GRANDPA specific dependencies.
pub grandpa: GrandpaDeps<B>,
/// BEEFY specific dependencies.
pub beefy: BeefyDeps<BS>,
}
/// Instantiate all RPC extensions.
pub fn create_full<C, P, SC, B>(deps: FullDeps<C, P, SC, B>) -> RpcExtension where
pub fn create_full<C, P, SC, B, BS>(deps: FullDeps<C, P, SC, B, BS>) -> RpcExtension where
C: ProvideRuntimeApi<Block> + HeaderBackend<Block> + AuxStore +
HeaderMetadata<Block, Error=BlockChainError> + Send + Sync + 'static,
C::Api: frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>,
C::Api: pallet_mmr_rpc::MmrRuntimeApi<Block, <Block as sp_runtime::traits::Block>::Hash>,
C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
C::Api: BabeApi<Block>,
C::Api: BlockBuilder<Block>,
......@@ -104,11 +115,13 @@ pub fn create_full<C, P, SC, B>(deps: FullDeps<C, P, SC, B>) -> RpcExtension whe
SC: SelectChain<Block> + 'static,
B: sc_client_api::Backend<Block> + Send + Sync + 'static,
B::State: sc_client_api::StateBackend<sp_runtime::traits::HashFor<Block>>,
BS: Clone + Send + parity_scale_codec::Encode + 'static,
{
use frame_rpc_system::{FullSystem, SystemApi};
use pallet_mmr_rpc::{MmrApi, Mmr};
use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi};
use sc_finality_grandpa_rpc::{GrandpaApi, GrandpaRpcHandler};
use sc_consensus_babe_rpc::BabeRpcHandler;
use sc_finality_grandpa_rpc::{GrandpaApi, GrandpaRpcHandler};
let mut io = jsonrpc_core::IoHandler::default();
let FullDeps {
......@@ -119,6 +132,7 @@ pub fn create_full<C, P, SC, B>(deps: FullDeps<C, P, SC, B>) -> RpcExtension whe
deny_unsafe,
babe,
grandpa,
beefy,
} = deps;
let BabeDeps {
keystore,
......@@ -139,6 +153,9 @@ pub fn create_full<C, P, SC, B>(deps: FullDeps<C, P, SC, B>) -> RpcExtension whe
io.extend_with(
TransactionPaymentApi::to_delegate(TransactionPayment::new(client.clone()))
);
io.extend_with(
MmrApi::to_delegate(Mmr::new(client.clone()))
);
io.extend_with(
sc_consensus_babe_rpc::BabeApi::to_delegate(
BabeRpcHandler::new(
......@@ -169,6 +186,14 @@ pub fn create_full<C, P, SC, B>(deps: FullDeps<C, P, SC, B>) -> RpcExtension whe
deny_unsafe,
))
);
io.extend_with(beefy_gadget_rpc::BeefyApi::to_delegate(
beefy_gadget_rpc::BeefyRpcHandler::new(
beefy.beefy_commitment_stream,
beefy.subscription_executor,
),
));
io
}
......
......@@ -8,12 +8,13 @@ edition = "2018"
impl-trait-for-tuples = "0.2.0"
bitvec = { version = "0.20.1", default-features = false, features = ["alloc"] }
parity-scale-codec = { version = "2.0.0", default-features = false, features = ["derive"] }
log = { version = "0.4.13", optional = true }
log = { version = "0.4.13", default-features = false }
rustc-hex = { version = "2.1.0", default-features = false }
serde = { version = "1.0.123", default-features = false }
serde_derive = { version = "1.0.117", optional = true }
static_assertions = "1.1.0"
beefy-primitives = { git = "https://github.com/paritytech/grandpa-bridge-gadget", branch = "master", default-features = false }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
......@@ -35,11 +36,14 @@ pallet-offences = { git = "https://github.com/paritytech/substrate", branch = "m
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-treasury = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-beefy = { git = "https://github.com/paritytech/grandpa-bridge-gadget", branch = "master", default-features = false }
pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features=false, optional = true }
primitives = { package = "polkadot-primitives", path = "../../primitives", default-features = false }
libsecp256k1 = { version = "0.3.5", default-features = false, optional = true }
libsecp256k1 = { version = "0.3.5", default-features = false }
runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parachains", default-features = false }
xcm = { path = "../../xcm", default-features = false }
......@@ -63,9 +67,10 @@ libsecp256k1 = "0.3.5"
default = ["std"]
no_std = []
std = [
"beefy-primitives/std",
"bitvec/std",
"parity-scale-codec/std",
"log",
"log/std",
"rustc-hex/std",
"serde_derive",
"serde/std",
......@@ -78,6 +83,8 @@ std = [
"frame-support/std",
"pallet-authorship/std",
"pallet-balances/std",
"pallet-beefy/std",
"pallet-mmr/std",
"pallet-session/std",
"pallet-staking/std",
"pallet-timestamp/std",
......@@ -88,6 +95,7 @@ std = [
"sp-session/std",
"sp-staking/std",
"frame-system/std",
"libsecp256k1/std",
"runtime-parachains/std",
"xcm/std",
]
......
......@@ -25,14 +25,16 @@ pub mod auctions;
pub mod crowdloan;
pub mod purchase;
pub mod impls;
pub mod mmr;
pub mod paras_sudo_wrapper;
pub mod paras_registrar;
pub mod traits;
pub mod xcm_sender;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod integration_tests;
pub mod xcm_sender;
use primitives::v1::{BlockNumber, ValidatorId, AssignmentId};
use sp_runtime::{Perquintill, Perbill, FixedPointNumber};
......
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot 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.
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! A pallet responsible for creating Merkle Mountain Range (MMR) leaf for current block.
use beefy_primitives::ValidatorSetId;
use sp_core::H256;
use sp_runtime::traits::Convert;
use sp_std::prelude::*;
use frame_support::{decl_module, decl_storage, RuntimeDebug};
use pallet_mmr::primitives::LeafDataProvider;
use parity_scale_codec::{Encode, Decode};
use runtime_parachains::paras;
/// A BEEFY consensus digest item with MMR root hash.
pub struct DepositBeefyDigest<T>(sp_std::marker::PhantomData<T>);
impl<T> pallet_mmr::primitives::OnNewRoot<beefy_primitives::MmrRootHash> for DepositBeefyDigest<T> where
T: pallet_mmr::Config<Hash = beefy_primitives::MmrRootHash>,
T: pallet_beefy::Config,
{
fn on_new_root(root: &<T as pallet_mmr::Config>::Hash) {
let digest = sp_runtime::generic::DigestItem::Consensus(
beefy_primitives::BEEFY_ENGINE_ID,
parity_scale_codec::Encode::encode(
&beefy_primitives::ConsensusLog::<<T as pallet_beefy::Config>::AuthorityId>::MmrRoot(*root)
),
);
<frame_system::Pallet<T>>::deposit_log(digest);
}
}
/// Convert BEEFY secp256k1 public keys into uncompressed form
pub struct UncompressBeefyEcdsaKeys;
impl Convert<beefy_primitives::ecdsa::AuthorityId, Vec<u8>> for UncompressBeefyEcdsaKeys {
fn convert(a: beefy_primitives::ecdsa::AuthorityId) -> Vec<u8> {
use sp_core::crypto::Public;
let compressed_key = a.as_slice();
// TODO [ToDr] Temporary workaround until we have a better way to get uncompressed keys.
secp256k1::PublicKey::parse_slice(compressed_key, Some(secp256k1::PublicKeyFormat::Compressed))
.map(|pub_key| pub_key.serialize().to_vec())
.map_err(|_| {
log::error!(target: "runtime::beefy", "Invalid BEEFY PublicKey format!");
})
.unwrap_or_default()
}
}
/// A leaf that gets added every block to the MMR constructed by [pallet_mmr].
#[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode)]
pub struct MmrLeaf<BlockNumber, Hash, MerkleRoot> {
/// Current block parent number and hash.
pub parent_number_and_hash: (BlockNumber, Hash),
/// A merkle root of all registered parachain heads.
pub parachain_heads: MerkleRoot,
/// A merkle root of the next BEEFY authority set.
pub beefy_next_authority_set: BeefyNextAuthoritySet<MerkleRoot>,
}
/// Details of the next BEEFY authority set.
#[derive(RuntimeDebug, Default, PartialEq, Eq, Clone, Encode, Decode)]
pub struct BeefyNextAuthoritySet<MerkleRoot> {
/// Id of the next set.
///
/// Id is required to correlate BEEFY signed commitments with the validator set.
/// Light Client can easily verify that the commitment witness it is getting is
/// produced by the latest validator set.
pub id: ValidatorSetId,
/// Number of validators in the set.
///
/// Some BEEFY Light Clients may use an interactive protocol to verify only subset
/// of signatures. We put set length here, so that these clients can verify the minimal
/// number of required signatures.
pub len: u32,
/// Merkle Root Hash build from BEEFY AuthorityIds.
///
/// This is used by Light Clients to confirm that the commitments are signed by the correct
/// validator set. Light Clients using interactive protocol, might verify only subset of
/// signatures, hence don't require the full list here (will receive inclusion proofs).
pub root: MerkleRoot,
}
type MerkleRootOf<T> = <T as pallet_mmr::Config>::Hash;
/// The module's configuration trait.
pub trait Config: pallet_mmr::Config + pallet_beefy::Config {
/// Convert BEEFY AuthorityId to a form that would end up in the Merkle Tree.
///
/// For instance for ECDSA (secp256k1) we want to store uncompressed public keys (65 bytes)
/// to simplify using them on Ethereum chain, but the rest of the Substrate codebase
/// is storing them compressed (33 bytes) for efficiency reasons.
type BeefyAuthorityToMerkleLeaf: Convert<<Self as pallet_beefy::Config>::AuthorityId, Vec<u8>>;
/// Retrieve a list of current parachain heads.
///
/// The trait is implemented for `paras` module, but since not all chains might have parachains,
/// and we want to keep the MMR leaf structure uniform, it's possible to use `()` as well to
/// simply put dummy data to the leaf.
type ParachainHeads: ParachainHeadsProvider;
}
/// A type that is able to return current list of parachain heads that end up in the MMR leaf.
pub trait ParachainHeadsProvider {
/// Return a list of encoded parachain heads.
fn encoded_heads() -> Vec<Vec<u8>>;
}
/// A default implementation for runtimes without parachains.
impl ParachainHeadsProvider for () {
fn encoded_heads() -> Vec<Vec<u8>> {
Default::default()
}
}
impl<T: Config + paras::Config> ParachainHeadsProvider for paras::Pallet<T> {
fn encoded_heads() -> Vec<Vec<u8>> {
paras::Pallet::<T>::parachains()
.into_iter()
.map(paras::Pallet::<T>::para_head)
.map(|maybe_para_head| maybe_para_head.encode())
.collect()
}
}
decl_storage! {
trait Store for Pallet<T: Config> as Beefy {
/// Details of next BEEFY authority set.
///
/// This storage entry is used as cache for calls to [`update_beefy_next_authority_set`].
pub BeefyNextAuthorities get(fn beefy_next_authorities): BeefyNextAuthoritySet<MerkleRootOf<T>>;
}
}
decl_module! {
pub struct Module<T: Config> for enum Call where origin: <T as frame_system::Config>::Origin {
}
}
impl<T: Config> LeafDataProvider for Pallet<T> where
MerkleRootOf<T>: From<H256>,
{
type LeafData = MmrLeaf<
<T as frame_system::Config>::BlockNumber,
<T as frame_system::Config>::Hash,
MerkleRootOf<T>,
>;
fn leaf_data() -> Self::LeafData {
MmrLeaf {
parent_number_and_hash: frame_system::Pallet::<T>::leaf_data(),
parachain_heads: Pallet::<T>::parachain_heads_merkle_root(),
beefy_next_authority_set: Pallet::<T>::update_beefy_next_authority_set(),
}
}
}
impl<T: Config> Pallet<T> where
MerkleRootOf<T>: From<H256>,
<T as pallet_beefy::Config>::AuthorityId:
{
/// Returns latest root hash of a merkle tree constructed from all registered parachain headers.
///
/// NOTE this does not include parathreads - only parachains are part of the merkle tree.
///
/// NOTE This is an initial and inefficient implementation, which re-constructs
/// the merkle tree every block. Instead we should update the merkle root in [Self::on_initialize]
/// call of this pallet and update the merkle tree efficiently (use on-chain storage to persist inner nodes).
fn parachain_heads_merkle_root() -> MerkleRootOf<T> {
let para_heads = T::ParachainHeads::encoded_heads();
sp_io::trie::keccak_256_ordered_root(para_heads).into()
}
/// Returns details of the next BEEFY authority set.
///
/// Details contain authority set id, authority set length and a merkle root,
/// constructed from uncompressed secp256k1 public keys of the next BEEFY authority set.
///
/// This function will use a storage-cached entry in case the set didn't change, or compute and cache
/// new one in case it did.
fn update_beefy_next_authority_set() -> BeefyNextAuthoritySet<MerkleRootOf<T>> {
let id = pallet_beefy::Pallet::<T>::validator_set_id() + 1;
let current_next = Self::beefy_next_authorities();
// avoid computing the merkle tree if validator set id didn't change.
if id == current_next.id {
return current_next;
}
let beefy_public_keys = pallet_beefy::Pallet::<T>::next_authorities()
.into_iter()
.map(T::BeefyAuthorityToMerkleLeaf::convert)
.collect::<Vec<_>>();
let len = beefy_public_keys.len() as u32;
let root: MerkleRootOf<T> = sp_io::trie::keccak_256_ordered_root(beefy_public_keys).into();
let next_set = BeefyNextAuthoritySet {
id,
len,
root,
};
// cache the result
BeefyNextAuthorities::<T>::put(&next_set);
next_set
}
}
......@@ -17,6 +17,7 @@ smallvec = "1.6.1"
authority-discovery-primitives = { package = "sp-authority-discovery", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
babe-primitives = { package = "sp-consensus-babe", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }