From 5616d9e449a92d82e69e53aa5a5e86209c84feec Mon Sep 17 00:00:00 2001 From: Steve Degosserie <723552+stiiifff@users.noreply.github.com> Date: Wed, 2 Apr 2025 23:49:47 +0200 Subject: [PATCH] =?UTF-8?q?feat(operator):=20=F0=9F=8F=97=EF=B8=8F=20Add?= =?UTF-8?q?=20Authorship,=20ImOnline=20&=20Offences=20pallets=20(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Track validator availability and reports offences which lead to exclusion of the misbehaving validator from the validator set. --- operator/Cargo.lock | 39 ++++++++++ operator/Cargo.toml | 3 + operator/node/Cargo.toml | 1 + operator/node/src/chain_spec.rs | 17 ++++- operator/runtime/Cargo.toml | 13 +++- operator/runtime/src/apis.rs | 3 +- operator/runtime/src/benchmarks.rs | 1 + operator/runtime/src/configs/mod.rs | 67 +++++++++++++---- operator/runtime/src/lib.rs | 74 ++++++++++++------- .../test/config/zombie-flamingo-local.toml | 2 +- 10 files changed, 172 insertions(+), 48 deletions(-) diff --git a/operator/Cargo.lock b/operator/Cargo.lock index b9cd7b17..f5f58bb0 100644 --- a/operator/Cargo.lock +++ b/operator/Cargo.lock @@ -1548,6 +1548,7 @@ dependencies = [ "hex-literal 0.3.4", "jsonrpsee", "mmr-rpc", + "pallet-im-online", "pallet-transaction-payment", "pallet-transaction-payment-rpc", "sc-basic-authorship", @@ -1599,6 +1600,7 @@ dependencies = [ "frame-try-runtime", "hex", "hex-literal 0.3.4", + "pallet-authorship", "pallet-babe", "pallet-balances", "pallet-beefy", @@ -1607,8 +1609,10 @@ dependencies = [ "pallet-evm", "pallet-evm-chain-id", "pallet-grandpa", + "pallet-im-online", "pallet-mmr", "pallet-multisig", + "pallet-offences", "pallet-preimage", "pallet-scheduler", "pallet-session", @@ -5906,6 +5910,25 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-im-online" +version = "37.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=stable2409#43d7b1e329f50a37cbd67e15aae70c60556cbb59" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", +] + [[package]] name = "pallet-message-queue" version = "41.0.2" @@ -5957,6 +5980,22 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-offences" +version = "37.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=stable2409#43d7b1e329f50a37cbd67e15aae70c60556cbb59" +dependencies = [ + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", + "sp-staking", +] + [[package]] name = "pallet-preimage" version = "38.0.0" diff --git a/operator/Cargo.toml b/operator/Cargo.toml index 201edf66..866f9970 100644 --- a/operator/Cargo.toml +++ b/operator/Cargo.toml @@ -82,10 +82,13 @@ frame-support-test = { git = "https://github.com/paritytech/polkadot-sdk", branc frame-system-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } frame-try-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } +pallet-authorship = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } pallet-babe = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } pallet-grandpa = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } +pallet-im-online = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } pallet-multisig = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } +pallet-offences = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } pallet-preimage = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } pallet-scheduler = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } pallet-session = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false } diff --git a/operator/node/Cargo.toml b/operator/node/Cargo.toml index b559cddb..ff20a92e 100644 --- a/operator/node/Cargo.toml +++ b/operator/node/Cargo.toml @@ -75,6 +75,7 @@ frame-system.workspace = true frame-system.default-features = true frame-metadata-hash-extension.workspace = true frame-metadata-hash-extension.default-features = true +pallet-im-online.workspace = true pallet-transaction-payment.workspace = true pallet-transaction-payment.default-features = true pallet-transaction-payment-rpc.workspace = true diff --git a/operator/node/src/chain_spec.rs b/operator/node/src/chain_spec.rs index 6aed3dc7..94d42514 100644 --- a/operator/node/src/chain_spec.rs +++ b/operator/node/src/chain_spec.rs @@ -2,6 +2,7 @@ use datahaven_runtime::{ configs::BABE_GENESIS_EPOCH_CONFIG, AccountId, SessionKeys, Signature, WASM_BINARY, }; use hex_literal::hex; +use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use sc_service::ChainType; use sp_consensus_babe::AuthorityId as BabeId; use sp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId; @@ -19,10 +20,16 @@ pub fn get_from_seed(seed: &str) -> ::Pu .public() } -fn session_keys(babe: BabeId, grandpa: GrandpaId, beefy: BeefyId) -> SessionKeys { +fn session_keys( + babe: BabeId, + grandpa: GrandpaId, + im_online: ImOnlineId, + beefy: BeefyId, +) -> SessionKeys { SessionKeys { babe, grandpa, + im_online, beefy, } } @@ -38,11 +45,12 @@ where } /// Generate a Babe authority key. -pub fn authority_keys_from_seed(s: &str) -> (AccountId, BabeId, GrandpaId, BeefyId) { +pub fn authority_keys_from_seed(s: &str) -> (AccountId, BabeId, GrandpaId, ImOnlineId, BeefyId) { ( get_account_id_from_seed::(s), get_from_seed::(s), get_from_seed::(s), + get_from_seed::(s), get_from_seed::(s), ) } @@ -107,7 +115,7 @@ pub fn local_testnet_config() -> Result { /// Configure initial storage state for FRAME modules. fn testnet_genesis( - initial_authorities: Vec<(AccountId, BabeId, GrandpaId, BeefyId)>, + initial_authorities: Vec<(AccountId, BabeId, GrandpaId, ImOnlineId, BeefyId)>, root_key: AccountId, endowed_accounts: Vec, _enable_println: bool, @@ -121,6 +129,7 @@ fn testnet_genesis( "epochConfig": Some(BABE_GENESIS_EPOCH_CONFIG), }, "grandpa": {}, + "imOnline": {}, "sudo": { // Assign network admin rights. "key": Some(root_key), @@ -130,7 +139,7 @@ fn testnet_genesis( }, "session": { "keys": initial_authorities.iter().map(|x| { - (x.0.clone(), x.0.clone(), session_keys(x.1.clone(), x.2.clone(), x.3.clone())) + (x.0.clone(), x.0.clone(), session_keys(x.1.clone(), x.2.clone(), x.3.clone(), x.4.clone())) }).collect::>(), }, }) diff --git a/operator/runtime/Cargo.toml b/operator/runtime/Cargo.toml index 5d203cbf..f5d8d509 100644 --- a/operator/runtime/Cargo.toml +++ b/operator/runtime/Cargo.toml @@ -23,10 +23,13 @@ frame-try-runtime = { optional = true, workspace = true } frame-executive.workspace = true frame-metadata-hash-extension.workspace = true hex-literal.workspace = true +pallet-authorship.workspace = true pallet-babe.workspace = true pallet-balances.workspace = true pallet-grandpa.workspace = true +pallet-im-online.workspace = true pallet-multisig.workspace = true +pallet-offences.workspace = true pallet-preimage.workspace = true pallet-scheduler.workspace = true pallet-session.workspace = true @@ -90,13 +93,16 @@ std = [ "datahaven-runtime-common/std", + "pallet-authorship/std", "pallet-babe/std", "pallet-beefy/std", "pallet-beefy-mmr/std", "pallet-balances/std", + "pallet-im-online/std", "pallet-grandpa/std", "pallet-multisig/std", "pallet-mmr/std", + "pallet-offences/std", "pallet-preimage/std", "pallet-scheduler/std", "pallet-session/std", @@ -144,9 +150,11 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "datahaven-runtime-common/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-im-online/runtime-benchmarks", "pallet-grandpa/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", + "pallet-offences/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", @@ -167,10 +175,13 @@ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "frame-try-runtime/try-runtime", + "pallet-authorship/try-runtime", "pallet-babe/try-runtime", "pallet-balances/try-runtime", "pallet-grandpa/try-runtime", + "pallet-im-online/try-runtime", "pallet-multisig/try-runtime", + "pallet-offences/try-runtime", "pallet-preimage/try-runtime", "pallet-scheduler/try-runtime", "pallet-session/try-runtime", diff --git a/operator/runtime/src/apis.rs b/operator/runtime/src/apis.rs index 0700a8ad..cc5ebfd1 100644 --- a/operator/runtime/src/apis.rs +++ b/operator/runtime/src/apis.rs @@ -191,7 +191,7 @@ impl_runtime_apis! { } } - impl fg_primitives::GrandpaApi for Runtime { + impl sp_consensus_grandpa::GrandpaApi for Runtime { fn grandpa_authorities() -> Vec<(GrandpaId, u64)> { Grandpa::grandpa_authorities() } @@ -220,7 +220,6 @@ impl_runtime_apis! { authority_id: fg_primitives::AuthorityId, ) -> Option { - Historical::prove((fg_primitives::KEY_TYPE, authority_id)) .map(|p| p.encode()) .map(fg_primitives::OpaqueKeyOwnershipProof::new) diff --git a/operator/runtime/src/benchmarks.rs b/operator/runtime/src/benchmarks.rs index e03bd296..8b457edd 100644 --- a/operator/runtime/src/benchmarks.rs +++ b/operator/runtime/src/benchmarks.rs @@ -27,6 +27,7 @@ frame_benchmarking::define_benchmarks!( [frame_benchmarking, BaselineBench::] [frame_system, SystemBench::] [pallet_balances, Balances] + [pallet_im_online, ImOnline] [pallet_multisig, Multisig] [pallet_preimage, Preimage] [pallet_scheduler, Scheduler] diff --git a/operator/runtime/src/configs/mod.rs b/operator/runtime/src/configs/mod.rs index 3413bf7e..18914305 100644 --- a/operator/runtime/src/configs/mod.rs +++ b/operator/runtime/src/configs/mod.rs @@ -26,9 +26,9 @@ // Local module imports use super::{ deposit, AccountId, Babe, Balance, Balances, BeefyMmrLeaf, Block, BlockNumber, EvmChainId, - Hash, Historical, Nonce, OriginCaller, PalletInfo, Preimage, Runtime, RuntimeCall, - RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask, Session, - SessionKeys, System, Timestamp, ValidatorSet, EXISTENTIAL_DEPOSIT, SLOT_DURATION, + Hash, Historical, ImOnline, Nonce, Offences, OriginCaller, PalletInfo, Preimage, Runtime, + RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask, + Session, SessionKeys, System, Timestamp, ValidatorSet, EXISTENTIAL_DEPOSIT, SLOT_DURATION, STORAGE_BYTE_FEE, SUPPLY_FACTOR, UNIT, VERSION, }; // Substrate and Polkadot dependencies @@ -38,7 +38,9 @@ use datahaven_runtime_common::{ time::{EpochDurationInBlocks, MILLISECS_PER_BLOCK, MINUTES}, }; use frame_support::{ - derive_impl, parameter_types, + derive_impl, + pallet_prelude::TransactionPriority, + parameter_types, traits::{ fungible::{Balanced, Credit, HoldConsideration, Inspect}, ConstU128, ConstU32, ConstU64, ConstU8, EqualPrivilegeOnly, FindAuthor, @@ -57,6 +59,8 @@ use pallet_evm::{ FrameSystemAccountProvider, IdentityAddressMapping, OnChargeEVMTransaction as OnChargeEVMTransactionT, }; +use pallet_grandpa::AuthorityId as GrandpaId; +use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_transaction_payment::{ ConstFeeMultiplier, FungibleAdapter, Multiplier, Pallet as TransactionPayment, }; @@ -65,9 +69,8 @@ use snowbridge_beacon_primitives::{Fork, ForkVersions}; use sp_consensus_beefy::mmr::BeefyDataProvider; use sp_consensus_beefy::{ecdsa_crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion}; use sp_core::{crypto::KeyTypeId, H160, H256, U256}; -use sp_runtime::traits::IdentityLookup; use sp_runtime::{ - traits::{ConvertInto, Keccak256, One, OpaqueKeys, UniqueSaturatedInto}, + traits::{ConvertInto, IdentityLookup, Keccak256, One, OpaqueKeys, UniqueSaturatedInto}, FixedPointNumber, Perbill, }; use sp_staking::{EraIndex, SessionIndex}; @@ -104,6 +107,12 @@ parameter_types! { pub const SetKeysCooldownBlocks: BlockNumber = 5 * MINUTES; pub const NodesSize: u32 = 32; pub const RootHistorySize: u32 = 30; + + pub const EquivocationReportPeriodInEpochs: u64 = 168; + pub const EquivocationReportPeriodInBlocks: u64 = + EquivocationReportPeriodInEpochs::get() * (EpochDurationInBlocks::get() as u64); + + pub const ImOnlineUnsignedPriority: TransactionPriority = TransactionPriority::max_value(); } /// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from @@ -140,6 +149,8 @@ impl frame_system::Config for Runtime { parameter_types! { pub const ExpectedBlockTime: Moment = MILLISECS_PER_BLOCK; + pub ReportLongevity: u64 = + BondingDuration::get() as u64 * SessionsPerEra::get() as u64 * (EpochDurationInBlocks::get() as u64); } // 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks. @@ -160,11 +171,11 @@ impl pallet_babe::Config for Runtime { type MaxAuthorities = MaxAuthorities; type MaxNominators = ConstU32<0>; - type KeyOwnerProof = sp_session::MembershipProof; + type KeyOwnerProof = + >::Proof; - // TODO! specify as pallet_babe::EquivocationReportSystem; - // when pallet_autorship and pallet_offences are added - type EquivocationReportSystem = (); + type EquivocationReportSystem = + pallet_babe::EquivocationReportSystem; } parameter_types! { @@ -181,8 +192,36 @@ impl pallet_grandpa::Config for Runtime { type MaxNominators = ConstU32<0>; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; - type KeyOwnerProof = sp_session::MembershipProof; - type EquivocationReportSystem = (); + type KeyOwnerProof = >::Proof; + type EquivocationReportSystem = pallet_grandpa::EquivocationReportSystem< + Self, + Offences, + Historical, + EquivocationReportPeriodInBlocks, + >; +} + +impl pallet_offences::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type IdentificationTuple = pallet_session::historical::IdentificationTuple; + type OnOffenceHandler = ValidatorSet; +} + +impl pallet_authorship::Config for Runtime { + type FindAuthor = pallet_session::FindAccountFromAuthorIndex; + type EventHandler = ImOnline; +} + +impl pallet_im_online::Config for Runtime { + type AuthorityId = ImOnlineId; + type MaxKeys = MaxAuthorities; + type MaxPeerInHeartbeats = ConstU32<0>; // Not used any more + type RuntimeEvent = RuntimeEvent; + type ValidatorSet = Historical; + type NextSessionRotation = Babe; + type ReportUnresponsiveness = Offences; + type UnsignedPriority = ImOnlineUnsignedPriority; + type WeightInfo = (); } impl pallet_session::Config for Runtime { @@ -198,8 +237,8 @@ impl pallet_session::Config for Runtime { } impl pallet_session::historical::Config for Runtime { - type FullIdentification = AccountId; - type FullIdentificationOf = ConvertInto; + type FullIdentification = Self::ValidatorId; + type FullIdentificationOf = Self::ValidatorIdOf; } impl pallet_utility::Config for Runtime { diff --git a/operator/runtime/src/lib.rs b/operator/runtime/src/lib.rs index 279714fc..97f07c6b 100644 --- a/operator/runtime/src/lib.rs +++ b/operator/runtime/src/lib.rs @@ -1,4 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -51,6 +53,7 @@ impl_opaque_keys! { pub struct SessionKeys { pub babe: Babe, pub grandpa: Grandpa, + pub im_online: ImOnline, pub beefy: Beefy, } } @@ -192,6 +195,14 @@ pub type Executive = frame_executive::Executive< Migrations, >; +impl frame_system::offchain::SendTransactionTypes for Runtime +where + RuntimeCall: From, +{ + type Extrinsic = UncheckedExtrinsic; + type OverarchingCall = RuntimeCall; +} + // Create the runtime by composing the FRAME pallets that were previously configured. #[frame_support::runtime] mod runtime { @@ -212,48 +223,41 @@ mod runtime { #[runtime::pallet_index(0)] pub type System = frame_system; + // Babe must be before session. #[runtime::pallet_index(1)] - pub type Timestamp = pallet_timestamp; - - #[runtime::pallet_index(2)] - pub type Balances = pallet_balances; - - #[runtime::pallet_index(3)] pub type Babe = pallet_babe; - // TODO! Add the following palllets to the runtime: + #[runtime::pallet_index(2)] + pub type Timestamp = pallet_timestamp; + + #[runtime::pallet_index(3)] + pub type Balances = pallet_balances; + + // Consensus support. // Authorship must be before session in order to note author in the correct session and era. - // pub type Authorship = pallet_authorship; - // pub type Offences = pallet_offences; #[runtime::pallet_index(4)] - pub type Historical = pallet_session::historical; + pub type Authorship = pallet_authorship; #[runtime::pallet_index(5)] - pub type ValidatorSet = pallet_validator_set; + pub type Offences = pallet_offences; #[runtime::pallet_index(6)] - pub type Session = pallet_session; + pub type Historical = pallet_session::historical; #[runtime::pallet_index(7)] - pub type Grandpa = pallet_grandpa; + pub type ValidatorSet = pallet_validator_set; #[runtime::pallet_index(8)] - pub type TransactionPayment = pallet_transaction_payment; + pub type Session = pallet_session; #[runtime::pallet_index(9)] - pub type Sudo = pallet_sudo; + pub type ImOnline = pallet_im_online; #[runtime::pallet_index(10)] - pub type Beefy = pallet_beefy; + pub type Grandpa = pallet_grandpa; #[runtime::pallet_index(11)] - pub type BeefyMmrLeaf = pallet_beefy_mmr; - - #[runtime::pallet_index(12)] - pub type Mmr = pallet_mmr; - - #[runtime::pallet_index(13)] - pub type EthereumBeaconClient = snowbridge_pallet_ethereum_client; + pub type TransactionPayment = pallet_transaction_payment; #[runtime::pallet_index(20)] pub type Utility = pallet_utility; @@ -267,12 +271,30 @@ mod runtime { #[runtime::pallet_index(24)] pub type Multisig = pallet_multisig; - #[runtime::pallet_index(31)] + // Frontier + #[runtime::pallet_index(30)] pub type Ethereum = pallet_ethereum; - #[runtime::pallet_index(32)] + #[runtime::pallet_index(31)] pub type Evm = pallet_evm; - #[runtime::pallet_index(33)] + #[runtime::pallet_index(32)] pub type EvmChainId = pallet_evm_chain_id; + + // ETH Bridge + #[runtime::pallet_index(200)] + pub type Beefy = pallet_beefy; + + #[runtime::pallet_index(201)] + pub type Mmr = pallet_mmr; + + #[runtime::pallet_index(202)] + pub type BeefyMmrLeaf = pallet_beefy_mmr; + + #[runtime::pallet_index(203)] + pub type EthereumBeaconClient = snowbridge_pallet_ethereum_client; + + // Sudo + #[runtime::pallet_index(255)] + pub type Sudo = pallet_sudo; } diff --git a/operator/test/config/zombie-flamingo-local.toml b/operator/test/config/zombie-flamingo-local.toml index ba0267b7..7704e1f3 100644 --- a/operator/test/config/zombie-flamingo-local.toml +++ b/operator/test/config/zombie-flamingo-local.toml @@ -4,7 +4,7 @@ timeout = 120 [relaychain] default_command = "${output_bin_dir:-./target/release}/datahaven-node" chain = "datahaven-local" -default_args = [ "-l=debug", "--pruning=archive", "--enable-offchain-indexing=true" ] +default_args = [ "-l=debug", "--pruning=archive", "--enable-offchain-indexing=true", "--offchain-worker=when-authority" ] [[relaychain.nodes]] name = "alice"