diff --git a/Cargo.lock b/Cargo.lock index 07cfa41b4f96..85406fd71241 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4355,6 +4355,7 @@ dependencies = [ "serde_derive", "serde_json", "sp-api", + "sp-application-crypto", "sp-core", "sp-inherents", "sp-io", diff --git a/primitives/src/parachain.rs b/primitives/src/parachain.rs index 6fcb696fc956..2afa3e19ae70 100644 --- a/primitives/src/parachain.rs +++ b/primitives/src/parachain.rs @@ -88,6 +88,17 @@ application_crypto::with_pair! { /// so we define it to be the same type as `SessionKey`. In the future it may have different crypto. pub type ValidatorSignature = validator_app::Signature; +/// The key type ID for a fisherman key. +pub const FISHERMAN_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"fish"); + +mod fisherman_app { + use application_crypto::{app_crypto, sr25519}; + app_crypto!(sr25519, super::FISHERMAN_KEY_TYPE_ID); +} + +/// Identity that fishermen use when generating reports. +pub type FishermanId = fisherman_app::Public; + /// Retriability for a given active para. #[derive(Clone, Eq, PartialEq, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug))] diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index 68effe1e60de..88d7519dfa6d 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -39,6 +39,7 @@ hex-literal = "0.2.1" keyring = { package = "sp-keyring", git = "https://github.com/paritytech/substrate", branch = "master" } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" } babe = { package = "pallet-babe", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } randomness-collective-flip = { package = "pallet-randomness-collective-flip", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-staking-reward-curve = { git = "https://github.com/paritytech/substrate", branch = "master" } treasury = { package = "pallet-treasury", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } diff --git a/runtime/common/src/parachains.rs b/runtime/common/src/parachains.rs index e960800558ce..4536eff98b06 100644 --- a/runtime/common/src/parachains.rs +++ b/runtime/common/src/parachains.rs @@ -57,7 +57,10 @@ use sp_runtime::transaction_validity::InvalidTransaction; use inherents::{ProvideInherent, InherentData, MakeFatalError, InherentIdentifier}; -use system::{ensure_none, ensure_signed}; +use system::{ + ensure_none, ensure_signed, + offchain::SubmitSignedTransaction, +}; use crate::attestations::{self, IncludedBlocks}; use crate::registrar::Registrar; @@ -222,7 +225,7 @@ pub trait Trait: attestations::Trait + session::historical::Trait { type Origin: From + From>; /// The outer call dispatch type. - type Call: Parameter + Dispatchable::Origin>; + type Call: Parameter + Dispatchable::Origin> + From>; /// Some way of interacting with balances for fees. type ParachainCurrency: ParachainCurrency; @@ -290,6 +293,9 @@ pub trait Trait: attestations::Trait + session::historical::Trait { /// A type that converts the opaque hash type to exact one. type BlockHashConversion: Convert; + + /// Submit a signed transaction. + type SubmitSignedTransaction: SubmitSignedTransaction::Call>; } /// Origin for the parachains module. @@ -784,6 +790,21 @@ impl Module { } } + /// Submit a double vote report. + pub fn submit_double_vote_report( + report: DoubleVoteReport, + ) -> Option<()> { + let call = Call::report_double_vote(report); + + let res = T::SubmitSignedTransaction::submit_signed(call); + + if res.iter().any(|(_, r)| r.is_ok()) { + Some(()) + } else { + None + } + } + /// Dispatch some messages from a parachain. fn dispatch_message( id: ParaId, @@ -1426,6 +1447,13 @@ impl sp_std::fmt::Debug for ValidateDoubleVoteReports where } } +impl ValidateDoubleVoteReports { + /// Create a new `ValidateDoubleVoteReports` struct. + pub fn new() -> Self { + ValidateDoubleVoteReports(sp_std::marker::PhantomData) + } +} + /// Custom validity error used while validating double vote reports. #[derive(RuntimeDebug)] #[repr(u8)] @@ -1522,7 +1550,7 @@ mod tests { Perbill, curve::PiecewiseLinear, traits::{ BlakeTwo256, IdentityLookup, SaturatedConversion, - OpaqueKeys, + OpaqueKeys, Extrinsic as ExtrinsicT, }, testing::TestXt, }; @@ -1787,6 +1815,31 @@ mod tests { pub const SlashPeriod: BlockNumber = 50; } + // This is needed for a custom `AccountId` type which is `u64` in testing here. + pub mod test_keys { + use sp_core::crypto::KeyTypeId; + + pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"test"); + + mod app { + use sp_application_crypto::{app_crypto, sr25519}; + use super::super::Parachains; + + app_crypto!(sr25519, super::KEY_TYPE); + + impl sp_runtime::traits::IdentifyAccount for Public { + type AccountId = u64; + + fn into_account(self) -> Self::AccountId { + Parachains::authorities().iter().position(|b| *b == self.0.clone().into()).unwrap() as u64 + } + } + } + + pub type ReporterId = app::Public; + pub type ReporterSignature = app::Signature; + } + impl Trait for Test { type Origin = Origin; type Call = Call; @@ -1807,6 +1860,27 @@ mod tests { type ReportOffence = Offences; type BlockHashConversion = sp_runtime::traits::Identity; type KeyOwnerProofSystem = Historical; + type SubmitSignedTransaction = system::offchain::TransactionSubmitter< + test_keys::ReporterId, + Test, + Extrinsic, + >; + } + + type Extrinsic = TestXt; + + impl system::offchain::CreateTransaction for Test { + type Public = test_keys::ReporterId; + type Signature = test_keys::ReporterSignature; + + fn create_transaction>( + call: ::Call, + _public: Self::Public, + _account: ::AccountId, + nonce: ::Index, + ) -> Option<(::Call, ::SignaturePayload)> { + Some((call, (nonce, ()))) + } } type Parachains = Module; diff --git a/runtime/common/src/registrar.rs b/runtime/common/src/registrar.rs index 33657a4554ab..2d1111cf7be8 100644 --- a/runtime/common/src/registrar.rs +++ b/runtime/common/src/registrar.rs @@ -563,6 +563,15 @@ impl ActiveParas for Module { pub struct LimitParathreadCommits(sp_std::marker::PhantomData) where ::Call: IsSubType, T>; +impl LimitParathreadCommits where + ::Call: IsSubType, T> +{ + /// Create a new `LimitParathreadCommits` struct. + pub fn new() -> Self { + LimitParathreadCommits(sp_std::marker::PhantomData) + } +} + impl sp_std::fmt::Debug for LimitParathreadCommits where ::Call: IsSubType, T> { @@ -658,7 +667,7 @@ mod tests { use sp_runtime::{ traits::{ BlakeTwo256, IdentityLookup, Dispatchable, - AccountIdConversion, + AccountIdConversion, Extrinsic as ExtrinsicT, }, testing::{UintAuthorityId, TestXt}, KeyTypeId, Perbill, curve::PiecewiseLinear, }; use primitives::{ @@ -840,6 +849,32 @@ mod tests { type FullIdentificationOf = staking::ExposureOf; } + // This is needed for a custom `AccountId` type which is `u64` in testing here. + pub mod test_keys { + use sp_core::crypto::KeyTypeId; + + pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"test"); + + mod app { + use super::super::Parachains; + use sp_application_crypto::{app_crypto, sr25519}; + + app_crypto!(sr25519, super::KEY_TYPE); + + impl sp_runtime::traits::IdentifyAccount for Public { + type AccountId = u64; + + fn into_account(self) -> Self::AccountId { + let id = self.0.clone().into(); + Parachains::authorities().iter().position(|b| *b == id).unwrap() as u64 + } + } + } + + pub type ReporterId = app::Public; + pub type ReporterSignature = app::Signature; + } + impl parachains::Trait for Test { type Origin = Origin; type Call = Call; @@ -858,6 +893,27 @@ mod tests { type IdentificationTuple = )>>::IdentificationTuple; type ReportOffence = (); type BlockHashConversion = sp_runtime::traits::Identity; + type SubmitSignedTransaction = system::offchain::TransactionSubmitter< + test_keys::ReporterId, + Test, + Extrinsic, + >; + } + + type Extrinsic = TestXt; + + impl system::offchain::CreateTransaction for Test { + type Public = test_keys::ReporterId; + type Signature = test_keys::ReporterSignature; + + fn create_transaction>( + call: ::Call, + _public: Self::Public, + _account: ::AccountId, + nonce: ::Index, + ) -> Option<(::Call, ::SignaturePayload)> { + Some((call, (nonce, ()))) + } } parameter_types! { diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index e6d4116c8ed6..028e33fb52cc 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -41,7 +41,7 @@ use sp_runtime::{ curve::PiecewiseLinear, traits::{ BlakeTwo256, Block as BlockT, SignedExtension, OpaqueKeys, ConvertInto, IdentityLookup, - DispatchInfoOf, + DispatchInfoOf, Extrinsic as ExtrinsicT, SaturatedConversion, }, }; #[cfg(feature = "runtime-benchmarks")] @@ -53,7 +53,8 @@ use version::NativeVersion; use sp_core::OpaqueMetadata; use sp_staking::SessionIndex; use frame_support::{ - parameter_types, construct_runtime, traits::{KeyOwnerProofSystem, SplitTwoWays, Randomness}, + parameter_types, construct_runtime, debug, + traits::{KeyOwnerProofSystem, SplitTwoWays, Randomness}, }; use im_online::sr25519::AuthorityId as ImOnlineId; use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; @@ -539,6 +540,46 @@ impl parachains::Trait for Runtime { type IdentificationTuple = )>>::IdentificationTuple; type ReportOffence = Offences; type BlockHashConversion = sp_runtime::traits::Identity; + type SubmitSignedTransaction = TransactionSubmitter; +} + +impl system::offchain::CreateTransaction for Runtime { + type Public = ::Signer; + type Signature = primitives::Signature; + + fn create_transaction>( + call: ::Call, + public: Self::Public, + account: ::AccountId, + nonce: ::Index, + ) -> Option<(Call, ::SignaturePayload)> { + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; + + let current_block = System::block_number() + .saturated_into::() + .saturating_sub(1); + let tip = 0; + let extra: SignedExtra = ( + RestrictFunctionality, + system::CheckVersion::::new(), + system::CheckGenesis::::new(), + system::CheckEra::::from(generic::Era::mortal(period, current_block)), + system::CheckNonce::::from(nonce), + system::CheckWeight::::new(), + transaction_payment::ChargeTransactionPayment::::from(tip), + registrar::LimitParathreadCommits::::new(), + parachains::ValidateDoubleVoteReports::::new(), + ); + let raw_payload = SignedPayload::new(call, extra).map_err(|e| { + debug::warn!("Unable to create signed payload: {:?}", e) + }).ok()?; + let signature = TSigner::sign(public, &raw_payload)?; + let (call, extra, _) = raw_payload.deconstruct(); + Some((call, (account, signature, extra))) + } } parameter_types! { @@ -769,6 +810,8 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = executive::Executive, Runtime, AllModules>; +/// The payload being signed in the transactions. +pub type SignedPayload = generic::SignedPayload; sp_api::impl_runtime_apis! { impl sp_api::Core for Runtime { diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index f5ef0ccf782c..468d1cb294ac 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -42,7 +42,7 @@ use sp_runtime::{ curve::PiecewiseLinear, traits::{ BlakeTwo256, Block as BlockT, SignedExtension, OpaqueKeys, ConvertInto, - IdentityLookup, DispatchInfoOf, + DispatchInfoOf, IdentityLookup, Extrinsic as ExtrinsicT, SaturatedConversion, }, }; #[cfg(feature = "runtime-benchmarks")] @@ -54,7 +54,8 @@ use version::NativeVersion; use sp_core::OpaqueMetadata; use sp_staking::SessionIndex; use frame_support::{ - parameter_types, construct_runtime, traits::{KeyOwnerProofSystem, SplitTwoWays, Randomness}, + parameter_types, construct_runtime, debug, + traits::{KeyOwnerProofSystem, SplitTwoWays, Randomness}, }; use im_online::sr25519::AuthorityId as ImOnlineId; use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; @@ -545,6 +546,46 @@ impl parachains::Trait for Runtime { type IdentificationTuple = )>>::IdentificationTuple; type ReportOffence = Offences; type BlockHashConversion = sp_runtime::traits::Identity; + type SubmitSignedTransaction = TransactionSubmitter; +} + +impl system::offchain::CreateTransaction for Runtime { + type Public = ::Signer; + type Signature = primitives::Signature; + + fn create_transaction>( + call: ::Call, + public: Self::Public, + account: ::AccountId, + nonce: ::Index, + ) -> Option<(Call, ::SignaturePayload)> { + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; + + let current_block = System::block_number() + .saturated_into::() + .saturating_sub(1); + let tip = 0; + let extra: SignedExtra = ( + OnlyStakingAndClaims, + system::CheckVersion::::new(), + system::CheckGenesis::::new(), + system::CheckEra::::from(generic::Era::mortal(period, current_block)), + system::CheckNonce::::from(nonce), + system::CheckWeight::::new(), + transaction_payment::ChargeTransactionPayment::::from(tip), + registrar::LimitParathreadCommits::::new(), + parachains::ValidateDoubleVoteReports::::new(), + ); + let raw_payload = SignedPayload::new(call, extra).map_err(|e| { + debug::warn!("Unable to create signed payload: {:?}", e) + }).ok()?; + let signature = TSigner::sign(public, &raw_payload)?; + let (call, extra, _) = raw_payload.deconstruct(); + Some((call, (account, signature, extra))) + } } parameter_types! { @@ -687,6 +728,8 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = executive::Executive, Runtime, AllModules>; +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; sp_api::impl_runtime_apis! { impl sp_api::Core for Runtime { diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index cb5224c7f8f4..9271fedd6526 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -41,7 +41,7 @@ use sp_runtime::{ curve::PiecewiseLinear, traits::{ BlakeTwo256, Block as BlockT, StaticLookup, SignedExtension, OpaqueKeys, ConvertInto, - DispatchInfoOf, + DispatchInfoOf, Extrinsic as ExtrinsicT, SaturatedConversion, }, }; use version::RuntimeVersion; @@ -51,11 +51,12 @@ use version::NativeVersion; use sp_core::OpaqueMetadata; use sp_staking::SessionIndex; use frame_support::{ - parameter_types, construct_runtime, + parameter_types, construct_runtime, debug, traits::{KeyOwnerProofSystem, Randomness}, }; use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; use session::historical as session_historical; +use system::offchain::TransactionSubmitter; #[cfg(feature = "std")] pub use staking::StakerStatus; @@ -348,6 +349,47 @@ impl parachains::Trait for Runtime { >::IdentificationTuple; type ReportOffence = Offences; type BlockHashConversion = sp_runtime::traits::Identity; + type SubmitSignedTransaction = TransactionSubmitter; +} + +impl system::offchain::CreateTransaction for Runtime { + type Public = ::Signer; + type Signature = primitives::Signature; + + fn create_transaction>( + call: ::Call, + public: Self::Public, + account: ::AccountId, + nonce: ::Index, + ) -> Option<(Call, ::SignaturePayload)> { + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; + + let current_block = System::block_number() + .saturated_into::() + .saturating_sub(1); + let tip = 0; + let extra: SignedExtra = ( + RestrictFunctionality, + system::CheckVersion::::new(), + system::CheckGenesis::::new(), + system::CheckEra::::from(generic::Era::mortal(period, current_block)), + system::CheckNonce::::from(nonce), + system::CheckWeight::::new(), + transaction_payment::ChargeTransactionPayment::::from(tip), + registrar::LimitParathreadCommits::::new(), + parachains::ValidateDoubleVoteReports::::new(), + ); + let raw_payload = SignedPayload::new(call, extra).map_err(|e| { + debug::warn!("Unable to create signed payload: {:?}", e) + }).ok()?; + let signature = TSigner::sign(public, &raw_payload)?; + let (call, extra, _) = raw_payload.deconstruct(); + let address = Indices::unlookup(account); + Some((call, (address, signature, extra))) + } } impl offences::Trait for Runtime { @@ -467,7 +509,8 @@ pub type SignedExtra = ( system::CheckNonce, system::CheckWeight, transaction_payment::ChargeTransactionPayment::, - registrar::LimitParathreadCommits + registrar::LimitParathreadCommits, + parachains::ValidateDoubleVoteReports, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; @@ -475,6 +518,8 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = executive::Executive, Runtime, AllModules>; +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; pub type Hash = ::Hash; pub type Extrinsic = ::Extrinsic; diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 61cc8cf501df..cd646ff49896 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -40,7 +40,7 @@ use sp_runtime::{ curve::PiecewiseLinear, traits::{ BlakeTwo256, Block as BlockT, SignedExtension, OpaqueKeys, ConvertInto, IdentityLookup, - DispatchInfoOf, + DispatchInfoOf, StaticLookup, Extrinsic as ExtrinsicT, SaturatedConversion, }, }; #[cfg(feature = "runtime-benchmarks")] @@ -51,7 +51,10 @@ use grandpa::{AuthorityId as GrandpaId, fg_primitives}; use version::NativeVersion; use sp_core::OpaqueMetadata; use sp_staking::SessionIndex; -use frame_support::{parameter_types, construct_runtime, traits::{KeyOwnerProofSystem, Randomness}}; +use frame_support::{ + parameter_types, construct_runtime, debug, + traits::{KeyOwnerProofSystem, Randomness}, +}; use im_online::sr25519::AuthorityId as ImOnlineId; use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; use system::offchain::TransactionSubmitter; @@ -407,6 +410,46 @@ impl parachains::Trait for Runtime { type IdentificationTuple = )>>::IdentificationTuple; type ReportOffence = Offences; type BlockHashConversion = sp_runtime::traits::Identity; + type SubmitSignedTransaction = TransactionSubmitter; +} + +impl system::offchain::CreateTransaction for Runtime { + type Public = ::Signer; + type Signature = primitives::Signature; + + fn create_transaction>( + call: ::Call, + public: Self::Public, + account: ::AccountId, + nonce: ::Index, + ) -> Option<(Call, ::SignaturePayload)> { + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; + + let current_block = System::block_number() + .saturated_into::() + .saturating_sub(1); + let tip = 0; + let extra: SignedExtra = ( + RestrictFunctionality, + system::CheckVersion::::new(), + system::CheckGenesis::::new(), + system::CheckEra::::from(generic::Era::mortal(period, current_block)), + system::CheckNonce::::from(nonce), + system::CheckWeight::::new(), + transaction_payment::ChargeTransactionPayment::::from(tip), + registrar::LimitParathreadCommits::::new(), + parachains::ValidateDoubleVoteReports::::new(), + ); + let raw_payload = SignedPayload::new(call, extra).map_err(|e| { + debug::warn!("Unable to create signed payload: {:?}", e) + }).ok()?; + let signature = TSigner::sign(public, &raw_payload)?; + let (call, extra, _) = raw_payload.deconstruct(); + Some((call, (account, signature, extra))) + } } parameter_types! { @@ -580,6 +623,8 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = executive::Executive, Runtime, AllModules>; +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; sp_api::impl_runtime_apis! { impl sp_api::Core for Runtime {