diff --git a/CHANGELOG.md b/CHANGELOG.md index 152aff91b..4f17c8377 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,18 +15,6 @@ At the moment this project **does not** adhere to structure, and the `NodeInfoChanged` event were removed from the Staking Extension pallet. The `AttestationHandler` config type was added to the Staking Extension pallet. The `KeyProvider` and `AttestationQueue` config types were removed from the Attestation pallet. -- In [#1128](https://github.com/entropyxyz/entropy-core/pull/1128) mnemonics can no longer be passed - in via a command line argument, file, or environment variable. Instead they are randomly generated - internally. -- In [#1179](https://github.com/entropyxyz/entropy-core/pull/1179) the format of TDX Quote input data has - been changed. -- In [#1147](https://github.com/entropyxyz/entropy-core/pull/1147) a field is added to the - chainspec: `jump_started_signers` which allows the chain to be started in a pre-jumpstarted state - for testing. If this is not desired it should be set to `None`. - -### Added -- [#1128](https://github.com/entropyxyz/entropy-core/pull/1128) adds an `/info` route to `entropy-tss` - which can be used to get the TSS account ID and x25519 public key. - In [#1068](https://github.com/entropyxyz/entropy-core/pull/1068) an extra type `PckCertChainVerifier` was added to the staking extension pallet's `Config` trait. - In [#1123](https://github.com/entropyxyz/entropy-core/pull/1123/) the `change_endpoint()` and @@ -36,11 +24,24 @@ At the moment this project **does not** adhere to - In [#1153](https://github.com/entropyxyz/entropy-core/pull/1153/) the program runtime was updated to accept multiple oracle inputs, this means any programs that were compiled and used need to be recompiled to the new runtime +- In [#1128](https://github.com/entropyxyz/entropy-core/pull/1128) mnemonics can no longer be passed + in to `entropy-tss` via the `--mnemonic` command line argument, a file, or an environment variable. + Instead they are randomly generated internally and can be retrieved with the `/info` HTTP route. +- In [#1179](https://github.com/entropyxyz/entropy-core/pull/1179) the format of TDX Quote input data has + been changed. +- In [#1147](https://github.com/entropyxyz/entropy-core/pull/1147) a field is added to the + chainspec: `jump_started_signers` which allows the chain to be started in a pre-jumpstarted state + for testing. If this is not desired it should be set to `None`. + +### Added +- In [#1128](https://github.com/entropyxyz/entropy-core/pull/1128) an `/info` route was added to `entropy-tss` + which can be used to get the TSS account ID and x25519 public key. - Protocol message versioning ([#1140](https://github.com/entropyxyz/entropy-core/pull/1140)) - CLI command to get oracle headings ([#1170](https://github.com/entropyxyz/entropy-core/pull/1170)) - Add TSS endpoint to get TDX quote ([#1173](https://github.com/entropyxyz/entropy-core/pull/1173)) - Add TDX test network chainspec ([#1204](https://github.com/entropyxyz/entropy-core/pull/1204)) - Test CLI command to retrieve quote and change endpoint / TSS account in one command ([#1198](https://github.com/entropyxyz/entropy-core/pull/1198)) +- On-chain unresponsiveness reporting [(#1215)](https://github.com/entropyxyz/entropy-core/pull/1215) ### Changed - Use correct key rotation endpoint in OCW ([#1104](https://github.com/entropyxyz/entropy-core/pull/1104)) diff --git a/Cargo.lock b/Cargo.lock index 298582681..d7d0e9ea8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1358,12 +1358,12 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -2547,7 +2547,7 @@ dependencies = [ "subxt", "synedrion", "tdx-quote", - "thiserror 2.0.6", + "thiserror 2.0.8", "tokio", "tracing", "x25519-dalek 2.0.1", @@ -2582,7 +2582,7 @@ dependencies = [ "sled", "sp-core 31.0.0", "synedrion", - "thiserror 2.0.6", + "thiserror 2.0.8", "tokio", "tracing", "zeroize", @@ -2642,7 +2642,7 @@ dependencies = [ "sp-keyring 34.0.0", "subxt", "synedrion", - "thiserror 2.0.6", + "thiserror 2.0.8", "tokio", "tokio-tungstenite", "tracing", @@ -2858,7 +2858,7 @@ dependencies = [ "subxt-signer", "synedrion", "tdx-quote", - "thiserror 2.0.6", + "thiserror 2.0.8", "tokio", "tokio-tungstenite", "tower-http 0.6.2", @@ -6768,6 +6768,7 @@ dependencies = [ "pallet-balances", "pallet-parameters", "pallet-session", + "pallet-slashing", "pallet-staking", "pallet-staking-extension", "pallet-staking-reward-curve", @@ -7287,6 +7288,7 @@ dependencies = [ "pallet-programs", "pallet-registry", "pallet-session", + "pallet-slashing", "pallet-staking", "pallet-staking-extension", "pallet-staking-reward-curve", @@ -7353,6 +7355,7 @@ dependencies = [ "pallet-parameters", "pallet-programs", "pallet-session", + "pallet-slashing", "pallet-staking", "pallet-staking-extension", "pallet-staking-reward-curve", @@ -7494,6 +7497,7 @@ dependencies = [ "pallet-balances", "pallet-parameters", "pallet-session", + "pallet-slashing", "pallet-staking", "pallet-staking-reward-curve", "pallet-timestamp", @@ -14334,11 +14338,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.6" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" dependencies = [ - "thiserror-impl 2.0.6", + "thiserror-impl 2.0.8", ] [[package]] @@ -14354,9 +14358,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.6" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" dependencies = [ "proc-macro2", "quote", diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 66a2244a8..b07b110d1 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -14,7 +14,7 @@ serde ={ version="1.0", default-features=false, features=["derive"] } entropy-shared={ version="0.3.0", path="../shared", default-features=false } subxt ={ version="0.35.3", default-features=false, features=["jsonrpsee"] } num ="0.4.3" -thiserror ="2.0.6" +thiserror ="2.0.8" futures ="0.3" sp-core ={ version="31.0.0", default-features=false, features=["full_crypto", "serde"] } tracing ="0.1.41" diff --git a/crates/client/entropy_metadata.scale b/crates/client/entropy_metadata.scale index 7ffe949d7..9d94e92bc 100644 Binary files a/crates/client/entropy_metadata.scale and b/crates/client/entropy_metadata.scale differ diff --git a/crates/kvdb/Cargo.toml b/crates/kvdb/Cargo.toml index 2d8a7d22d..c21efd4dd 100644 --- a/crates/kvdb/Cargo.toml +++ b/crates/kvdb/Cargo.toml @@ -12,7 +12,7 @@ edition ='2021' # Common rand ={ version="0.8", default-features=false } serde ={ version="1.0", features=["derive"] } -thiserror="2.0.6" +thiserror="2.0.8" hex ="0.4.3" # Substrate diff --git a/crates/protocol/Cargo.toml b/crates/protocol/Cargo.toml index c6b542101..36b5ea286 100644 --- a/crates/protocol/Cargo.toml +++ b/crates/protocol/Cargo.toml @@ -20,7 +20,7 @@ x25519-dalek ={ version="2.0.1", features=["static_secrets"] } futures ="0.3" hex ="0.4.3" blake2 ="0.10.4" -thiserror ="2.0.6" +thiserror ="2.0.8" snow ="0.9.6" getrandom ={ version="0.2", features=["js"] } rand_core ={ version="0.6.4", features=["getrandom"] } diff --git a/crates/test-cli/Cargo.toml b/crates/test-cli/Cargo.toml index 23c8c67b8..b21357028 100644 --- a/crates/test-cli/Cargo.toml +++ b/crates/test-cli/Cargo.toml @@ -11,7 +11,7 @@ edition ='2021' [dependencies] entropy-client={ version="0.3.0", path="../client" } clap ={ version="4.5.23", features=["derive"] } -colored ="2.0.4" +colored ="2.2.0" subxt ="0.35.3" sp-core ="31.0.0" anyhow ="1.0.94" diff --git a/crates/threshold-signature-server/Cargo.toml b/crates/threshold-signature-server/Cargo.toml index e1a299245..090041cfd 100644 --- a/crates/threshold-signature-server/Cargo.toml +++ b/crates/threshold-signature-server/Cargo.toml @@ -12,7 +12,7 @@ edition ='2021' # Common serde ={ version="1.0", default-features=false, features=["derive"] } serde_json ="1.0" -thiserror ="2.0.6" +thiserror ="2.0.8" anyhow ="1.0.94" blake2 ="0.10.4" x25519-dalek ={ version="2.0.1", features=["static_secrets"] } diff --git a/pallets/attestation/Cargo.toml b/pallets/attestation/Cargo.toml index e9b7356bd..917fdf320 100644 --- a/pallets/attestation/Cargo.toml +++ b/pallets/attestation/Cargo.toml @@ -42,6 +42,8 @@ pallet-staking-reward-curve ={ version="11.0.0" } tdx-quote ={ version="0.0.1", features=["mock"] } rand_core ="0.6.4" +pallet-slashing={ version="0.3.0", path="../slashing", default-features=false } + [features] default=['std'] runtime-benchmarks=['frame-benchmarking', 'tdx-quote/mock', 'pallet-session'] diff --git a/pallets/attestation/src/mock.rs b/pallets/attestation/src/mock.rs index 32cd7369d..7ed5c402f 100644 --- a/pallets/attestation/src/mock.rs +++ b/pallets/attestation/src/mock.rs @@ -64,6 +64,7 @@ frame_support::construct_runtime!( Historical: pallet_session_historical, BagsList: pallet_bags_list, Parameters: pallet_parameters, + Slashing: pallet_slashing, } ); @@ -342,6 +343,18 @@ impl pallet_parameters::Config for Test { type WeightInfo = (); } +parameter_types! { + pub const ReportThreshold: u32 = 5; +} + +impl pallet_slashing::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AuthorityId = UintAuthorityId; + type ReportThreshold = ReportThreshold; + type ValidatorSet = Historical; + type ReportUnresponsiveness = (); +} + // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap(); diff --git a/pallets/propagation/Cargo.toml b/pallets/propagation/Cargo.toml index e634f2b51..9385b0fbb 100644 --- a/pallets/propagation/Cargo.toml +++ b/pallets/propagation/Cargo.toml @@ -29,10 +29,10 @@ sp-staking ={ version="27.0.0", default-features=false } entropy-shared={ version="0.3.0", path="../../crates/shared", default-features=false, features=[ "wasm-no-std", ] } -pallet-registry={ version="0.3.0", path="../registry", default-features=false } +pallet-attestation={ version="0.3.0", path="../attestation", default-features=false } pallet-programs={ version="0.3.0", path="../programs", default-features=false } +pallet-registry={ version="0.3.0", path="../registry", default-features=false } pallet-staking-extension={ version="0.3.0", path="../staking", default-features=false } -pallet-attestation={ version="0.3.0", path="../attestation", default-features=false } [dev-dependencies] parking_lot="0.12.3" @@ -49,6 +49,7 @@ sp-keystore ={ version="0.35.0" } sp-npos-elections ={ version="27.0.0", default-features=false } pallet-parameters ={ version="0.3.0", path="../parameters", default-features=false } pallet-oracle ={ version='0.3.0', path='../oracle', default-features=false } +pallet-slashing ={ version="0.3.0", path="../slashing", default-features=false } [features] default=['std'] diff --git a/pallets/propagation/src/mock.rs b/pallets/propagation/src/mock.rs index 1fce23e1c..e3d3bf77e 100644 --- a/pallets/propagation/src/mock.rs +++ b/pallets/propagation/src/mock.rs @@ -61,6 +61,7 @@ frame_support::construct_runtime!( Parameters: pallet_parameters, Attestation: pallet_attestation, Oracle: pallet_oracle, + Slashing: pallet_slashing, } ); @@ -394,6 +395,18 @@ impl pallet_attestation::Config for Test { type Randomness = TestPastRandomness; } +parameter_types! { + pub const ReportThreshold: u32 = 5; +} + +impl pallet_slashing::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AuthorityId = UintAuthorityId; + type ReportThreshold = ReportThreshold; + type ValidatorSet = Historical; + type ReportUnresponsiveness = (); +} + // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap(); diff --git a/pallets/registry/Cargo.toml b/pallets/registry/Cargo.toml index 8c092beca..64cbe8362 100644 --- a/pallets/registry/Cargo.toml +++ b/pallets/registry/Cargo.toml @@ -46,6 +46,7 @@ sp-io ={ version="31.0.0", default-features=false } sp-npos-elections ={ version="27.0.0", default-features=false } sp-staking ={ version="27.0.0", default-features=false } pallet-oracle ={ version='0.3.0', path='../oracle', default-features=false } +pallet-slashing ={ version="0.3.0", path="../slashing", default-features=false } [features] diff --git a/pallets/registry/src/benchmarking.rs b/pallets/registry/src/benchmarking.rs index 65849c535..13caabbd4 100644 --- a/pallets/registry/src/benchmarking.rs +++ b/pallets/registry/src/benchmarking.rs @@ -216,7 +216,7 @@ benchmarks! { let network_verifying_key = synedrion::ecdsa::VerifyingKey::try_from(network_verifying_key.as_slice()).unwrap(); - // We substract one from the count since this gets incremented after a succesful registration, + // We subtract one from the count since this gets incremented after a succesful registration, // and we're interested in the account we just registered. let count = >::count() - 1; let derivation_path = diff --git a/pallets/registry/src/mock.rs b/pallets/registry/src/mock.rs index 76054af22..573ea9e75 100644 --- a/pallets/registry/src/mock.rs +++ b/pallets/registry/src/mock.rs @@ -60,6 +60,7 @@ frame_support::construct_runtime!( Programs: pallet_programs, Parameters: pallet_parameters, Oracle: pallet_oracle, + Slashing: pallet_slashing, } ); @@ -380,6 +381,18 @@ impl pallet_parameters::Config for Test { type WeightInfo = (); } +parameter_types! { + pub const ReportThreshold: u32 = 5; +} + +impl pallet_slashing::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AuthorityId = UintAuthorityId; + type ReportThreshold = ReportThreshold; + type ValidatorSet = Historical; + type ReportUnresponsiveness = (); +} + // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap(); diff --git a/pallets/staking/Cargo.toml b/pallets/staking/Cargo.toml index 3cb777887..ff6ecfbbe 100644 --- a/pallets/staking/Cargo.toml +++ b/pallets/staking/Cargo.toml @@ -34,6 +34,7 @@ p256 ={ version="0.13.2", default-features=false, features=["ecdsa" rand ={ version="0.8.5", default-features=false, features=["alloc"] } pallet-parameters={ version="0.3.0", path="../parameters", default-features=false } +pallet-slashing={ version="0.3.0", path="../slashing", default-features=false } entropy-shared={ version="0.3.0", path="../../crates/shared", features=[ "wasm-no-std", ], default-features=false } @@ -69,6 +70,7 @@ std=[ 'pallet-balances/std', 'pallet-parameters/std', 'pallet-session/std', + 'pallet-slashing/std', 'pallet-staking/std', 'scale-info/std', 'sp-consensus-babe/std', diff --git a/pallets/staking/src/benchmarking.rs b/pallets/staking/src/benchmarking.rs index 112a9c978..a6dad4dae 100644 --- a/pallets/staking/src/benchmarking.rs +++ b/pallets/staking/src/benchmarking.rs @@ -29,6 +29,7 @@ use frame_support::{ }; use frame_system::{EventRecord, RawOrigin}; use pallet_parameters::{SignersInfo, SignersSize}; +use pallet_slashing::Event as SlashingEvent; use pallet_staking::{ Event as FrameStakingEvent, MaxNominationsOf, MaxValidatorsCount, Nominations, Pallet as FrameStaking, RewardDestination, ValidatorPrefs, @@ -56,6 +57,16 @@ fn assert_last_event_frame_staking( assert_eq!(event, &system_event); } +fn assert_last_event_slashing( + generic_event: ::RuntimeEvent, +) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + // compare to the last event record + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + pub fn create_validators( count: u32, seed: u32, @@ -518,6 +529,37 @@ benchmarks! { verify { assert!(NextSigners::::get().is_some()); } + + report_unstable_peer { + // We subtract `2` here to give room to our test signers + let s in 0 .. (MAX_SIGNERS - 2) as u32; + + let threshold_reporter: T::AccountId = whitelisted_caller(); + let threshold_offender: T::AccountId = account("threshold_offender", 0, SEED); + + let reporter_validator_id = ::ValidatorId::try_from(threshold_reporter.clone()) + .or(Err(Error::::InvalidValidatorId)) + .unwrap(); + + let offender_validator_id = ::ValidatorId::try_from(threshold_offender.clone()) + .or(Err(Error::::InvalidValidatorId)) + .unwrap(); + + ThresholdToStash::::insert(&threshold_reporter, &reporter_validator_id); + ThresholdToStash::::insert(&threshold_offender, &offender_validator_id); + + let mut signers = vec![reporter_validator_id.clone(); s as usize]; + signers.push(reporter_validator_id); + signers.push(offender_validator_id); + + Signers::::put(signers.clone()); + + }: _(RawOrigin::Signed(threshold_reporter.clone()), threshold_offender.clone()) + verify { + assert_last_event_slashing::( + pallet_slashing::Event::NoteReport(threshold_reporter, threshold_offender).into(), + ); + } } impl_benchmark_test_suite!(Staking, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index 88bf6cbec..b4961cabe 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -90,6 +90,7 @@ pub mod pallet { + frame_system::Config + pallet_staking::Config + pallet_parameters::Config + + pallet_slashing::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -336,6 +337,7 @@ pub mod pallet { InvalidValidatorId, SigningGroupError, TssAccountAlreadyExists, + NotSigner, NotNextSigner, ReshareNotInProgress, AlreadyConfirmed, @@ -735,6 +737,57 @@ pub mod pallet { // signers see https://github.com/entropyxyz/entropy-core/issues/985 Ok(Pays::No.into()) } + + /// An on-chain hook for TSS servers in the signing committee to report other TSS servers in + /// the committee for misbehaviour. + /// + /// Any "conequences" are handled by the configured Slashing pallet and not this pallet + /// itself. + #[pallet::call_index(7)] + #[pallet::weight(::WeightInfo::report_unstable_peer(MAX_SIGNERS as u32))] + pub fn report_unstable_peer( + origin: OriginFor, + offender_tss_account: T::AccountId, + ) -> DispatchResultWithPostInfo { + let reporter_tss_account = ensure_signed(origin)?; + + // For reporting purposes we need to know the validator account tied to the TSS account. + let reporter_validator_id = Self::threshold_to_stash(&reporter_tss_account) + .ok_or(Error::::NoThresholdKey)?; + let offender_validator_id = Self::threshold_to_stash(&offender_tss_account) + .ok_or(Error::::NoThresholdKey)?; + + // Note: This operation is O(n), but with a small enough Signer group this should be + // fine to do on-chain. + let signers = Self::signers(); + ensure!(signers.contains(&reporter_validator_id), Error::::NotSigner); + ensure!(signers.contains(&offender_validator_id), Error::::NotSigner); + + // We do a bit of a weird conversion here since we want the validator's underlying + // `AccountId` for the reporting mechanism, not their `ValidatorId`. + // + // The Session pallet should have this configured to be the same thing, but we can't + // prove that to the compiler. + let encoded_validator_id = T::ValidatorId::encode(&reporter_validator_id); + let reporter_validator_account = T::AccountId::decode(&mut &encoded_validator_id[..]) + .expect("A `ValidatorId` should be equivalent to an `AccountId`."); + + let encoded_validator_id = T::ValidatorId::encode(&offender_validator_id); + let offending_peer_validator_account = + T::AccountId::decode(&mut &encoded_validator_id[..]) + .expect("A `ValidatorId` should be equivalent to an `AccountId`."); + + // We don't actually take any action here, we offload the reporting to the Slashing + // pallet. + pallet_slashing::Pallet::::note_report( + reporter_validator_account, + offending_peer_validator_account, + )?; + + let actual_weight = + ::WeightInfo::report_unstable_peer(signers.len() as u32); + Ok(Some(actual_weight).into()) + } } impl Pallet { diff --git a/pallets/staking/src/mock.rs b/pallets/staking/src/mock.rs index 21a35e06e..4c71b12b8 100644 --- a/pallets/staking/src/mock.rs +++ b/pallets/staking/src/mock.rs @@ -63,6 +63,7 @@ frame_support::construct_runtime!( Historical: pallet_session_historical, BagsList: pallet_bags_list, Parameters: pallet_parameters, + Slashing: pallet_slashing, } ); @@ -418,6 +419,43 @@ impl entropy_shared::AttestationHandler for MockAttestationHandler { fn request_quote(_attestee: &AccountId, _nonce: [u8; 32]) {} } +type IdentificationTuple = (u64, pallet_staking::Exposure); +type Offence = pallet_slashing::UnresponsivenessOffence; + +parameter_types! { + pub static Offences: Vec = vec![]; +} + +/// A mock offence report handler. +pub struct OffenceHandler; +impl sp_staking::offence::ReportOffence + for OffenceHandler +{ + fn report_offence( + _reporters: Vec, + offence: Offence, + ) -> Result<(), sp_staking::offence::OffenceError> { + Offences::mutate(|l| l.push(offence)); + Ok(()) + } + + fn is_known_offence(_offenders: &[IdentificationTuple], _time_slot: &SessionIndex) -> bool { + false + } +} + +parameter_types! { + pub const ReportThreshold: u32 = 5; +} + +impl pallet_slashing::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AuthorityId = UintAuthorityId; + type ReportThreshold = ReportThreshold; + type ValidatorSet = Historical; + type ReportUnresponsiveness = OffenceHandler; +} + impl pallet_staking_extension::Config for Test { type AttestationHandler = MockAttestationHandler; type Currency = Balances; diff --git a/pallets/staking/src/tests.rs b/pallets/staking/src/tests.rs index 92125b2a9..f2a957b85 100644 --- a/pallets/staking/src/tests.rs +++ b/pallets/staking/src/tests.rs @@ -844,3 +844,58 @@ fn it_stops_chill_when_signer_or_next_signer() { ); }); } + +#[test] +fn cannot_report_outside_of_signer_set() { + new_test_ext().execute_with(|| { + // These mappings come from the mock GenesisConfig + let (alice_validator, alice_tss) = (5, 7); + let (_bob_validator, bob_tss) = (6, 8); + + let (_not_validator, not_tss) = (33, 33); + + // We only want Alice to be part of the signing committee for the test. + Signers::::put(vec![alice_validator]); + + // A TSS which doesn't have a `ValidatorId` cannot report another peer + assert_noop!( + Staking::report_unstable_peer(RuntimeOrigin::signed(not_tss), bob_tss), + Error::::NoThresholdKey + ); + + // A validator which isn't part of the signing committee cannot report another peer + assert_noop!( + Staking::report_unstable_peer(RuntimeOrigin::signed(bob_tss), alice_tss), + Error::::NotSigner + ); + + // An offender that does not have a `ValidatorId` cannot be reported + assert_noop!( + Staking::report_unstable_peer(RuntimeOrigin::signed(alice_tss), not_tss), + Error::::NoThresholdKey + ); + + // An offender which isn't part of the signing committee cannot be reported + assert_noop!( + Staking::report_unstable_peer(RuntimeOrigin::signed(alice_tss), bob_tss), + Error::::NotSigner + ); + }) +} + +#[test] +fn can_report_unstable_peer() { + new_test_ext().execute_with(|| { + // These mappings come from the mock GenesisConfig + let (alice_validator, alice_tss) = (5, 7); + let (bob_validator, bob_tss) = (6, 8); + + Signers::::put(vec![alice_validator, bob_validator]); + + // The TSS accounts are used for reports. We expect the accompanying validator to be + // reported though. + assert_ok!(Staking::report_unstable_peer(RuntimeOrigin::signed(alice_tss), bob_tss)); + + assert_eq!(>::failed_registrations(bob_validator), 1); + }) +} diff --git a/pallets/staking/src/weights.rs b/pallets/staking/src/weights.rs index 012f873ce..e2aa31bfa 100644 --- a/pallets/staking/src/weights.rs +++ b/pallets/staking/src/weights.rs @@ -62,6 +62,7 @@ pub trait WeightInfo { fn confirm_key_reshare_completed() -> Weight; fn new_session_base_weight(s: u32) -> Weight; fn new_session(c: u32, l: u32, v: u32, r: u32) -> Weight; + fn report_unstable_peer(s: u32, ) -> Weight; } /// Weights for pallet_staking_extension using the Substrate node and recommended hardware. @@ -332,6 +333,24 @@ impl WeightInfo for SubstrateWeight { .saturating_add(Weight::from_parts(0, 11364552184692736).saturating_mul(r.into())) .saturating_add(Weight::from_parts(0, 18).saturating_mul(v.into())) } + /// Storage: `StakingExtension::ThresholdToStash` (r:2 w:0) + /// Proof: `StakingExtension::ThresholdToStash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `StakingExtension::Signers` (r:1 w:0) + /// Proof: `StakingExtension::Signers` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Slashing::FailedRegistrations` (r:1 w:1) + /// Proof: `Slashing::FailedRegistrations` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// The range of component `s` is `[0, 13]`. + fn report_unstable_peer(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `519 + s * (32 ±0)` + // Estimated: `6459 + s * (32 ±0)` + // Minimum execution time: 16_000_000 picoseconds. + Weight::from_parts(19_055_447, 0) + .saturating_add(Weight::from_parts(0, 6459)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(s.into())) + } } // For backwards compatibility and tests @@ -601,4 +620,22 @@ impl WeightInfo for () { .saturating_add(Weight::from_parts(0, 11364552184692736).saturating_mul(r.into())) .saturating_add(Weight::from_parts(0, 18).saturating_mul(v.into())) } + /// Storage: `StakingExtension::ThresholdToStash` (r:2 w:0) + /// Proof: `StakingExtension::ThresholdToStash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `StakingExtension::Signers` (r:1 w:0) + /// Proof: `StakingExtension::Signers` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Slashing::FailedRegistrations` (r:1 w:1) + /// Proof: `Slashing::FailedRegistrations` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// The range of component `s` is `[0, 13]`. + fn report_unstable_peer(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `519 + s * (32 ±0)` + // Estimated: `6459 + s * (32 ±0)` + // Minimum execution time: 16_000_000 picoseconds. + Weight::from_parts(19_055_447, 0) + .saturating_add(Weight::from_parts(0, 6459)) + .saturating_add(RocksDbWeight::get().reads(4)) + .saturating_add(RocksDbWeight::get().writes(1)) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(s.into())) + } } diff --git a/runtime/src/weights/pallet_staking_extension.rs b/runtime/src/weights/pallet_staking_extension.rs index a6bc32bad..0132f9f95 100644 --- a/runtime/src/weights/pallet_staking_extension.rs +++ b/runtime/src/weights/pallet_staking_extension.rs @@ -321,4 +321,22 @@ impl pallet_staking_extension::WeightInfo for WeightInf .saturating_add(Weight::from_parts(0, 11364552184692736).saturating_mul(r.into())) .saturating_add(Weight::from_parts(0, 18).saturating_mul(v.into())) } + /// Storage: `StakingExtension::ThresholdToStash` (r:2 w:0) + /// Proof: `StakingExtension::ThresholdToStash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `StakingExtension::Signers` (r:1 w:0) + /// Proof: `StakingExtension::Signers` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Slashing::FailedRegistrations` (r:1 w:1) + /// Proof: `Slashing::FailedRegistrations` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// The range of component `s` is `[0, 13]`. + fn report_unstable_peer(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `519 + s * (32 ±0)` + // Estimated: `6459 + s * (32 ±0)` + // Minimum execution time: 16_000_000 picoseconds. + Weight::from_parts(19_055_447, 0) + .saturating_add(Weight::from_parts(0, 6459)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(s.into())) + } }