diff --git a/Cargo.lock b/Cargo.lock index 9ce4f24cb9..ff24072fb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7819,6 +7819,7 @@ dependencies = [ name = "polkadot-runtime" version = "1.0.0" dependencies = [ + "binary-merkle-tree", "bitvec", "frame-benchmarking", "frame-election-provider-support", @@ -7836,6 +7837,8 @@ dependencies = [ "pallet-babe", "pallet-bags-list", "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", "pallet-bounties", "pallet-child-bounties", "pallet-collective", @@ -7851,6 +7854,7 @@ dependencies = [ "pallet-indices", "pallet-membership", "pallet-message-queue", + "pallet-mmr", "pallet-multisig", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", @@ -7889,6 +7893,7 @@ dependencies = [ "serde_json", "smallvec", "sp-api", + "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", diff --git a/relay/polkadot/Cargo.toml b/relay/polkadot/Cargo.toml index f8e7ded089..1f7021c9f9 100644 --- a/relay/polkadot/Cargo.toml +++ b/relay/polkadot/Cargo.toml @@ -21,12 +21,14 @@ smallvec = "1.8.0" authority-discovery-primitives = { package = "sp-authority-discovery", default-features = false , version = "21.0.0" } babe-primitives = { package = "sp-consensus-babe", default-features = false , version = "0.27.0" } beefy-primitives = { package = "sp-consensus-beefy", default-features = false , version = "8.0.0" } +binary-merkle-tree = { default-features = false , version = "8.0.0" } block-builder-api = { package = "sp-block-builder", default-features = false , version = "21.0.0" } inherents = { package = "sp-inherents", default-features = false , version = "21.0.0" } offchain-primitives = { package = "sp-offchain", default-features = false , version = "21.0.0" } tx-pool-api = { package = "sp-transaction-pool", default-features = false , version = "21.0.0" } sp-arithmetic = { default-features = false , version = "18.0.0" } sp-api = { default-features = false , version = "21.0.0" } +sp-application-crypto = { default-features = false , version = "25.0.0" } sp-std = { default-features = false , version = "10.0.0" } sp-io = { default-features = false , version = "25.0.0" } sp-mmr-primitives = { default-features = false , version = "21.0.0" } @@ -43,6 +45,8 @@ pallet-authorship = { default-features = false , version = "23.0.0" } pallet-babe = { default-features = false , version = "23.0.0" } pallet-bags-list = { default-features = false , version = "22.0.0" } pallet-balances = { default-features = false , version = "23.0.0" } +pallet-beefy = { default-features = false , version = "23.0.0" } +pallet-beefy-mmr = { default-features = false , version = "23.0.0" } pallet-bounties = { default-features = false , version = "22.0.0" } pallet-child-bounties = { default-features = false , version = "22.0.0" } pallet-transaction-payment = { default-features = false , version = "23.0.0" } @@ -60,6 +64,7 @@ pallet-im-online = { default-features = false , version = "22.0.0" } pallet-indices = { default-features = false , version = "23.0.0" } pallet-membership = { default-features = false , version = "23.0.0" } pallet-message-queue = { default-features = false , version = "26.0.0" } +pallet-mmr = { default-features = false , version = "22.0.0" } pallet-multisig = { default-features = false , version = "23.0.0" } pallet-nomination-pools = { default-features = false , version = "20.0.0" } pallet-nomination-pools-runtime-api = { default-features = false , version = "18.0.0" } @@ -125,6 +130,7 @@ std = [ "authority-discovery-primitives/std", "babe-primitives/std", "beefy-primitives/std", + "binary-merkle-tree/std", "bitvec/std", "block-builder-api/std", "frame-benchmarking?/std", @@ -143,6 +149,8 @@ std = [ "pallet-babe/std", "pallet-bags-list/std", "pallet-balances/std", + "pallet-beefy/std", + "pallet-beefy-mmr/std", "pallet-bounties/std", "pallet-child-bounties/std", "pallet-collective/std", @@ -158,6 +166,7 @@ std = [ "pallet-indices/std", "pallet-membership/std", "pallet-message-queue/std", + "pallet-mmr/std", "pallet-multisig/std", "pallet-nomination-pools-benchmarking?/std", "pallet-nomination-pools-runtime-api/std", @@ -192,6 +201,7 @@ std = [ "serde/std", "serde_derive", "sp-api/std", + "sp-application-crypto/std", "sp-arithmetic/std", "sp-core/std", "sp-io/std", @@ -234,6 +244,7 @@ runtime-benchmarks = [ "pallet-indices/runtime-benchmarks", "pallet-membership/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", + "pallet-mmr/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-nomination-pools-benchmarking/runtime-benchmarks", "pallet-nomination-pools/runtime-benchmarks", @@ -273,6 +284,8 @@ try-runtime = [ "pallet-babe/try-runtime", "pallet-bags-list/try-runtime", "pallet-balances/try-runtime", + "pallet-beefy-mmr/try-runtime", + "pallet-beefy/try-runtime", "pallet-bounties/try-runtime", "pallet-child-bounties/try-runtime", "pallet-collective/try-runtime", @@ -287,6 +300,7 @@ try-runtime = [ "pallet-indices/try-runtime", "pallet-membership/try-runtime", "pallet-message-queue/try-runtime", + "pallet-mmr/try-runtime", "pallet-multisig/try-runtime", "pallet-nomination-pools/try-runtime", "pallet-offences/try-runtime", diff --git a/relay/polkadot/src/lib.rs b/relay/polkadot/src/lib.rs index 45ea561b33..5d04f78cc6 100644 --- a/relay/polkadot/src/lib.rs +++ b/relay/polkadot/src/lib.rs @@ -40,7 +40,10 @@ use runtime_parachains::{ }; use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; -use beefy_primitives::ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}; +use beefy_primitives::{ + ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}, + mmr::{BeefyDataProvider, MmrLeafVersion}, +}; use frame_election_provider_support::{ bounds::ElectionBoundsBuilder, generate_solution_type, onchain, SequentialPhragmen, }; @@ -67,15 +70,14 @@ use primitives::{ ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, LOWEST_PUBLIC_ID, PARACHAIN_KEY_TYPE_ID, }; -use sp_core::OpaqueMetadata; -use sp_mmr_primitives as mmr; +use sp_core::{OpaqueMetadata, H256}; use sp_runtime::{ create_runtime_str, curve::PiecewiseLinear, generic, impl_opaque_keys, traits::{ - AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, Extrinsic as ExtrinsicT, - OpaqueKeys, SaturatedConversion, Verify, + AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, Extrinsic as ExtrinsicT, Get, + Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, RuntimeDebug, @@ -94,7 +96,6 @@ pub use pallet_election_provider_multi_phase::Call as EPMCall; pub use pallet_staking::StakerStatus; use pallet_staking::UseValidatorsMap; pub use pallet_timestamp::Call as TimestampCall; -use sp_runtime::traits::Get; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -301,6 +302,81 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<0>; } +parameter_types! { + pub BeefySetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get(); +} + +impl pallet_beefy::Config for Runtime { + type BeefyId = BeefyId; + type MaxAuthorities = MaxAuthorities; + type MaxNominators = MaxNominatorRewardedPerValidator; + type MaxSetIdSessionEntries = BeefySetIdSessionEntries; + type OnNewValidatorSet = BeefyMmrLeaf; + type WeightInfo = (); + type KeyOwnerProof = >::Proof; + type EquivocationReportSystem = + pallet_beefy::EquivocationReportSystem; +} + +impl pallet_mmr::Config for Runtime { + const INDEXING_PREFIX: &'static [u8] = mmr::INDEXING_PREFIX; + type Hashing = Keccak256; + type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest; + type WeightInfo = (); + type LeafData = pallet_beefy_mmr::Pallet; +} + +/// MMR helper types. +mod mmr { + use super::Runtime; + pub use pallet_mmr::primitives::*; + + pub type Leaf = <::LeafData as LeafDataProvider>::LeafData; + pub type Hashing = ::Hashing; + pub type Hash = ::Output; +} + +parameter_types! { + /// Version of the produced MMR leaf. + /// + /// The version consists of two parts; + /// - `major` (3 bits) + /// - `minor` (5 bits) + /// + /// `major` should be updated only if decoding the previous MMR Leaf format from the payload + /// is not possible (i.e. backward incompatible change). + /// `minor` should be updated if fields are added to the previous MMR Leaf, which given SCALE + /// encoding does not prevent old leafs from being decoded. + /// + /// Hence we expect `major` to be changed really rarely (think never). + /// See [`MmrLeafVersion`] type documentation for more details. + pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0); +} + +/// A BEEFY data provider that merkelizes all the parachain heads at the current block +/// (sorted by their parachain id). +pub struct ParaHeadsRootProvider; +impl BeefyDataProvider for ParaHeadsRootProvider { + fn extra_data() -> H256 { + let mut para_heads: Vec<(u32, Vec)> = Paras::parachains() + .into_iter() + .filter_map(|id| Paras::para_head(&id).map(|head| (id.into(), head.0))) + .collect(); + para_heads.sort_by_key(|k| k.0); + binary_merkle_tree::merkle_root::( + para_heads.into_iter().map(|pair| pair.encode()), + ) + .into() + } +} + +impl pallet_beefy_mmr::Config for Runtime { + type LeafVersion = LeafVersion; + type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; + type LeafExtra = H256; + type BeefyDataProvider = ParaHeadsRootProvider; +} + parameter_types! { pub const TransactionByteFee: Balance = 10 * MILLICENTS; /// This value increases the priority of `Operational` transactions by adding @@ -332,6 +408,17 @@ impl pallet_authorship::Config for Runtime { type EventHandler = (Staking, ImOnline); } +impl_opaque_keys! { + pub struct OldSessionKeys { + pub grandpa: Grandpa, + pub babe: Babe, + pub im_online: ImOnline, + pub para_validator: Initializer, + pub para_assignment: ParaSessionInfo, + pub authority_discovery: AuthorityDiscovery, + } +} + impl_opaque_keys! { pub struct SessionKeys { pub grandpa: Grandpa, @@ -340,6 +427,33 @@ impl_opaque_keys! { pub para_validator: Initializer, pub para_assignment: ParaSessionInfo, pub authority_discovery: AuthorityDiscovery, + pub beefy: Beefy, + } +} + +// remove this when removing `OldSessionKeys` +fn transform_session_keys(v: AccountId, old: OldSessionKeys) -> SessionKeys { + SessionKeys { + grandpa: old.grandpa, + babe: old.babe, + im_online: old.im_online, + para_validator: old.para_validator, + para_assignment: old.para_assignment, + authority_discovery: old.authority_discovery, + beefy: { + // From Session::upgrade_keys(): + // + // Care should be taken that the raw versions of the + // added keys are unique for every `ValidatorId, KeyTypeId` combination. + // This is an invariant that the session pallet typically maintains internally. + // + // So, produce a dummy value that's unique for the `ValidatorId, KeyTypeId` combination. + let mut id: BeefyId = sp_application_crypto::ecdsa::Public::from_raw([0u8; 33]).into(); + let id_raw: &mut [u8] = id.as_mut(); + id_raw[1..33].copy_from_slice(v.as_ref()); + id_raw[0..4].copy_from_slice(b"beef"); + id + }, } } @@ -1343,6 +1457,14 @@ construct_runtime! { Staking: pallet_staking::{Pallet, Call, Storage, Config, Event} = 7, Offences: pallet_offences::{Pallet, Storage, Event} = 8, Historical: session_historical::{Pallet} = 33, + + // BEEFY Bridges support. + Beefy: pallet_beefy::{Pallet, Call, Storage, Config, ValidateUnsigned} = 200, + // MMR leaf construction must be before session in order to have leaf contents + // refer to block consistently. see substrate issue #11797 for details. + Mmr: pallet_mmr::{Pallet, Storage} = 201, + BeefyMmrLeaf: pallet_beefy_mmr::{Pallet, Storage} = 202, + Session: pallet_session::{Pallet, Call, Storage, Event, Config} = 9, Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event, ValidateUnsigned} = 11, ImOnline: pallet_im_online::{Pallet, Call, Storage, Event, ValidateUnsigned, Config} = 12, @@ -1504,6 +1626,16 @@ pub mod migrations { type PalletName = TipsPalletName; } + /// Upgrade Session keys to include BEEFY key. + /// When this is removed, should also remove `OldSessionKeys`. + pub struct UpgradeSessionKeys; + impl frame_support::traits::OnRuntimeUpgrade for UpgradeSessionKeys { + fn on_runtime_upgrade() -> Weight { + Session::upgrade_keys::(transform_session_keys); + Perbill::from_percent(50) * BlockWeights::get().max_block + } + } + pub struct ParachainsToUnlock; impl Contains for ParachainsToUnlock { fn contains(id: &ParaId) -> bool { @@ -1541,6 +1673,9 @@ pub mod migrations { parachains_configuration::migration::v9::MigrateToV9, // Migrate parachain info format paras_registrar::migration::VersionCheckedMigrateToV1, + + // Upgrade SessionKeys to include BEEFY key + UpgradeSessionKeys, ); } @@ -1839,62 +1974,94 @@ sp_api::impl_runtime_apis! { impl beefy_primitives::BeefyApi for Runtime { fn beefy_genesis() -> Option { - // dummy implementation due to lack of BEEFY pallet. - None + Beefy::genesis_block() } fn validator_set() -> Option> { - // dummy implementation due to lack of BEEFY pallet. - None + Beefy::validator_set() } fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: beefy_primitives::EquivocationProof< + equivocation_proof: beefy_primitives::EquivocationProof< BlockNumber, BeefyId, BeefySignature, >, - _key_owner_proof: beefy_primitives::OpaqueKeyOwnershipProof, + key_owner_proof: beefy_primitives::OpaqueKeyOwnershipProof, ) -> Option<()> { - None + let key_owner_proof = key_owner_proof.decode()?; + + Beefy::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) } fn generate_key_ownership_proof( _set_id: beefy_primitives::ValidatorSetId, - _authority_id: BeefyId, + authority_id: BeefyId, ) -> Option { - None + use parity_scale_codec::Encode; + + Historical::prove((beefy_primitives::KEY_TYPE, authority_id)) + .map(|p| p.encode()) + .map(beefy_primitives::OpaqueKeyOwnershipProof::new) } } impl mmr::MmrApi for Runtime { - fn mmr_root() -> Result { - Err(mmr::Error::PalletNotIncluded) + fn mmr_root() -> Result { + Ok(Mmr::mmr_root()) } fn mmr_leaf_count() -> Result { - Err(mmr::Error::PalletNotIncluded) + Ok(Mmr::mmr_leaves()) } fn generate_proof( - _block_numbers: Vec, - _best_known_block_number: Option, - ) -> Result<(Vec, mmr::Proof), mmr::Error> { - Err(mmr::Error::PalletNotIncluded) + block_numbers: Vec, + best_known_block_number: Option, + ) -> Result<(Vec, mmr::Proof), mmr::Error> { + Mmr::generate_proof(block_numbers, best_known_block_number).map( + |(leaves, proof)| { + ( + leaves + .into_iter() + .map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)) + .collect(), + proof, + ) + }, + ) } - fn verify_proof(_leaves: Vec, _proof: mmr::Proof) + fn verify_proof(leaves: Vec, proof: mmr::Proof) -> Result<(), mmr::Error> { - Err(mmr::Error::PalletNotIncluded) + let leaves = leaves.into_iter().map(|leaf| + leaf.into_opaque_leaf() + .try_decode() + .ok_or(mmr::Error::Verify)).collect::, mmr::Error>>()?; + Mmr::verify_leaves(leaves, proof) } fn verify_proof_stateless( - _root: Hash, - _leaves: Vec, - _proof: mmr::Proof + root: mmr::Hash, + leaves: Vec, + proof: mmr::Proof ) -> Result<(), mmr::Error> { - Err(mmr::Error::PalletNotIncluded) + let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect(); + pallet_mmr::verify_leaves_proof::(root, nodes, proof) + } + } + + impl pallet_beefy_mmr::BeefyMmrApi for RuntimeApi { + fn authority_set_proof() -> beefy_primitives::mmr::BeefyAuthoritySet { + BeefyMmrLeaf::authority_set_proof() + } + + fn next_authority_set_proof() -> beefy_primitives::mmr::BeefyNextAuthoritySet { + BeefyMmrLeaf::next_authority_set_proof() } } @@ -2362,7 +2529,7 @@ mod test { #[test] fn call_size() { - RuntimeCall::assert_size_under(230); + RuntimeCall::assert_size_under(256); } #[test]