diff --git a/CHANGELOG.md b/CHANGELOG.md index fe5a34753c..ca18f73b56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - XCM transport fees are now exponential and are sent to a treasury account ([polkadot-fellows/runtimes#87](https://github.com/polkadot-fellows/runtimes/pull/87)). Context: https://github.com/paritytech/polkadot-sdk/pull/1234 - System parachains are now trusted teleporters of each other ([polkadot-fellows/runtimes#87](https://github.com/polkadot-fellows/runtimes/pull/87)). Context: https://github.com/paritytech/polkadot-sdk/pull/1368 - Treasury is able to spend various asset kinds ([polkadot-fellows/runtimes#87](https://github.com/polkadot-fellows/runtimes/pull/87)) +- Add BEEFY to Polkadot ([polkadot-fellows/runtimes#65](https://github.com/polkadot-fellows/runtimes/pull/65)) - Fellowship Treasury pallet on Polkadot Collectives ([polkadot-fellows/runtimes#109](https://github.com/polkadot-fellows/runtimes/pull/109)) ### Fixed @@ -29,7 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Add missing weight functions for `runtime_parachains_hrmp` and `preimage` pallets ([polkadot-fellows/runtimes#56](https://github.com/polkadot-fellows/runtimes/pull/56)) - Fix for Reward Deficit in the pool ([polkadot-fellows/runtimes#87](https://github.com/polkadot-fellows/runtimes/pull/87)). Context: https://github.com/paritytech/polkadot-sdk/pull/1255 -## [1.0.1] 22.10.2023 +## [1.0.1] 14.11.2023 ### Changed diff --git a/Cargo.lock b/Cargo.lock index 7e8f1a408f..90c6aff873 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8081,6 +8081,7 @@ dependencies = [ name = "polkadot-runtime" version = "1.0.0" dependencies = [ + "binary-merkle-tree", "bitvec", "frame-benchmarking", "frame-election-provider-support", @@ -8099,6 +8100,8 @@ dependencies = [ "pallet-babe", "pallet-bags-list", "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", "pallet-bounties", "pallet-child-bounties", "pallet-collective", @@ -8114,6 +8117,7 @@ dependencies = [ "pallet-indices", "pallet-membership", "pallet-message-queue", + "pallet-mmr", "pallet-multisig", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", @@ -8153,6 +8157,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 0208c50c83..4de8c71b8c 100644 --- a/relay/polkadot/Cargo.toml +++ b/relay/polkadot/Cargo.toml @@ -21,6 +21,7 @@ smallvec = "1.8.0" authority-discovery-primitives = { package = "sp-authority-discovery", default-features = false , version = "23.0.0" } babe-primitives = { package = "sp-consensus-babe", default-features = false , version = "0.29.0" } beefy-primitives = { package = "sp-consensus-beefy", default-features = false , version = "10.0.0" } +binary-merkle-tree = { default-features = false , version = "10.0.0" } block-builder-api = { package = "sp-block-builder", default-features = false , version = "23.0.0" } inherents = { package = "sp-inherents", default-features = false , version = "23.0.0" } offchain-primitives = { package = "sp-offchain", default-features = false , version = "23.0.0" } @@ -29,6 +30,7 @@ sp-arithmetic = { default-features = false , version = "20.0.0" } sp-api = { default-features = false , version = "23.0.0" } sp-genesis-builder = { default-features = false , version = "0.4.0" } sp-std = { default-features = false , version = "12.0.0" } +sp-application-crypto = { default-features = false , version = "27.0.0" } sp-io = { default-features = false , version = "27.0.0" } sp-mmr-primitives = { default-features = false , version = "23.0.0" } sp-runtime = { default-features = false , version = "28.0.0" } @@ -45,6 +47,8 @@ pallet-authorship = { default-features = false , version = "25.0.0" } pallet-babe = { default-features = false , version = "25.0.0" } pallet-bags-list = { default-features = false , version = "24.0.0" } pallet-balances = { default-features = false , version = "25.0.0" } +pallet-beefy = { default-features = false , version = "25.0.0" } +pallet-beefy-mmr = { default-features = false , version = "25.0.0" } pallet-bounties = { default-features = false , version = "24.0.0" } pallet-child-bounties = { default-features = false , version = "24.0.0" } pallet-transaction-payment = { default-features = false , version = "25.0.0" } @@ -62,6 +66,7 @@ pallet-im-online = { default-features = false , version = "24.0.0" } pallet-indices = { default-features = false , version = "25.0.0" } pallet-membership = { default-features = false , version = "25.0.0" } pallet-message-queue = { default-features = false , version = "28.0.0" } +pallet-mmr = { default-features = false , version = "24.0.0" } pallet-multisig = { default-features = false , version = "25.0.0" } pallet-nomination-pools = { default-features = false , version = "22.0.0" } pallet-nomination-pools-runtime-api = { default-features = false , version = "20.0.0" } @@ -128,6 +133,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", @@ -147,6 +153,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", @@ -162,6 +170,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", @@ -197,6 +206,7 @@ std = [ "serde/std", "serde_derive", "sp-api/std", + "sp-application-crypto/std", "sp-arithmetic/std", "sp-core/std", "sp-genesis-builder/std", @@ -241,6 +251,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", @@ -281,6 +292,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", @@ -295,6 +308,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 a5c54fa151..03ed0a78cb 100644 --- a/relay/polkadot/src/lib.rs +++ b/relay/polkadot/src/lib.rs @@ -45,7 +45,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, }; @@ -54,9 +57,9 @@ use frame_support::{ genesis_builder_helper::{build_config, create_default_config}, parameter_types, traits::{ - fungible::HoldConsideration, ConstU32, Contains, EitherOf, EitherOfDiverse, InstanceFilter, - KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage, ProcessMessageError, - WithdrawReasons, + fungible::HoldConsideration, ConstU32, Contains, EitherOf, EitherOfDiverse, Get, + InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage, + ProcessMessageError, WithdrawReasons, }, weights::{ConstantMultiplier, WeightMeter}, PalletId, @@ -76,15 +79,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, - IdentityLookup, OpaqueKeys, SaturatedConversion, Verify, + IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, RuntimeDebug, @@ -107,7 +109,6 @@ pub use pallet_election_provider_multi_phase::{Call as EPMCall, GeometricDeposit 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; @@ -322,6 +323,81 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<8>; } +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 @@ -353,6 +429,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, @@ -361,6 +448,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 + }, } } @@ -1448,6 +1562,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, @@ -1612,6 +1734,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 { @@ -1649,6 +1781,9 @@ pub mod migrations { // Migrate parachain info format paras_registrar::migration::VersionCheckedMigrateToV1, + // Upgrade SessionKeys to include BEEFY key + UpgradeSessionKeys, + pallet_nomination_pools::migration::versioned_migrations::V5toV6, pallet_nomination_pools::migration::versioned_migrations::V6ToV7, @@ -1952,62 +2087,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() } } @@ -2502,7 +2669,7 @@ mod test { #[test] fn call_size() { - RuntimeCall::assert_size_under(230); + RuntimeCall::assert_size_under(256); } #[test]