From b92c4e1a3ceedbf9a85f79b4a2c4e99b42326c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 25 Feb 2020 15:23:43 +0100 Subject: [PATCH 001/108] New approach to offchain signing. --- bin/node/runtime/src/lib.rs | 1 - frame/im-online/src/lib.rs | 14 +- frame/system/src/offchain.rs | 244 +++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+), 8 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e91eca4e400cd..debdafb18df10 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -455,7 +455,6 @@ impl pallet_im_online::Trait for Runtime { type AuthorityId = ImOnlineId; type Event = Event; type Call = Call; - type SubmitTransaction = SubmitTransaction; type SessionDuration = SessionDuration; type ReportUnresponsiveness = Offences; } diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index d6d13c66c2501..5919d679ac4cc 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -94,7 +94,7 @@ use frame_support::{ traits::Get, }; use frame_system::{self as system, ensure_none}; -use frame_system::offchain::SubmitUnsignedTransaction; +use frame_system::offchain::new::{self, SendRawUnsignedTransaction}; pub mod sr25519 { mod app_sr25519 { @@ -224,11 +224,8 @@ pub trait Trait: frame_system::Trait + pallet_session::historical::Trait { /// The overarching event type. type Event: From> + Into<::Event>; - /// A dispatchable call type. - type Call: From>; - - /// A transaction submitter. - type SubmitTransaction: SubmitUnsignedTransaction::Call>; + /// A transaction submitter / signing types. + type Types: new::SigningTypes + new::SubmitTransactionTypes>; /// An expected duration of the session. /// @@ -455,7 +452,9 @@ impl Module { session_index, authority_index, }; + let signature = key.sign(&heartbeat_data.encode()).ok_or(OffchainErr::FailedSigning)?; + Ok(Call::heartbeat(heartbeat_data, signature)) }; @@ -480,7 +479,8 @@ impl Module { call, ); - T::SubmitTransaction::submit_unsigned(call) + new::Signer:: + ::send_raw_unsigned_transaction(call.into()) .map_err(|_| OffchainErr::SubmitTransaction)?; Ok(()) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 5abafe7655172..9340590473844 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -23,6 +23,250 @@ use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature}; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; use frame_support::{debug, storage::StorageMap}; + +pub mod new { + use super::*; + + pub enum ForAll {} + pub enum ForAny {} + + pub struct Signer { + accounts: Vec, + _phantom: std::marker::PhantomData, + } + + impl Signer { + pub fn accounts(accounts: Vec) -> Self { + Signer { + accounts, + _phantom: Default::default(), + } + } + + pub fn with_all(self) -> Signer { + Signer { + accounts: self.accounts, + _phantom: Default::default(), + } + } + + pub fn with_any(self) -> Signer { + Signer { + accounts: self.accounts, + _phantom: Default::default(), + } + } + } + + impl< + T: SigningTypes + SubmitTransactionTypes, + X, + C, + > SendRawUnsignedTransaction for Signer { + fn send_raw_unsigned_transaction(_call: T::Call) -> Result<(), ()> { + unimplemented!() + } + } + + impl SignMessage for Signer { + type Result = Vec; + + // fn sign_message(&self, message: &[u8]) -> Self::Result { + // self.accounts + // .iter() + // .filter_map(|key| { + // let runtime_key = T::RuntimeAppPublic::from(key.clone()); + // runtime_key.sign(message) + // }) + // .collect() + // } + + fn sign(&self, f: F) -> Self::Result where + F: Fn(T::AccountId, T::Public) -> TPayload, + TPayload: SignedPayload, + { + self.accounts + .iter() + .filter_map(|key| { + let account_id = key.clone().into_account(); + let payload = f(account_id, key.clone()); + payload.sign() + }) + .collect() + } + } + + impl SignMessage for Signer { + type Result = Option; + + // fn sign_message(&self, message: &[u8]) -> Self::Result { + // self.accounts + // .iter() + // .filter_map(|key| { + // let runtime_key = T::RuntimeAppPublic::from(key.clone()); + // runtime_key.sign(message) + // }) + // .collect() + // } + + fn sign(&self, f: F) -> Self::Result where + F: Fn(T::AccountId, T::Public) -> TPayload, + TPayload: SignedPayload, + { + for key in &self.accounts { + let account_id = key.clone().into_account(); + let payload = f(account_id, key.clone()); + let res = payload.sign(); + if res.is_some() { + return res + } + } + + None + } + } + + impl< + T: SigningTypes + SubmitTransactionTypes, + C + > SendSignedTransaction for Signer { + type Result = (T::Public, Result<(), ()>); + + fn send_signed_transaction( + &self, + _f: impl Fn(T::AccountId, T::Public) -> T::Call, + ) -> Self::Result { + unimplemented!() + } + } + + impl< + T: SigningTypes + SubmitTransactionTypes, + C, + > SendSignedTransaction for Signer { + type Result = Vec<(T::Public, Result<(), ()>)>; + + fn send_signed_transaction( + &self, + _f: impl Fn(T::AccountId, T::Public) -> T::Call, + ) -> Self::Result { + unimplemented!() + } + } + + impl< + T: SigningTypes + SubmitTransactionTypes, + C, + > SendUnsignedTransaction for Signer { + type Result = Option; + + fn send_unsigned_transaction( + &self, + _f: F, + _f2: impl Fn(TPayload, T::Signature) -> T::Call, + ) -> Self::Result + where + F: Fn(T::AccountId, T::Public) -> TPayload, + TPayload: SignedPayload { + unimplemented!() + } + } + + impl< + T: SigningTypes + SubmitTransactionTypes, + C, + > SendUnsignedTransaction for Signer { + type Result = Vec>; + + fn send_unsigned_transaction( + &self, + _f: F, + _f2: impl Fn(TPayload, T::Signature) -> T::Call, + ) -> Self::Result + where + F: Fn(T::AccountId, T::Public) -> TPayload, + TPayload: SignedPayload { + unimplemented!() + } + } + + + /// traits + + // pub trait Sign: Sized { + // type WithAll; + // type WithAny; + + // fn accounts(public: Vec) -> Self; + + // fn with_all(self) -> Self::WithAll; + // fn with_any(self) -> Self::WithAny; + // } + + pub trait SigningTypes { + type AccountId; + type Public: Clone + IdentifyAccount; + type RuntimeAppPublic: From + RuntimeAppPublic; + type Signature: Into<::Signature> + + From<::Signature>; + } + + pub trait SubmitTransactionTypes { + type Call: From; + } + + pub trait SignMessage { + type Result; + + // fn sign_message(&self, message: &[u8]) -> Self::Result; + + fn sign(&self, f: F) -> Self::Result where + F: Fn(T::AccountId, T::Public) -> TPayload, + TPayload: SignedPayload, + ; + } + + pub trait SendSignedTransaction, C> { + type Result; + + fn send_signed_transaction( + &self, + f: impl Fn(T::AccountId, T::Public) -> T::Call, + ) -> Self::Result; + } + + pub trait SendUnsignedTransaction, C> { + type Result; + + fn send_unsigned_transaction( + &self, + f: F, + f2: impl Fn(TPayload, T::Signature) -> T::Call, + ) -> Self::Result + where + F: Fn(T::AccountId, T::Public) -> TPayload, + TPayload: SignedPayload; + } + + pub trait SendRawUnsignedTransaction, C> { + fn send_raw_unsigned_transaction(call: T::Call) -> Result<(), ()>; + } + + pub trait SignedPayload: Encode { + fn public(&self) -> T::Public; + + fn sign(&self) -> Option { + let x = T::RuntimeAppPublic::from(self.public()); + x.sign(&self.encode()).map(Into::into) + } + + fn verify(&self, signature: T::Signature) -> bool { + let x = T::RuntimeAppPublic::from(self.public()); + x.verify(&self.encode(), &signature.into()) + } + } +} + /// Creates runtime-specific signed transaction. /// /// This trait should be implemented by your `Runtime` to be able From c27c7bd84a96894901d7575f9658db27fc1ac1a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 26 Feb 2020 10:56:40 +0100 Subject: [PATCH 002/108] Use in im-online --- bin/node/runtime/src/lib.rs | 18 ++++- frame/im-online/src/lib.rs | 4 +- frame/system/src/offchain.rs | 137 ++++++++++++++++++++-------------- primitives/runtime/src/lib.rs | 21 ++++++ 4 files changed, 120 insertions(+), 60 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index debdafb18df10..373c0a16792f7 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -451,12 +451,28 @@ parameter_types! { pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _; } +pub struct SigningTypes; +impl frame_system::offchain::new::SigningTypes for SigningTypes { + type AccountId = ::AccountId; + type Public = ::Signer; + type Signature = Signature; + type RuntimeAppPublic = ImOnlineId; + type GenericSignature = sp_core::sr25519::Signature; + type GenericPublic = sp_core::sr25519::Public; +} + +impl frame_system::offchain::new::SendTransactionTypes for SigningTypes where + Call: From, +{ + type Call = Call; +} + impl pallet_im_online::Trait for Runtime { type AuthorityId = ImOnlineId; type Event = Event; - type Call = Call; type SessionDuration = SessionDuration; type ReportUnresponsiveness = Offences; + type Types = SigningTypes; } impl pallet_offences::Trait for Runtime { diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 5919d679ac4cc..41ad8515c33c9 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -224,8 +224,8 @@ pub trait Trait: frame_system::Trait + pallet_session::historical::Trait { /// The overarching event type. type Event: From> + Into<::Event>; - /// A transaction submitter / signing types. - type Types: new::SigningTypes + new::SubmitTransactionTypes>; + // A transaction submitter / signing types. + type Types: new::SigningTypes + new::SendTransactionTypes>; /// An expected duration of the session. /// diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 9340590473844..96467576769eb 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -31,35 +31,36 @@ pub mod new { pub enum ForAny {} pub struct Signer { - accounts: Vec, - _phantom: std::marker::PhantomData, + accounts: Option>, + _phantom: sp_std::marker::PhantomData, } - impl Signer { - pub fn accounts(accounts: Vec) -> Self { - Signer { - accounts, + impl Default for Signer { + fn default() -> Self { + Self { + accounts: Default::default(), _phantom: Default::default(), } } + } - pub fn with_all(self) -> Signer { - Signer { - accounts: self.accounts, - _phantom: Default::default(), - } + impl Signer { + pub fn all_accounts() -> Signer { + Default::default() } - pub fn with_any(self) -> Signer { - Signer { - accounts: self.accounts, - _phantom: Default::default(), - } + pub fn any_account() -> Signer { + Default::default() + } + + pub fn with_filter(mut self, accounts: Vec) -> Self { + self.accounts = Some(accounts); + self } } impl< - T: SigningTypes + SubmitTransactionTypes, + T: SigningTypes + SendTransactionTypes, X, C, > SendRawUnsignedTransaction for Signer { @@ -85,14 +86,18 @@ pub mod new { F: Fn(T::AccountId, T::Public) -> TPayload, TPayload: SignedPayload, { - self.accounts - .iter() - .filter_map(|key| { - let account_id = key.clone().into_account(); - let payload = f(account_id, key.clone()); - payload.sign() - }) - .collect() + if let Some(ref accounts) = self.accounts { + accounts + .iter() + .filter_map(|key| { + let account_id = key.clone().into_account(); + let payload = f(account_id, key.clone()); + payload.sign() + }) + .collect() + } else { + unimplemented!() + } } } @@ -113,13 +118,17 @@ pub mod new { F: Fn(T::AccountId, T::Public) -> TPayload, TPayload: SignedPayload, { - for key in &self.accounts { - let account_id = key.clone().into_account(); - let payload = f(account_id, key.clone()); - let res = payload.sign(); - if res.is_some() { - return res + if let Some(ref accounts) = self.accounts { + for key in accounts { + let account_id = key.clone().into_account(); + let payload = f(account_id, key.clone()); + let res = payload.sign(); + if res.is_some() { + return res + } } + } else { + unimplemented!() } None @@ -127,7 +136,7 @@ pub mod new { } impl< - T: SigningTypes + SubmitTransactionTypes, + T: SigningTypes + SendTransactionTypes, C > SendSignedTransaction for Signer { type Result = (T::Public, Result<(), ()>); @@ -141,7 +150,7 @@ pub mod new { } impl< - T: SigningTypes + SubmitTransactionTypes, + T: SigningTypes + SendTransactionTypes, C, > SendSignedTransaction for Signer { type Result = Vec<(T::Public, Result<(), ()>)>; @@ -155,7 +164,7 @@ pub mod new { } impl< - T: SigningTypes + SubmitTransactionTypes, + T: SigningTypes + SendTransactionTypes, C, > SendUnsignedTransaction for Signer { type Result = Option; @@ -173,7 +182,7 @@ pub mod new { } impl< - T: SigningTypes + SubmitTransactionTypes, + T: SigningTypes + SendTransactionTypes, C, > SendUnsignedTransaction for Signer { type Result = Vec>; @@ -190,28 +199,28 @@ pub mod new { } } - /// traits - // pub trait Sign: Sized { - // type WithAll; - // type WithAny; - - // fn accounts(public: Vec) -> Self; - - // fn with_all(self) -> Self::WithAll; - // fn with_any(self) -> Self::WithAny; - // } - pub trait SigningTypes { type AccountId; - type Public: Clone + IdentifyAccount; - type RuntimeAppPublic: From + RuntimeAppPublic; - type Signature: Into<::Signature> - + From<::Signature>; + type Public: Clone + + IdentifyAccount + + From + + TryInto; + type Signature: + From + + TryInto + ; + type RuntimeAppPublic: RuntimeAppPublic; + type GenericPublic: + From + + Into; + type GenericSignature: + From<::Signature> + + Into<::Signature>; } - pub trait SubmitTransactionTypes { + pub trait SendTransactionTypes { type Call: From; } @@ -226,7 +235,7 @@ pub mod new { ; } - pub trait SendSignedTransaction, C> { + pub trait SendSignedTransaction, C> { type Result; fn send_signed_transaction( @@ -235,7 +244,7 @@ pub mod new { ) -> Self::Result; } - pub trait SendUnsignedTransaction, C> { + pub trait SendUnsignedTransaction, C> { type Result; fn send_unsigned_transaction( @@ -248,7 +257,7 @@ pub mod new { TPayload: SignedPayload; } - pub trait SendRawUnsignedTransaction, C> { + pub trait SendRawUnsignedTransaction, C> { fn send_raw_unsigned_transaction(call: T::Call) -> Result<(), ()>; } @@ -256,12 +265,26 @@ pub mod new { fn public(&self) -> T::Public; fn sign(&self) -> Option { - let x = T::RuntimeAppPublic::from(self.public()); - x.sign(&self.encode()).map(Into::into) + let p: T::GenericPublic = self.public().try_into().ok()?; + let x: T::RuntimeAppPublic = p.into(); + x.sign(&self.encode()) + .map(|x| { + let sig: T::GenericSignature = x.into(); + sig + }) + .map(From::from) } fn verify(&self, signature: T::Signature) -> bool { - let x = T::RuntimeAppPublic::from(self.public()); + let p: T::GenericPublic = match self.public().try_into() { + Ok(a) => a, + _ => return false + }; + let x: T::RuntimeAppPublic = p.into(); + let signature: T::GenericSignature = match signature.try_into() { + Ok(a) => a, + _ => return false + }; x.verify(&self.encode(), &signature.into()) } } diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 6501dafc0e54b..a111f40fb0abe 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -178,18 +178,39 @@ impl From for MultiSignature { } } +impl TryFrom for ed25519::Signature { + type Error = (); + fn try_from(m: MultiSignature) -> Result { + if let MultiSignature::Ed25519(x) = m { Ok(x) } else { Err(()) } + } +} + impl From for MultiSignature { fn from(x: sr25519::Signature) -> Self { MultiSignature::Sr25519(x) } } +impl TryFrom for sr25519::Signature { + type Error = (); + fn try_from(m: MultiSignature) -> Result { + if let MultiSignature::Sr25519(x) = m { Ok(x) } else { Err(()) } + } +} + impl From for MultiSignature { fn from(x: ecdsa::Signature) -> Self { MultiSignature::Ecdsa(x) } } +impl TryFrom for ecdsa::Signature { + type Error = (); + fn try_from(m: MultiSignature) -> Result { + if let MultiSignature::Ecdsa(x) = m { Ok(x) } else { Err(()) } + } +} + impl Default for MultiSignature { fn default() -> Self { MultiSignature::Ed25519(Default::default()) From 1b817e336414070e3101ae0c663eb765a2463629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 26 Feb 2020 11:19:44 +0100 Subject: [PATCH 003/108] Rewrite to use Account --- bin/node/runtime/src/lib.rs | 3 ++ frame/system/src/offchain.rs | 81 +++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 373c0a16792f7..be00f624d6102 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -451,7 +451,9 @@ parameter_types! { pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _; } +// TODO [ToDr] add generic resolution for `RuntimeAppPublic` pub struct SigningTypes; + impl frame_system::offchain::new::SigningTypes for SigningTypes { type AccountId = ::AccountId; type Public = ::Signer; @@ -465,6 +467,7 @@ impl frame_system::offchain::new::SendTransactionTypes for SigningTypes wh Call: From, { type Call = Call; + type Extrinsic = UncheckedExtrinsic; } impl pallet_im_online::Trait for Runtime { diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 96467576769eb..06cd0f7ae39c0 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -64,13 +64,14 @@ pub mod new { X, C, > SendRawUnsignedTransaction for Signer { - fn send_raw_unsigned_transaction(_call: T::Call) -> Result<(), ()> { - unimplemented!() + fn send_raw_unsigned_transaction(call: C) -> Result<(), ()> { + let xt = T::Extrinsic::new(call.into(), None).ok_or(())?; + sp_io::offchain::submit_transaction(xt.encode()) } } impl SignMessage for Signer { - type Result = Vec; + type Result = Vec<(Account, T::Signature)>; // fn sign_message(&self, message: &[u8]) -> Self::Result { // self.accounts @@ -83,16 +84,18 @@ pub mod new { // } fn sign(&self, f: F) -> Self::Result where - F: Fn(T::AccountId, T::Public) -> TPayload, + F: Fn(&Account) -> TPayload, TPayload: SignedPayload, { if let Some(ref accounts) = self.accounts { accounts .iter() - .filter_map(|key| { + .enumerate() + .filter_map(|(index, key)| { let account_id = key.clone().into_account(); - let payload = f(account_id, key.clone()); - payload.sign() + let account = Account::new(index, account_id, key.clone()); + let payload = f(&account); + payload.sign().map(|signature| (account, signature)) }) .collect() } else { @@ -102,7 +105,7 @@ pub mod new { } impl SignMessage for Signer { - type Result = Option; + type Result = Option<(Account, T::Signature)>; // fn sign_message(&self, message: &[u8]) -> Self::Result { // self.accounts @@ -115,16 +118,17 @@ pub mod new { // } fn sign(&self, f: F) -> Self::Result where - F: Fn(T::AccountId, T::Public) -> TPayload, + F: Fn(&Account) -> TPayload, TPayload: SignedPayload, { if let Some(ref accounts) = self.accounts { - for key in accounts { + for (index, key) in accounts.iter().enumerate() { let account_id = key.clone().into_account(); - let payload = f(account_id, key.clone()); + let account = Account::new(index, account_id, key.clone()); + let payload = f(&account); let res = payload.sign(); - if res.is_some() { - return res + if let Some(signature) = res { + return Some((account, signature)) } } } else { @@ -139,11 +143,11 @@ pub mod new { T: SigningTypes + SendTransactionTypes, C > SendSignedTransaction for Signer { - type Result = (T::Public, Result<(), ()>); + type Result = (Account, Result<(), ()>); fn send_signed_transaction( &self, - _f: impl Fn(T::AccountId, T::Public) -> T::Call, + f: impl Fn(&Account) -> T::Call, ) -> Self::Result { unimplemented!() } @@ -153,11 +157,11 @@ pub mod new { T: SigningTypes + SendTransactionTypes, C, > SendSignedTransaction for Signer { - type Result = Vec<(T::Public, Result<(), ()>)>; + type Result = Vec<(Account, Result<(), ()>)>; fn send_signed_transaction( &self, - _f: impl Fn(T::AccountId, T::Public) -> T::Call, + _f: impl Fn(&Account) -> T::Call, ) -> Self::Result { unimplemented!() } @@ -167,7 +171,7 @@ pub mod new { T: SigningTypes + SendTransactionTypes, C, > SendUnsignedTransaction for Signer { - type Result = Option; + type Result = (Account, Result<(), ()>); fn send_unsigned_transaction( &self, @@ -175,8 +179,9 @@ pub mod new { _f2: impl Fn(TPayload, T::Signature) -> T::Call, ) -> Self::Result where - F: Fn(T::AccountId, T::Public) -> TPayload, - TPayload: SignedPayload { + F: Fn(&Account) -> TPayload, + TPayload: SignedPayload + { unimplemented!() } } @@ -193,7 +198,7 @@ pub mod new { _f2: impl Fn(TPayload, T::Signature) -> T::Call, ) -> Self::Result where - F: Fn(T::AccountId, T::Public) -> TPayload, + F: Fn(&Account) -> TPayload, TPayload: SignedPayload { unimplemented!() } @@ -201,6 +206,31 @@ pub mod new { /// traits + pub struct Account { + pub index: usize, + pub id: T::AccountId, + pub public: T::Public, + } + + impl Account { + pub fn new(index: usize, id: T::AccountId, public: T::Public) -> Self { + Self { index, id, public } + } + } + + impl Clone for Account where + T::AccountId: Clone, + T::Public: Clone, + { + fn clone(&self) -> Self { + Self { + index: self.index, + id: self.id.clone(), + public: self.public.clone(), + } + } + } + pub trait SigningTypes { type AccountId; type Public: Clone @@ -222,6 +252,7 @@ pub mod new { pub trait SendTransactionTypes { type Call: From; + type Extrinsic: ExtrinsicT + codec::Encode; } pub trait SignMessage { @@ -230,7 +261,7 @@ pub mod new { // fn sign_message(&self, message: &[u8]) -> Self::Result; fn sign(&self, f: F) -> Self::Result where - F: Fn(T::AccountId, T::Public) -> TPayload, + F: Fn(&Account) -> TPayload, TPayload: SignedPayload, ; } @@ -240,7 +271,7 @@ pub mod new { fn send_signed_transaction( &self, - f: impl Fn(T::AccountId, T::Public) -> T::Call, + f: impl Fn(&Account) -> T::Call, ) -> Self::Result; } @@ -253,12 +284,12 @@ pub mod new { f2: impl Fn(TPayload, T::Signature) -> T::Call, ) -> Self::Result where - F: Fn(T::AccountId, T::Public) -> TPayload, + F: Fn(&Account) -> TPayload, TPayload: SignedPayload; } pub trait SendRawUnsignedTransaction, C> { - fn send_raw_unsigned_transaction(call: T::Call) -> Result<(), ()>; + fn send_raw_unsigned_transaction(call: C) -> Result<(), ()>; } pub trait SignedPayload: Encode { From c1e4fb1d620f45dc4b4e656118127fea80a37eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 26 Feb 2020 11:43:09 +0100 Subject: [PATCH 004/108] DRY signing. --- frame/system/src/offchain.rs | 120 +++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 06cd0f7ae39c0..bf632ba9ce9c2 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -59,6 +59,48 @@ pub mod new { } } + + impl Signer { + fn for_all(&self, f: F) -> Vec<(Account, R)> where + F: Fn(&Account) -> Option, + { + if let Some(ref accounts) = self.accounts { + accounts + .iter() + .enumerate() + .filter_map(|(index, key)| { + let account_id = key.clone().into_account(); + let account = Account::new(index, account_id, key.clone()); + f(&account).map(|res| (account, res)) + }) + .collect() + } else { + unimplemented!() + } + } + } + + impl Signer { + fn for_any(&self, f: F) -> Option<(Account, R)> where + F: Fn(&Account) -> Option, + { + if let Some(ref accounts) = self.accounts { + for (index, key) in accounts.iter().enumerate() { + let account_id = key.clone().into_account(); + let account = Account::new(index, account_id, key.clone()); + let res = f(&account); + if let Some(res) = res { + return Some((account, res)); + } + } + } else { + unimplemented!() + } + + None + } + } + impl< T: SigningTypes + SendTransactionTypes, X, @@ -73,69 +115,30 @@ pub mod new { impl SignMessage for Signer { type Result = Vec<(Account, T::Signature)>; - // fn sign_message(&self, message: &[u8]) -> Self::Result { - // self.accounts - // .iter() - // .filter_map(|key| { - // let runtime_key = T::RuntimeAppPublic::from(key.clone()); - // runtime_key.sign(message) - // }) - // .collect() - // } + fn sign_message(&self, message: &[u8]) -> Self::Result { + self.for_all(|account| sign::(message, account.public.clone())) + } fn sign(&self, f: F) -> Self::Result where F: Fn(&Account) -> TPayload, TPayload: SignedPayload, { - if let Some(ref accounts) = self.accounts { - accounts - .iter() - .enumerate() - .filter_map(|(index, key)| { - let account_id = key.clone().into_account(); - let account = Account::new(index, account_id, key.clone()); - let payload = f(&account); - payload.sign().map(|signature| (account, signature)) - }) - .collect() - } else { - unimplemented!() - } + self.for_all(|account| f(account).sign()) } } impl SignMessage for Signer { type Result = Option<(Account, T::Signature)>; - // fn sign_message(&self, message: &[u8]) -> Self::Result { - // self.accounts - // .iter() - // .filter_map(|key| { - // let runtime_key = T::RuntimeAppPublic::from(key.clone()); - // runtime_key.sign(message) - // }) - // .collect() - // } + fn sign_message(&self, message: &[u8]) -> Self::Result { + self.for_any(|account| sign::(message, account.public.clone())) + } fn sign(&self, f: F) -> Self::Result where F: Fn(&Account) -> TPayload, TPayload: SignedPayload, { - if let Some(ref accounts) = self.accounts { - for (index, key) in accounts.iter().enumerate() { - let account_id = key.clone().into_account(); - let account = Account::new(index, account_id, key.clone()); - let payload = f(&account); - let res = payload.sign(); - if let Some(signature) = res { - return Some((account, signature)) - } - } - } else { - unimplemented!() - } - - None + self.for_any(|account| f(account).sign()) } } @@ -242,6 +245,7 @@ pub mod new { + TryInto ; type RuntimeAppPublic: RuntimeAppPublic; + // TODO [ToDr] The conversions are messy, clean them up. type GenericPublic: From + Into; @@ -258,7 +262,7 @@ pub mod new { pub trait SignMessage { type Result; - // fn sign_message(&self, message: &[u8]) -> Self::Result; + fn sign_message(&self, message: &[u8]) -> Self::Result; fn sign(&self, f: F) -> Self::Result where F: Fn(&Account) -> TPayload, @@ -296,14 +300,7 @@ pub mod new { fn public(&self) -> T::Public; fn sign(&self) -> Option { - let p: T::GenericPublic = self.public().try_into().ok()?; - let x: T::RuntimeAppPublic = p.into(); - x.sign(&self.encode()) - .map(|x| { - let sig: T::GenericSignature = x.into(); - sig - }) - .map(From::from) + sign::(&self.encode(), self.public()) } fn verify(&self, signature: T::Signature) -> bool { @@ -319,6 +316,17 @@ pub mod new { x.verify(&self.encode(), &signature.into()) } } + + fn sign(payload: &[u8], public: T::Public) -> Option { + let p: T::GenericPublic = public.try_into().ok()?; + let x: T::RuntimeAppPublic = p.into(); + x.sign(&payload) + .map(|x| { + let sig: T::GenericSignature = x.into(); + sig + }) + .map(From::from) + } } /// Creates runtime-specific signed transaction. From 033a32540e605f9c1df0f1707be9782339a7f870 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 26 Feb 2020 22:03:16 +0100 Subject: [PATCH 005/108] Implement send_raw_unsigned_transaction --- frame/system/src/offchain.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 9340590473844..39b22bb6df6fd 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -58,16 +58,6 @@ pub mod new { } } - impl< - T: SigningTypes + SubmitTransactionTypes, - X, - C, - > SendRawUnsignedTransaction for Signer { - fn send_raw_unsigned_transaction(_call: T::Call) -> Result<(), ()> { - unimplemented!() - } - } - impl SignMessage for Signer { type Result = Vec; @@ -126,6 +116,17 @@ pub mod new { } } + impl< + T: SigningTypes + SubmitTransactionTypes, + X, + C, + > SendRawUnsignedTransaction for Signer { + fn send_raw_unsigned_transaction(call: T::Call) -> Result<(), ()> { + let xt = T::Extrinsic::new(call.into(), None).ok_or(())?; + sp_io::offchain::submit_transaction(xt.encode()) + } + } + impl< T: SigningTypes + SubmitTransactionTypes, C @@ -213,6 +214,7 @@ pub mod new { pub trait SubmitTransactionTypes { type Call: From; + type Extrinsic: ExtrinsicT + codec::Encode; } pub trait SignMessage { From 459c5dd205fc262068519a116e9100f827716fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 2 Mar 2020 12:22:59 +0100 Subject: [PATCH 006/108] WiP --- frame/system/src/offchain.rs | 63 +++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index bf632ba9ce9c2..8191ceab8ddee 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -104,7 +104,7 @@ pub mod new { impl< T: SigningTypes + SendTransactionTypes, X, - C, + C: Into, > SendRawUnsignedTransaction for Signer { fn send_raw_unsigned_transaction(call: C) -> Result<(), ()> { let xt = T::Extrinsic::new(call.into(), None).ok_or(())?; @@ -143,22 +143,48 @@ pub mod new { } impl< - T: SigningTypes + SendTransactionTypes, + T: SendTransactionTypes, C > SendSignedTransaction for Signer { - type Result = (Account, Result<(), ()>); + type Result = Option<(Account, Result<(), ()>)>; fn send_signed_transaction( &self, f: impl Fn(&Account) -> T::Call, ) -> Self::Result { - unimplemented!() + self.for_any(|account| { + let call = f(account); + let mut account_data = crate::Account::::get(&account.id); + debug::native::debug!( + target: "offchain", + "Creating signed transaction from account: {:?} (nonce: {:?})", + account.id, + account_data.nonce, + ); + let (call, signature) = T::CreateTransaction::create_transaction( + call, + &account.public, + &account.id, + account_data.nonce + )?; + let xt = T::Extrinsic::new(call, Some(signature))?; + let res = sp_io::offchain::submit_transaction(xt.encode()); + + if res.is_ok() { + // increment the nonce. This is fine, since the code should always + // be running in off-chain context, so we NEVER persists data. + account_data.nonce += One::one(); + crate::Account::::insert(&account.id, account_data); + } + + Some(res) + }) } } impl< - T: SigningTypes + SendTransactionTypes, - C, + T: SendTransactionTypes, + C: Into, > SendSignedTransaction for Signer { type Result = Vec<(Account, Result<(), ()>)>; @@ -172,7 +198,7 @@ pub mod new { impl< T: SigningTypes + SendTransactionTypes, - C, + C: Into, > SendUnsignedTransaction for Signer { type Result = (Account, Result<(), ()>); @@ -234,8 +260,8 @@ pub mod new { } } - pub trait SigningTypes { - type AccountId; + pub trait SigningTypes: crate::Trait { + //type AccountId; type Public: Clone + IdentifyAccount + From @@ -254,9 +280,24 @@ pub mod new { + Into<::Signature>; } - pub trait SendTransactionTypes { - type Call: From; + pub trait SendTransactionTypes>: SigningTypes { type Extrinsic: ExtrinsicT + codec::Encode; + type CreateTransaction: CreateTransaction; + } + + pub trait CreateTransaction, LocalCall> { + /// Attempt to create signed extrinsic data that encodes call from given account. + /// + /// Runtime implementation is free to construct the payload to sign and the signature + /// in any way it wants. + /// Returns `None` if signed extrinsic could not be created (either because signing failed + /// or because of any other runtime-specific reason). + fn create_transaction( + call: T::Call, + public: T::Public, + account: T::AccountId, + nonce: T::Index, + ) -> Option<(T::Call, ::SignaturePayload)>; } pub trait SignMessage { From f1b2d62b28409dd7a03846dc58ae3115c9db3aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 2 Mar 2020 13:16:05 +0100 Subject: [PATCH 007/108] Expunge LocalCall --- frame/system/src/offchain.rs | 52 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index c97418cae255f..d59e89318b0a7 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -102,11 +102,10 @@ pub mod new { } impl< - T: SigningTypes + SendTransactionTypes, + T: SigningTypes + SendTransactionTypes, X, - C: Into, - > SendRawUnsignedTransaction for Signer { - fn send_raw_unsigned_transaction(call: C) -> Result<(), ()> { + > SendRawUnsignedTransaction for Signer { + fn send_raw_unsigned_transaction(call: impl Into) -> Result<(), ()> { let xt = T::Extrinsic::new(call.into(), None).ok_or(())?; sp_io::offchain::submit_transaction(xt.encode()) } @@ -142,10 +141,7 @@ pub mod new { } } - impl< - T: SendTransactionTypes, - C - > SendSignedTransaction for Signer { + impl SendSignedTransaction for Signer { type Result = Option<(Account, Result<(), ()>)>; fn send_signed_transaction( @@ -183,21 +179,21 @@ pub mod new { } impl< - T: SendTransactionTypes, + T: SendTransactionTypes, C: Into, - > SendSignedTransaction for Signer { + > SendSignedTransaction { type Result = Vec<(Account, Result<(), ()>)>; fn send_signed_transaction( &self, - _f: impl Fn(&Account) -> T::Call, + _f: impl Fn(&Account) -> C, ) -> Self::Result { unimplemented!() } } impl< - T: SigningTypes + SendTransactionTypes, + T: SigningTypes + SendTransactionTypes, C: Into, > SendUnsignedTransaction for Signer { type Result = (Account, Result<(), ()>); @@ -205,7 +201,7 @@ pub mod new { fn send_unsigned_transaction( &self, _f: F, - _f2: impl Fn(TPayload, T::Signature) -> T::Call, + _f2: impl Fn(TPayload, T::Signature) -> C, ) -> Self::Result where F: Fn(&Account) -> TPayload, @@ -216,15 +212,15 @@ pub mod new { } impl< - T: SigningTypes + SendTransactionTypes, - C, + T: SigningTypes + SendTransactionTypes, + C: Into, > SendUnsignedTransaction for Signer { type Result = Vec>; fn send_unsigned_transaction( &self, _f: F, - _f2: impl Fn(TPayload, T::Signature) -> T::Call, + _f2: impl Fn(TPayload, T::Signature) -> C, ) -> Self::Result where F: Fn(&Account) -> TPayload, @@ -294,12 +290,12 @@ pub mod new { + Into<::Signature>; } - pub trait SendTransactionTypes>: SigningTypes { + pub trait SendTransactionTypes: SigningTypes { type Extrinsic: ExtrinsicT + codec::Encode; - type CreateTransaction: CreateTransaction; + type CreateTransaction: CreateTransaction; } - pub trait CreateTransaction, LocalCall> { + pub trait CreateTransaction { /// Attempt to create signed extrinsic data that encodes call from given account. /// /// Runtime implementation is free to construct the payload to sign and the signature @@ -325,30 +321,36 @@ pub mod new { ; } - pub trait SendSignedTransaction, C> { + pub trait SendSignedTransaction< + T: SigningTypes + SendTransactionTypes, + C: Into, + > { type Result; fn send_signed_transaction( &self, - f: impl Fn(&Account) -> T::Call, + f: impl Fn(&Account) -> C, ) -> Self::Result; } - pub trait SendUnsignedTransaction, C> { + pub trait SendUnsignedTransaction< + T: SigningTypes + SendTransactionTypes, + C: Into, + > { type Result; fn send_unsigned_transaction( &self, f: F, - f2: impl Fn(TPayload, T::Signature) -> T::Call, + f2: impl Fn(TPayload, T::Signature) -> C, ) -> Self::Result where F: Fn(&Account) -> TPayload, TPayload: SignedPayload; } - pub trait SendRawUnsignedTransaction, C> { - fn send_raw_unsigned_transaction(call: C) -> Result<(), ()>; + pub trait SendRawUnsignedTransaction { + fn send_raw_unsigned_transaction(call: impl Into) -> Result<(), ()>; } pub trait SignedPayload: Encode { From e4db565b8dbf120ffc4698435d95c8cffcab5516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 2 Mar 2020 13:21:02 +0100 Subject: [PATCH 008/108] Expunge LocalCall --- frame/system/src/offchain.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index d59e89318b0a7..226731d23433c 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -141,16 +141,19 @@ pub mod new { } } - impl SendSignedTransaction for Signer { + impl< + T: SendTransactionTypes, + C: Into, + > SendSignedTransaction for Signer { type Result = Option<(Account, Result<(), ()>)>; fn send_signed_transaction( &self, - f: impl Fn(&Account) -> T::Call, + f: impl Fn(&Account) -> C, ) -> Self::Result { self.for_any(|account| { let call = f(account); - let mut account_data = crate::Account::::get(&account.id); + let mut account_data = crate::Account::::get(&account.id); debug::native::debug!( target: "offchain", "Creating signed transaction from account: {:?} (nonce: {:?})", @@ -158,9 +161,9 @@ pub mod new { account_data.nonce, ); let (call, signature) = T::CreateTransaction::create_transaction( - call, - &account.public, - &account.id, + call.into(), + account.public.clone(), + account.id.clone(), account_data.nonce )?; let xt = T::Extrinsic::new(call, Some(signature))?; @@ -170,7 +173,7 @@ pub mod new { // increment the nonce. This is fine, since the code should always // be running in off-chain context, so we NEVER persists data. account_data.nonce += One::one(); - crate::Account::::insert(&account.id, account_data); + crate::Account::::insert(&account.id, account_data); } Some(res) @@ -181,7 +184,7 @@ pub mod new { impl< T: SendTransactionTypes, C: Into, - > SendSignedTransaction { + > SendSignedTransaction for Signer { type Result = Vec<(Account, Result<(), ()>)>; fn send_signed_transaction( @@ -266,6 +269,10 @@ pub mod new { From + TryInto ; + // TODO [ToDr] + // since now the `SignintTypes` trait extends `System` trait, we can't + // really have a single `RuntimeAppPublic` here. + // `RuntimeAppPublic` thus needs to be passed in some other way to all the functions. type RuntimeAppPublic: RuntimeAppPublic; // TODO [ToDr] The conversions are messy, clean them up. // From 3e11852cdc307c4d843dab1c7d928a5387fd51a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 2 Mar 2020 13:39:42 +0100 Subject: [PATCH 009/108] Fix compilation. --- frame/im-online/src/lib.rs | 10 +++++----- frame/system/src/offchain.rs | 29 +++++++++++++++-------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 60b4d1f39107f..230ba7587b318 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -217,15 +217,15 @@ pub struct Heartbeat pub authority_index: AuthIndex, } -pub trait Trait: frame_system::Trait + pallet_session::historical::Trait { +pub trait Trait: new::SendTransactionTypes<::Call> + pallet_session::historical::Trait { /// The identifier type for an authority. type AuthorityId: Member + Parameter + RuntimeAppPublic + Default + Ord; /// The overarching event type. type Event: From> + Into<::Event>; - // A transaction submitter / signing types. - type Types: new::SigningTypes + new::SendTransactionTypes>; + /// The overarching event type. + type Call: From>; /// An expected duration of the session. /// @@ -480,8 +480,8 @@ impl Module { call, ); - new::Signer:: - ::send_raw_unsigned_transaction(call.into()) + new::Signer:: + ::send_raw_unsigned_transaction(call) .map_err(|_| OffchainErr::SubmitTransaction)?; Ok(()) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 226731d23433c..b29da14874e5c 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -102,10 +102,11 @@ pub mod new { } impl< - T: SigningTypes + SendTransactionTypes, + T: SigningTypes + SendTransactionTypes, X, - > SendRawUnsignedTransaction for Signer { - fn send_raw_unsigned_transaction(call: impl Into) -> Result<(), ()> { + C: Into, + > SendRawUnsignedTransaction for Signer { + fn send_raw_unsigned_transaction(call: C) -> Result<(), ()> { let xt = T::Extrinsic::new(call.into(), None).ok_or(())?; sp_io::offchain::submit_transaction(xt.encode()) } @@ -142,7 +143,7 @@ pub mod new { } impl< - T: SendTransactionTypes, + T: SendTransactionTypes, C: Into, > SendSignedTransaction for Signer { type Result = Option<(Account, Result<(), ()>)>; @@ -182,7 +183,7 @@ pub mod new { } impl< - T: SendTransactionTypes, + T: SendTransactionTypes, C: Into, > SendSignedTransaction for Signer { type Result = Vec<(Account, Result<(), ()>)>; @@ -196,7 +197,7 @@ pub mod new { } impl< - T: SigningTypes + SendTransactionTypes, + T: SigningTypes + SendTransactionTypes, C: Into, > SendUnsignedTransaction for Signer { type Result = (Account, Result<(), ()>); @@ -215,7 +216,7 @@ pub mod new { } impl< - T: SigningTypes + SendTransactionTypes, + T: SigningTypes + SendTransactionTypes, C: Into, > SendUnsignedTransaction for Signer { type Result = Vec>; @@ -297,12 +298,12 @@ pub mod new { + Into<::Signature>; } - pub trait SendTransactionTypes: SigningTypes { + pub trait SendTransactionTypes>: SigningTypes { type Extrinsic: ExtrinsicT + codec::Encode; - type CreateTransaction: CreateTransaction; + type CreateTransaction: CreateTransaction; } - pub trait CreateTransaction { + pub trait CreateTransaction, C: Into> { /// Attempt to create signed extrinsic data that encodes call from given account. /// /// Runtime implementation is free to construct the payload to sign and the signature @@ -329,7 +330,7 @@ pub mod new { } pub trait SendSignedTransaction< - T: SigningTypes + SendTransactionTypes, + T: SigningTypes + SendTransactionTypes, C: Into, > { type Result; @@ -341,7 +342,7 @@ pub mod new { } pub trait SendUnsignedTransaction< - T: SigningTypes + SendTransactionTypes, + T: SigningTypes + SendTransactionTypes, C: Into, > { type Result; @@ -356,8 +357,8 @@ pub mod new { TPayload: SignedPayload; } - pub trait SendRawUnsignedTransaction { - fn send_raw_unsigned_transaction(call: impl Into) -> Result<(), ()>; + pub trait SendRawUnsignedTransaction, C: Into> { + fn send_raw_unsigned_transaction(call: C) -> Result<(), ()>; } pub trait SignedPayload: Encode { From 726061b04e9c0bb949fdc3dc7df23f84ec123cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 2 Mar 2020 13:47:17 +0100 Subject: [PATCH 010/108] Solve call. --- frame/im-online/src/lib.rs | 5 +---- frame/system/src/offchain.rs | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 230ba7587b318..4a6b10e588395 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -217,16 +217,13 @@ pub struct Heartbeat pub authority_index: AuthIndex, } -pub trait Trait: new::SendTransactionTypes<::Call> + pallet_session::historical::Trait { +pub trait Trait: new::SendTransactionTypes> + pallet_session::historical::Trait { /// The identifier type for an authority. type AuthorityId: Member + Parameter + RuntimeAppPublic + Default + Ord; /// The overarching event type. type Event: From> + Into<::Event>; - /// The overarching event type. - type Call: From>; - /// An expected duration of the session. /// /// This parameter is used to determine the longevity of `heartbeat` transaction diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index b29da14874e5c..15e8897229bab 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -104,7 +104,7 @@ pub mod new { impl< T: SigningTypes + SendTransactionTypes, X, - C: Into, + C, > SendRawUnsignedTransaction for Signer { fn send_raw_unsigned_transaction(call: C) -> Result<(), ()> { let xt = T::Extrinsic::new(call.into(), None).ok_or(())?; @@ -144,7 +144,7 @@ pub mod new { impl< T: SendTransactionTypes, - C: Into, + C, > SendSignedTransaction for Signer { type Result = Option<(Account, Result<(), ()>)>; @@ -184,7 +184,7 @@ pub mod new { impl< T: SendTransactionTypes, - C: Into, + C, > SendSignedTransaction for Signer { type Result = Vec<(Account, Result<(), ()>)>; @@ -198,7 +198,7 @@ pub mod new { impl< T: SigningTypes + SendTransactionTypes, - C: Into, + C, > SendUnsignedTransaction for Signer { type Result = (Account, Result<(), ()>); @@ -217,7 +217,7 @@ pub mod new { impl< T: SigningTypes + SendTransactionTypes, - C: Into, + C, > SendUnsignedTransaction for Signer { type Result = Vec>; @@ -298,12 +298,13 @@ pub mod new { + Into<::Signature>; } - pub trait SendTransactionTypes>: SigningTypes { - type Extrinsic: ExtrinsicT + codec::Encode; - type CreateTransaction: CreateTransaction; + pub trait SendTransactionTypes: SigningTypes { + type Extrinsic: ExtrinsicT + codec::Encode; + type CreateTransaction: CreateTransaction; + type OverarchingCall: From; } - pub trait CreateTransaction, C: Into> { + pub trait CreateTransaction, C> { /// Attempt to create signed extrinsic data that encodes call from given account. /// /// Runtime implementation is free to construct the payload to sign and the signature @@ -311,11 +312,11 @@ pub mod new { /// Returns `None` if signed extrinsic could not be created (either because signing failed /// or because of any other runtime-specific reason). fn create_transaction( - call: T::Call, + call: T::OverarchingCall, public: T::Public, account: T::AccountId, nonce: T::Index, - ) -> Option<(T::Call, ::SignaturePayload)>; + ) -> Option<(T::OverarchingCall, ::SignaturePayload)>; } pub trait SignMessage { @@ -331,7 +332,7 @@ pub mod new { pub trait SendSignedTransaction< T: SigningTypes + SendTransactionTypes, - C: Into, + C, > { type Result; @@ -343,7 +344,7 @@ pub mod new { pub trait SendUnsignedTransaction< T: SigningTypes + SendTransactionTypes, - C: Into, + C, > { type Result; @@ -357,7 +358,7 @@ pub mod new { TPayload: SignedPayload; } - pub trait SendRawUnsignedTransaction, C: Into> { + pub trait SendRawUnsignedTransaction, C> { fn send_raw_unsigned_transaction(call: C) -> Result<(), ()>; } From 6bce38e0813a441fbcbb5f12fe0493939d559763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 2 Mar 2020 14:54:02 +0100 Subject: [PATCH 011/108] Make it compile again. --- bin/node/runtime/src/lib.rs | 26 ++++++++++++++++++-------- frame/system/src/offchain.rs | 4 ++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index be00f624d6102..3d670b3e6dcc2 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -451,23 +451,34 @@ parameter_types! { pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _; } -// TODO [ToDr] add generic resolution for `RuntimeAppPublic` -pub struct SigningTypes; - -impl frame_system::offchain::new::SigningTypes for SigningTypes { - type AccountId = ::AccountId; +impl frame_system::offchain::new::SigningTypes for Runtime { type Public = ::Signer; type Signature = Signature; + // TODO [ToDr] add generic resolution for `RuntimeAppPublic` type RuntimeAppPublic = ImOnlineId; type GenericSignature = sp_core::sr25519::Signature; type GenericPublic = sp_core::sr25519::Public; } -impl frame_system::offchain::new::SendTransactionTypes for SigningTypes where +impl frame_system::offchain::new::CreateTransaction for Runtime where + Call: From, +{ + fn create_transaction( + call: Call, + public: ::Signer, + account: AccountId, + nonce: Index, + ) -> Option<(Call, ::SignaturePayload)> { + unimplemented!() + } +} + +impl frame_system::offchain::new::SendTransactionTypes for Runtime where Call: From, { - type Call = Call; + type OverarchingCall = Call; type Extrinsic = UncheckedExtrinsic; + type CreateTransaction = Runtime; } impl pallet_im_online::Trait for Runtime { @@ -475,7 +486,6 @@ impl pallet_im_online::Trait for Runtime { type Event = Event; type SessionDuration = SessionDuration; type ReportUnresponsiveness = Offences; - type Types = SigningTypes; } impl pallet_offences::Trait for Runtime { diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 15e8897229bab..db03aadd2e5ca 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -262,14 +262,14 @@ pub mod new { pub trait SigningTypes: crate::Trait { //type AccountId; + // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? type Public: Clone + IdentifyAccount + From + TryInto; type Signature: From - + TryInto - ; + + TryInto; // TODO [ToDr] // since now the `SignintTypes` trait extends `System` trait, we can't // really have a single `RuntimeAppPublic` here. From 775df429b9439b41b26e71911c3e75006379f0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 2 Mar 2020 16:01:36 +0100 Subject: [PATCH 012/108] Finalize implementation. --- bin/node/runtime/src/lib.rs | 19 +++-- frame/im-online/src/lib.rs | 5 +- frame/system/src/offchain.rs | 130 +++++++++++++++++++---------------- 3 files changed, 90 insertions(+), 64 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 3d670b3e6dcc2..c45d40f7e4f7c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -454,10 +454,6 @@ parameter_types! { impl frame_system::offchain::new::SigningTypes for Runtime { type Public = ::Signer; type Signature = Signature; - // TODO [ToDr] add generic resolution for `RuntimeAppPublic` - type RuntimeAppPublic = ImOnlineId; - type GenericSignature = sp_core::sr25519::Signature; - type GenericPublic = sp_core::sr25519::Public; } impl frame_system::offchain::new::CreateTransaction for Runtime where @@ -478,11 +474,26 @@ impl frame_system::offchain::new::SendTransactionTypes for Runtime where { type OverarchingCall = Call; type Extrinsic = UncheckedExtrinsic; + // TODO [ToDr] This should just extend `SendTransactionTypes` type CreateTransaction = Runtime; } +pub struct ImOnlineAuthId; +impl frame_system::offchain::new::AppCrypto for ImOnlineAuthId { + // TODO [ToDr] Get rid of this trait and instead + // have `RuntimeAppPublic` be able to give you `GenericSignature/GenericPublic` + // or see the proposal at `system/src/offchain.rs`. + // + // That way `AppCrypto` would just have a blanket impl for `RuntimeAppPublic`; + type RuntimeAppPublic = ImOnlineId; + type GenericSignature = sp_core::sr25519::Signature; + type GenericPublic = sp_core::sr25519::Public; +} + + impl pallet_im_online::Trait for Runtime { type AuthorityId = ImOnlineId; + type AuthorityId2 = ImOnlineAuthId; type Event = Event; type SessionDuration = SessionDuration; type ReportUnresponsiveness = Offences; diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 4a6b10e588395..79fbc794c59e4 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -221,6 +221,9 @@ pub trait Trait: new::SendTransactionTypes> + pallet_session::histori /// The identifier type for an authority. type AuthorityId: Member + Parameter + RuntimeAppPublic + Default + Ord; + /// TODO when we remove `AppCrypto` it should just be the same as `AuthorityId` + type AuthorityId2: new::AppCrypto; + /// The overarching event type. type Event: From> + Into<::Event>; @@ -477,7 +480,7 @@ impl Module { call, ); - new::Signer:: + new::Signer:: ::send_raw_unsigned_transaction(call) .map_err(|_| OffchainErr::SubmitTransaction)?; diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index db03aadd2e5ca..98703b448a6fd 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -17,7 +17,7 @@ //! Module helpers for off-chain calls. use codec::Encode; -use sp_std::convert::TryInto; +use sp_std::convert::{TryInto, TryFrom}; use sp_std::prelude::Vec; use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature}; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; @@ -30,12 +30,12 @@ pub mod new { pub enum ForAll {} pub enum ForAny {} - pub struct Signer { + pub struct Signer, X = ForAny> { accounts: Option>, - _phantom: sp_std::marker::PhantomData, + _phantom: sp_std::marker::PhantomData<(X, C)>, } - impl Default for Signer { + impl, X> Default for Signer { fn default() -> Self { Self { accounts: Default::default(), @@ -44,12 +44,12 @@ pub mod new { } } - impl Signer { - pub fn all_accounts() -> Signer { + impl, X> Signer { + pub fn all_accounts() -> Signer { Default::default() } - pub fn any_account() -> Signer { + pub fn any_account() -> Signer { Default::default() } @@ -60,7 +60,7 @@ pub mod new { } - impl Signer { + impl> Signer { fn for_all(&self, f: F) -> Vec<(Account, R)> where F: Fn(&Account) -> Option, { @@ -80,7 +80,7 @@ pub mod new { } } - impl Signer { + impl> Signer { fn for_any(&self, f: F) -> Option<(Account, R)> where F: Fn(&Account) -> Option, { @@ -102,55 +102,57 @@ pub mod new { } impl< - T: SigningTypes + SendTransactionTypes, + T: SigningTypes + SendTransactionTypes, + C: AppCrypto, X, - C, - > SendRawUnsignedTransaction for Signer { - fn send_raw_unsigned_transaction(call: C) -> Result<(), ()> { + LocalCall, + > SendRawUnsignedTransaction for Signer { + fn send_raw_unsigned_transaction(call: LocalCall) -> Result<(), ()> { let xt = T::Extrinsic::new(call.into(), None).ok_or(())?; sp_io::offchain::submit_transaction(xt.encode()) } } - impl SignMessage for Signer { + impl> SignMessage for Signer { type Result = Vec<(Account, T::Signature)>; fn sign_message(&self, message: &[u8]) -> Self::Result { - self.for_all(|account| sign::(message, account.public.clone())) + self.for_all(|account| sign::(message, account.public.clone())) } fn sign(&self, f: F) -> Self::Result where F: Fn(&Account) -> TPayload, TPayload: SignedPayload, { - self.for_all(|account| f(account).sign()) + self.for_all(|account| f(account).sign::()) } } - impl SignMessage for Signer { + impl> SignMessage for Signer { type Result = Option<(Account, T::Signature)>; fn sign_message(&self, message: &[u8]) -> Self::Result { - self.for_any(|account| sign::(message, account.public.clone())) + self.for_any(|account| sign::(message, account.public.clone())) } fn sign(&self, f: F) -> Self::Result where F: Fn(&Account) -> TPayload, TPayload: SignedPayload, { - self.for_any(|account| f(account).sign()) + self.for_any(|account| f(account).sign::()) } } impl< - T: SendTransactionTypes, - C, - > SendSignedTransaction for Signer { + T: SendTransactionTypes, + C: AppCrypto, + LocalCall, + > SendSignedTransaction for Signer { type Result = Option<(Account, Result<(), ()>)>; fn send_signed_transaction( &self, - f: impl Fn(&Account) -> C, + f: impl Fn(&Account) -> LocalCall, ) -> Self::Result { self.for_any(|account| { let call = f(account); @@ -183,29 +185,31 @@ pub mod new { } impl< - T: SendTransactionTypes, - C, - > SendSignedTransaction for Signer { + T: SendTransactionTypes, + C: AppCrypto, + LocalCall, + > SendSignedTransaction for Signer { type Result = Vec<(Account, Result<(), ()>)>; fn send_signed_transaction( &self, - _f: impl Fn(&Account) -> C, + _f: impl Fn(&Account) -> LocalCall, ) -> Self::Result { unimplemented!() } } impl< - T: SigningTypes + SendTransactionTypes, - C, - > SendUnsignedTransaction for Signer { + T: SigningTypes + SendTransactionTypes, + C: AppCrypto, + LocalCall, + > SendUnsignedTransaction for Signer { type Result = (Account, Result<(), ()>); fn send_unsigned_transaction( &self, _f: F, - _f2: impl Fn(TPayload, T::Signature) -> C, + _f2: impl Fn(TPayload, T::Signature) -> LocalCall, ) -> Self::Result where F: Fn(&Account) -> TPayload, @@ -216,15 +220,16 @@ pub mod new { } impl< - T: SigningTypes + SendTransactionTypes, - C, - > SendUnsignedTransaction for Signer { + T: SigningTypes + SendTransactionTypes, + C: AppCrypto, + LocalCall, + > SendUnsignedTransaction for Signer { type Result = Vec>; fn send_unsigned_transaction( &self, _f: F, - _f2: impl Fn(TPayload, T::Signature) -> C, + _f2: impl Fn(TPayload, T::Signature) -> LocalCall, ) -> Self::Result where F: Fn(&Account) -> TPayload, @@ -260,16 +265,7 @@ pub mod new { } } - pub trait SigningTypes: crate::Trait { - //type AccountId; - // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? - type Public: Clone - + IdentifyAccount - + From - + TryInto; - type Signature: - From - + TryInto; + pub trait AppCrypto { // TODO [ToDr] // since now the `SignintTypes` trait extends `System` trait, we can't // really have a single `RuntimeAppPublic` here. @@ -292,10 +288,22 @@ pub mod new { // MutliSigner = From type GenericPublic: From - + Into; + + Into + + TryFrom + + Into; type GenericSignature: From<::Signature> - + Into<::Signature>; + + Into<::Signature> + + TryFrom + + Into; + } + + pub trait SigningTypes: crate::Trait { + //type AccountId; + // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? + type Public: Clone + + IdentifyAccount; + type Signature; } pub trait SendTransactionTypes: SigningTypes { @@ -365,33 +373,37 @@ pub mod new { pub trait SignedPayload: Encode { fn public(&self) -> T::Public; - fn sign(&self) -> Option { - sign::(&self.encode(), self.public()) + fn sign>(&self) -> Option { + sign::(&self.encode(), self.public()) } - fn verify(&self, signature: T::Signature) -> bool { - let p: T::GenericPublic = match self.public().try_into() { + // TODO [ToDr] Clean up variable names, code and conversions here and in sign. + fn verify>(&self, signature: T::Signature) -> bool { + let p: C::GenericPublic = match self.public().try_into() { Ok(a) => a, _ => return false }; - let x: T::RuntimeAppPublic = p.into(); - let signature: T::GenericSignature = match signature.try_into() { + let x = Into::::into(p); + let signature: C::GenericSignature = match signature.try_into() { Ok(a) => a, _ => return false }; - x.verify(&self.encode(), &signature.into()) + let signature = Into::<< + C::RuntimeAppPublic as RuntimeAppPublic + >::Signature>::into(signature); + x.verify(&self.encode(), &signature) } } - fn sign(payload: &[u8], public: T::Public) -> Option { - let p: T::GenericPublic = public.try_into().ok()?; - let x: T::RuntimeAppPublic = p.into(); + fn sign>(payload: &[u8], public: T::Public) -> Option { + let p: C::GenericPublic = public.try_into().ok()?; + let x = Into::::into(p); x.sign(&payload) .map(|x| { - let sig: T::GenericSignature = x.into(); + let sig: C::GenericSignature = x.into(); sig }) - .map(From::from) + .map(Into::into) } } From 930a7cb6f5f474f3703e46aad3166c40a0541795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 2 Mar 2020 16:10:43 +0100 Subject: [PATCH 013/108] Change CreateTransaction --- bin/node/runtime/src/lib.rs | 2 -- frame/system/src/offchain.rs | 23 +++++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index c45d40f7e4f7c..61e224ee64d87 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -474,8 +474,6 @@ impl frame_system::offchain::new::SendTransactionTypes for Runtime where { type OverarchingCall = Call; type Extrinsic = UncheckedExtrinsic; - // TODO [ToDr] This should just extend `SendTransactionTypes` - type CreateTransaction = Runtime; } pub struct ImOnlineAuthId; diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 98703b448a6fd..9fa70133d9698 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -144,7 +144,7 @@ pub mod new { } impl< - T: SendTransactionTypes, + T: CreateSignedTransaction, C: AppCrypto, LocalCall, > SendSignedTransaction for Signer { @@ -163,8 +163,11 @@ pub mod new { account.id, account_data.nonce, ); - let (call, signature) = T::CreateTransaction::create_transaction( + let p: C::GenericPublic = account.public.clone().try_into().ok()?; + let x = Into::::into(p); + let (call, signature) = T::create_transaction::( call.into(), + x, account.public.clone(), account.id.clone(), account_data.nonce @@ -308,23 +311,23 @@ pub mod new { pub trait SendTransactionTypes: SigningTypes { type Extrinsic: ExtrinsicT + codec::Encode; - type CreateTransaction: CreateTransaction; type OverarchingCall: From; } - pub trait CreateTransaction, C> { + pub trait CreateSignedTransaction: SendTransactionTypes { /// Attempt to create signed extrinsic data that encodes call from given account. /// /// Runtime implementation is free to construct the payload to sign and the signature /// in any way it wants. /// Returns `None` if signed extrinsic could not be created (either because signing failed /// or because of any other runtime-specific reason). - fn create_transaction( - call: T::OverarchingCall, - public: T::Public, - account: T::AccountId, - nonce: T::Index, - ) -> Option<(T::OverarchingCall, ::SignaturePayload)>; + fn create_transaction>( + call: Self::OverarchingCall, + crypto: C::RuntimeAppPublic, + public: Self::Public, + account: Self::AccountId, + nonce: Self::Index, + ) -> Option<(Self::OverarchingCall, ::SignaturePayload)>; } pub trait SignMessage { From 31846d9ca6ea420ad40b217032e048bd7178a48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 2 Mar 2020 16:16:34 +0100 Subject: [PATCH 014/108] Clear CreateTransaction. --- bin/node/runtime/src/lib.rs | 5 +++-- frame/system/src/offchain.rs | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 61e224ee64d87..c7865b868a2c5 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -456,11 +456,12 @@ impl frame_system::offchain::new::SigningTypes for Runtime { type Signature = Signature; } -impl frame_system::offchain::new::CreateTransaction for Runtime where +impl frame_system::offchain::new::CreateSignedTransaction for Runtime where Call: From, { - fn create_transaction( + fn create_transaction>( call: Call, + crypto: C::RuntimeAppPublic, public: ::Signer, account: AccountId, nonce: Index, diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 9fa70133d9698..ab7b5696483d7 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -323,6 +323,9 @@ pub mod new { /// or because of any other runtime-specific reason). fn create_transaction>( call: Self::OverarchingCall, + // TODO [ToDr] This probably should be replaced with `SignedPayload` somehow. + // i.e. split `create_transaction` into two parts and let it create some + // `SignedPayload`. crypto: C::RuntimeAppPublic, public: Self::Public, account: Self::AccountId, From 25896f01d1874695a7e545ca43f77c91f05b3e96 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 3 Mar 2020 12:14:57 +0100 Subject: [PATCH 015/108] Add price payload --- frame/example-offchain-worker/src/lib.rs | 39 ++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 1c72a8be68653..25f3f85ce0712 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -40,20 +40,26 @@ //! one unsigned transaction floating in the network. #![cfg_attr(not(feature = "std"), no_std)] +use frame_system::{ + self as system, + ensure_signed, + ensure_none, + offchain::{self, new::{self, SendUnsignedTransaction, SendRawUnsignedTransaction}} +}; use frame_support::{ debug, dispatch::DispatchResult, decl_module, decl_storage, decl_event, traits::Get, weights::SimpleDispatchInfo, }; -use frame_system::{self as system, ensure_signed, ensure_none, offchain}; -use serde_json as json; use sp_core::crypto::KeyTypeId; use sp_runtime::{ offchain::{http, Duration, storage::StorageValueRef}, traits::Zero, transaction_validity::{InvalidTransaction, ValidTransaction, TransactionValidity}, }; +use codec::Encode; +use serde_json as json; #[cfg(test)] mod tests; @@ -77,7 +83,10 @@ pub mod crypto { } /// This pallet's configuration trait -pub trait Trait: frame_system::Trait { +pub trait Trait: new::SendTransactionTypes> { + /// The identifier type for an offchain worker. + type AuthorityId: new::AppCrypto; + /// The type to sign and submit transactions. type SubmitSignedTransaction: offchain::SubmitSignedTransaction::Call>; @@ -105,6 +114,30 @@ pub trait Trait: frame_system::Trait { type UnsignedInterval: Get; } +struct PricePayload { + price: u32, + public: T::Public +} + +impl PricePayload { + fn new(price: u32, public: T::Public) -> PricePayload { + PricePayload { + price, + public + } + } +} + +impl new::SignedPayload for PricePayload { + fn public(&self) -> T::Public { + self.public.clone() + } +} + +impl Encode for PricePayload { + fn encode(&self) -> Vec { Encode::encode(&self.price) } +} + decl_storage! { trait Store for Module as Example { /// A vector of recently submitted prices. From a62d00de91c6335b5838feb1709b8fcb3e4cced7 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 3 Mar 2020 12:22:38 +0100 Subject: [PATCH 016/108] Send raw transaction --- frame/example-offchain-worker/src/lib.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 25f3f85ce0712..b89b51b0b6f61 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -391,7 +391,6 @@ impl Module { /// A helper function to fetch the price and send unsigned transaction. fn fetch_price_and_send_unsigned(block_number: T::BlockNumber) -> Result<(), String> { - use system::offchain::SubmitUnsignedTransaction; // Make sure we don't fetch the price if unsigned transaction is going to be rejected // anyway. let next_unsigned_at = >::get(); @@ -410,13 +409,21 @@ impl Module { // passing `price` as an argument. let call = Call::submit_price_unsigned(block_number, price); - // Now let's create an unsigned transaction out of this call and submit it to the pool. + // Now let's create a transaction out of this call and submit it to the pool. + // Here we showcase multiple ways to send a transaction: + // 1. An unsigned transaction / unsigned payload (raw) + // 2. An unsigned transaction with a signed payload + // 3. A signed transaction + // // By default unsigned transactions are disallowed, so we need to whitelist this case // by writing `UnsignedValidator`. Note that it's EXTREMELY important to carefuly // implement unsigned validation logic, as any mistakes can lead to opening DoS or spam // attack vectors. See validation logic docs for more details. - T::SubmitUnsignedTransaction::submit_unsigned(call) - .map_err(|()| "Unable to submit unsigned transaction.".into()) + let _result_raw: Result<(), String> = new::Signer::::send_raw_unsigned_transaction(call) + .map_err(|()| "Unable to submit unsigned transaction.".into()); + Ok(()) + // T::SubmitUnsignedTransaction::submit_unsigned(call) + // .map_err(|()| "Unable to submit unsigned transaction.".into()) } From 71325fb90771056f348c7d70765a86c07e975fa2 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 3 Mar 2020 12:58:11 +0100 Subject: [PATCH 017/108] Submit signed payload / unsigned transaction (WIP) --- frame/example-offchain-worker/src/lib.rs | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index b89b51b0b6f61..6f7d90461b752 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -219,6 +219,23 @@ decl_module! { Ok(()) } + pub fn submit_price_unsigned_with_signed_payload( + origin, + block_number: T::BlockNumber, + price: u32, + signature: T::Signature + ) -> DispatchResult + { + // This ensures that the function can only be called via unsigned transaction. + ensure_none(origin)?; + // Add the price to the on-chain list, but mark it as coming from an empty address. + Self::add_price(Default::default(), price); + // now increment the block number at which we expect next unsigned transaction. + let current_block = >::block_number(); + >::put(current_block + T::UnsignedInterval::get()); + Ok(()) + } + /// Offchain Worker entry point. /// /// By implementing `fn offchain_worker` within `decl_module!` you declare a new offchain @@ -421,6 +438,14 @@ impl Module { // attack vectors. See validation logic docs for more details. let _result_raw: Result<(), String> = new::Signer::::send_raw_unsigned_transaction(call) .map_err(|()| "Unable to submit unsigned transaction.".into()); + + let _result_signed_payload = new::Signer::::all_accounts().send_unsigned_transaction( + |account| PricePayload::new(price, account.public.clone()), + |payload, signature| { + Call::submit_price_unsigned_with_signed_payload(block_number, payload.price, signature) + } + ); + Ok(()) // T::SubmitUnsignedTransaction::submit_unsigned(call) // .map_err(|()| "Unable to submit unsigned transaction.".into()) From 3324ef709936e42d6fb12a0e1b21ae827726118c Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 3 Mar 2020 14:39:14 +0100 Subject: [PATCH 018/108] Supertrait requirements on T::Signature --- frame/system/src/offchain.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index ab7b5696483d7..337ea7702494c 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -18,6 +18,7 @@ use codec::Encode; use sp_std::convert::{TryInto, TryFrom}; +use sp_std::fmt::Debug; use sp_std::prelude::Vec; use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature}; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; @@ -306,7 +307,7 @@ pub mod new { // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? type Public: Clone + IdentifyAccount; - type Signature; + type Signature: Debug + Clone + PartialEq + codec::Encode + codec::Decode; } pub trait SendTransactionTypes: SigningTypes { From b58512c6eb12f1fd2afcf3c9a33fa3d3413b3cf3 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 3 Mar 2020 15:28:11 +0100 Subject: [PATCH 019/108] Validate signature of payload on an unsigned transaction --- frame/example-offchain-worker/src/lib.rs | 127 ++++++++++++----------- 1 file changed, 69 insertions(+), 58 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 6f7d90461b752..7530d0b1e0b00 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -58,7 +58,7 @@ use sp_runtime::{ traits::Zero, transaction_validity::{InvalidTransaction, ValidTransaction, TransactionValidity}, }; -use codec::Encode; +use codec::{Encode, Decode}; use serde_json as json; #[cfg(test)] @@ -114,6 +114,7 @@ pub trait Trait: new::SendTransactionTypes> { type UnsignedInterval: Get; } +#[derive(Encode, Decode, Clone, PartialEq, Eq)] struct PricePayload { price: u32, public: T::Public @@ -134,10 +135,6 @@ impl new::SignedPayload for PricePayload { } } -impl Encode for PricePayload { - fn encode(&self) -> Vec { Encode::encode(&self.price) } -} - decl_storage! { trait Store for Module as Example { /// A vector of recently submitted prices. @@ -222,14 +219,14 @@ decl_module! { pub fn submit_price_unsigned_with_signed_payload( origin, block_number: T::BlockNumber, - price: u32, + price_payload: PricePayload, signature: T::Signature ) -> DispatchResult { // This ensures that the function can only be called via unsigned transaction. ensure_none(origin)?; // Add the price to the on-chain list, but mark it as coming from an empty address. - Self::add_price(Default::default(), price); + Self::add_price(Default::default(), price_payload.price); // now increment the block number at which we expect next unsigned transaction. let current_block = >::block_number(); >::put(current_block + T::UnsignedInterval::get()); @@ -442,7 +439,7 @@ impl Module { let _result_signed_payload = new::Signer::::all_accounts().send_unsigned_transaction( |account| PricePayload::new(price, account.public.clone()), |payload, signature| { - Call::submit_price_unsigned_with_signed_payload(block_number, payload.price, signature) + Call::submit_price_unsigned_with_signed_payload(block_number, payload, signature) } ); @@ -546,6 +543,58 @@ impl Module { Some(prices.iter().fold(0_u32, |a, b| a.saturating_add(*b)) / prices.len() as u32) } } + + fn validate_transaction_parameters( + block_number: &T::BlockNumber, + new_price: &u32 + ) -> TransactionValidity { + // Now let's check if the transaction has any chance to succeed. + let next_unsigned_at = >::get(); + if &next_unsigned_at > block_number { + return InvalidTransaction::Stale.into(); + } + // Let's make sure to reject transactions from the future. + let current_block = >::block_number(); + if ¤t_block < block_number { + return InvalidTransaction::Future.into(); + } + + // We prioritize transactions that are more far away from current average. + // + // Note this doesn't make much sense when building an actual oracle, but this example + // is here mostly to show off offchain workers capabilities, not about building an + // oracle. + let avg_price = Self::average_price() + .map(|price| if &price > new_price { price - new_price } else { new_price - price }) + .unwrap_or(0); + + Ok(ValidTransaction { + // We set base priority to 2**20 to make sure it's included before any other + // transactions in the pool. Next we tweak the priority depending on how much + // it differs from the current average. (the more it differs the more priority it + // has). + priority: (1 << 20) + avg_price as u64, + // This transaction does not require anything else to go before into the pool. + // In theory we could require `previous_unsigned_at` transaction to go first, + // but it's not necessary in our case. + requires: vec![], + // We set the `provides` tag to be the same as `next_unsigned_at`. This makes + // sure only one transaction produced after `next_unsigned_at` will ever + // get to the transaction pool and will end up in the block. + // We can still have multiple transactions compete for the same "spot", + // and the one with higher priority will replace other one in the pool. + provides: vec![codec::Encode::encode(&(KEY_TYPE.0, next_unsigned_at))], + // The transaction is only valid for next 5 blocks. After that it's + // going to be revalidated by the pool. + longevity: 5, + // It's fine to propagate that transaction to other peers, which means it can be + // created even by nodes that don't produce blocks. + // Note that sometimes it's better to keep it for yourself (if you are the block + // producer), since for instance in some schemes others may copy your solution and + // claim a reward. + propagate: true, + }) + } } #[allow(deprecated)] // ValidateUnsigned @@ -559,55 +608,17 @@ impl frame_support::unsigned::ValidateUnsigned for Module { /// are being whitelisted and marked as valid. fn validate_unsigned(call: &Self::Call) -> TransactionValidity { // Firstly let's check that we call the right function. - if let Call::submit_price_unsigned(block_number, new_price) = call { - // Now let's check if the transaction has any chance to succeed. - let next_unsigned_at = >::get(); - if &next_unsigned_at > block_number { - return InvalidTransaction::Stale.into(); - } - // Let's make sure to reject transactions from the future. - let current_block = >::block_number(); - if ¤t_block < block_number { - return InvalidTransaction::Future.into(); + if let Call::submit_price_unsigned_with_signed_payload(block_number, payload, signature) = call { + let signature_valid = payload.verify(&signature); + if !signature_valid { + return InvalidTransaction::BadProof.into(); } - - // We prioritize transactions that are more far away from current average. - // - // Note this doesn't make much sense when building an actual oracle, but this example - // is here mostly to show off offchain workers capabilities, not about building an - // oracle. - let avg_price = Self::average_price() - .map(|price| if &price > new_price { price - new_price } else { new_price - price }) - .unwrap_or(0); - - Ok(ValidTransaction { - // We set base priority to 2**20 to make sure it's included before any other - // transactions in the pool. Next we tweak the priority depending on how much - // it differs from the current average. (the more it differs the more priority it - // has). - priority: (1 << 20) + avg_price as u64, - // This transaction does not require anything else to go before into the pool. - // In theory we could require `previous_unsigned_at` transaction to go first, - // but it's not necessary in our case. - requires: vec![], - // We set the `provides` tag to be the same as `next_unsigned_at`. This makes - // sure only one transaction produced after `next_unsigned_at` will ever - // get to the transaction pool and will end up in the block. - // We can still have multiple transactions compete for the same "spot", - // and the one with higher priority will replace other one in the pool. - provides: vec![codec::Encode::encode(&(KEY_TYPE.0, next_unsigned_at))], - // The transaction is only valid for next 5 blocks. After that it's - // going to be revalidated by the pool. - longevity: 5, - // It's fine to propagate that transaction to other peers, which means it can be - // created even by nodes that don't produce blocks. - // Note that sometimes it's better to keep it for yourself (if you are the block - // producer), since for instance in some schemes others may copy your solution and - // claim a reward. - propagate: true, - }) - } else { - InvalidTransaction::Call.into() - } - } + Self::validate_transaction_parameters(block_number, payload.price) + } + else if let Call::submit_price_unsigned(block_number, new_price) = call { + Self::validate_transaction_parameters(block_number, new_price) + } else { + InvalidTransaction::Call.into() + } + } } From b850dfdec838c551d003662b0ec73b10e546548d Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 3 Mar 2020 16:00:53 +0100 Subject: [PATCH 020/108] Fix encoding - part 1 --- frame/example-offchain-worker/src/lib.rs | 10 +++++----- frame/system/src/offchain.rs | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 7530d0b1e0b00..a451ab99fa406 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -115,13 +115,13 @@ pub trait Trait: new::SendTransactionTypes> { } #[derive(Encode, Decode, Clone, PartialEq, Eq)] -struct PricePayload { +struct PricePayload { price: u32, - public: T::Public + public: Public } -impl PricePayload { - fn new(price: u32, public: T::Public) -> PricePayload { +impl PricePayload { + fn new(price: u32, public: Public) -> PricePayload { PricePayload { price, public @@ -129,7 +129,7 @@ impl PricePayload { } } -impl new::SignedPayload for PricePayload { +impl new::SignedPayload for PricePayload { fn public(&self) -> T::Public { self.public.clone() } diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 337ea7702494c..e6f51fe971462 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -16,7 +16,7 @@ //! Module helpers for off-chain calls. -use codec::Encode; +use codec::{self, Encode}; use sp_std::convert::{TryInto, TryFrom}; use sp_std::fmt::Debug; use sp_std::prelude::Vec; @@ -305,9 +305,9 @@ pub mod new { pub trait SigningTypes: crate::Trait { //type AccountId; // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? - type Public: Clone + type Public: Clone + codec::Codec + IdentifyAccount; - type Signature: Debug + Clone + PartialEq + codec::Encode + codec::Decode; + type Signature: Debug + Clone + PartialEq + codec::Codec; } pub trait SendTransactionTypes: SigningTypes { From 5aa5f0fb9a8d0495c82135163c2725ba5d6b3abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 3 Mar 2020 16:48:08 +0100 Subject: [PATCH 021/108] Make it compile. --- bin/node/runtime/src/lib.rs | 4 +- frame/example-offchain-worker/src/lib.rs | 36 +++++++------- frame/im-online/src/lib.rs | 2 +- frame/system/src/offchain.rs | 60 ++++++++++++++---------- 4 files changed, 57 insertions(+), 45 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index f717c0d3195dd..a66f0a4dab6fb 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -462,7 +462,7 @@ impl frame_system::offchain::new::SigningTypes for Runtime { impl frame_system::offchain::new::CreateSignedTransaction for Runtime where Call: From, { - fn create_transaction>( + fn create_transaction>( call: Call, crypto: C::RuntimeAppPublic, public: ::Signer, @@ -481,7 +481,7 @@ impl frame_system::offchain::new::SendTransactionTypes for Runtime where } pub struct ImOnlineAuthId; -impl frame_system::offchain::new::AppCrypto for ImOnlineAuthId { +impl frame_system::offchain::new::AppCrypto<::Signer, Signature> for ImOnlineAuthId { // TODO [ToDr] Get rid of this trait and instead // have `RuntimeAppPublic` be able to give you `GenericSignature/GenericPublic` // or see the proposal at `system/src/offchain.rs`. diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index a451ab99fa406..7049ada185a9d 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -85,7 +85,7 @@ pub mod crypto { /// This pallet's configuration trait pub trait Trait: new::SendTransactionTypes> { /// The identifier type for an offchain worker. - type AuthorityId: new::AppCrypto; + type AuthorityId: new::AppCrypto; /// The type to sign and submit transactions. type SubmitSignedTransaction: @@ -114,8 +114,9 @@ pub trait Trait: new::SendTransactionTypes> { type UnsignedInterval: Get; } -#[derive(Encode, Decode, Clone, PartialEq, Eq)] -struct PricePayload { +// TODO [ToDr] This should be RuntimeDebug +#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)] +pub struct PricePayload { price: u32, public: Public } @@ -219,7 +220,7 @@ decl_module! { pub fn submit_price_unsigned_with_signed_payload( origin, block_number: T::BlockNumber, - price_payload: PricePayload, + price_payload: PricePayload, signature: T::Signature ) -> DispatchResult { @@ -607,18 +608,21 @@ impl frame_support::unsigned::ValidateUnsigned for Module { /// here we make sure that some particular calls (the ones produced by offchain worker) /// are being whitelisted and marked as valid. fn validate_unsigned(call: &Self::Call) -> TransactionValidity { + use new::SignedPayload; + // Firstly let's check that we call the right function. - if let Call::submit_price_unsigned_with_signed_payload(block_number, payload, signature) = call { - let signature_valid = payload.verify(&signature); - if !signature_valid { - return InvalidTransaction::BadProof.into(); - } - Self::validate_transaction_parameters(block_number, payload.price) - } - else if let Call::submit_price_unsigned(block_number, new_price) = call { - Self::validate_transaction_parameters(block_number, new_price) - } else { - InvalidTransaction::Call.into() - } + // if let Call::submit_price_unsigned_with_signed_payload(block_number, payload, signature) = call { + // let signature_valid = payload.verify(&signature); + // if !signature_valid { + // return InvalidTransaction::BadProof.into(); + // } + // Self::validate_transaction_parameters(block_number, &payload.price) + // } + // else if let Call::submit_price_unsigned(block_number, new_price) = call { + // Self::validate_transaction_parameters(block_number, new_price) + // } else { + // InvalidTransaction::Call.into() + // } + unimplemented!() } } diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 19c45fa158fc5..6f70baeffac1b 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -222,7 +222,7 @@ pub trait Trait: new::SendTransactionTypes> + pallet_session::histori type AuthorityId: Member + Parameter + RuntimeAppPublic + Default + Ord; /// TODO when we remove `AppCrypto` it should just be the same as `AuthorityId` - type AuthorityId2: new::AppCrypto; + type AuthorityId2: new::AppCrypto; /// The overarching event type. type Event: From> + Into<::Event>; diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index e6f51fe971462..0503c93c8f2a4 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -16,9 +16,8 @@ //! Module helpers for off-chain calls. -use codec::{self, Encode}; +use codec::Encode; use sp_std::convert::{TryInto, TryFrom}; -use sp_std::fmt::Debug; use sp_std::prelude::Vec; use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature}; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; @@ -31,12 +30,12 @@ pub mod new { pub enum ForAll {} pub enum ForAny {} - pub struct Signer, X = ForAny> { + pub struct Signer, X = ForAny> { accounts: Option>, _phantom: sp_std::marker::PhantomData<(X, C)>, } - impl, X> Default for Signer { + impl, X> Default for Signer { fn default() -> Self { Self { accounts: Default::default(), @@ -45,7 +44,7 @@ pub mod new { } } - impl, X> Signer { + impl, X> Signer { pub fn all_accounts() -> Signer { Default::default() } @@ -61,7 +60,7 @@ pub mod new { } - impl> Signer { + impl> Signer { fn for_all(&self, f: F) -> Vec<(Account, R)> where F: Fn(&Account) -> Option, { @@ -81,7 +80,7 @@ pub mod new { } } - impl> Signer { + impl> Signer { fn for_any(&self, f: F) -> Option<(Account, R)> where F: Fn(&Account) -> Option, { @@ -104,7 +103,7 @@ pub mod new { impl< T: SigningTypes + SendTransactionTypes, - C: AppCrypto, + C: AppCrypto, X, LocalCall, > SendRawUnsignedTransaction for Signer { @@ -114,7 +113,7 @@ pub mod new { } } - impl> SignMessage for Signer { + impl> SignMessage for Signer { type Result = Vec<(Account, T::Signature)>; fn sign_message(&self, message: &[u8]) -> Self::Result { @@ -129,7 +128,7 @@ pub mod new { } } - impl> SignMessage for Signer { + impl> SignMessage for Signer { type Result = Option<(Account, T::Signature)>; fn sign_message(&self, message: &[u8]) -> Self::Result { @@ -146,7 +145,7 @@ pub mod new { impl< T: CreateSignedTransaction, - C: AppCrypto, + C: AppCrypto, LocalCall, > SendSignedTransaction for Signer { type Result = Option<(Account, Result<(), ()>)>; @@ -190,7 +189,7 @@ pub mod new { impl< T: SendTransactionTypes, - C: AppCrypto, + C: AppCrypto, LocalCall, > SendSignedTransaction for Signer { type Result = Vec<(Account, Result<(), ()>)>; @@ -205,7 +204,7 @@ pub mod new { impl< T: SigningTypes + SendTransactionTypes, - C: AppCrypto, + C: AppCrypto, LocalCall, > SendUnsignedTransaction for Signer { type Result = (Account, Result<(), ()>); @@ -225,7 +224,7 @@ pub mod new { impl< T: SigningTypes + SendTransactionTypes, - C: AppCrypto, + C: AppCrypto, LocalCall, > SendUnsignedTransaction for Signer { type Result = Vec>; @@ -269,7 +268,7 @@ pub mod new { } } - pub trait AppCrypto { + pub trait AppCrypto { // TODO [ToDr] // since now the `SignintTypes` trait extends `System` trait, we can't // really have a single `RuntimeAppPublic` here. @@ -293,21 +292,28 @@ pub mod new { type GenericPublic: From + Into - + TryFrom - + Into; + + TryFrom + + Into; type GenericSignature: From<::Signature> + Into<::Signature> - + TryFrom - + Into; + + TryFrom + + Into; } pub trait SigningTypes: crate::Trait { //type AccountId; // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? - type Public: Clone + codec::Codec - + IdentifyAccount; - type Signature: Debug + Clone + PartialEq + codec::Codec; + // Seems that this may cause issues with bounds resolution. + type Public: Clone + + PartialEq + + IdentifyAccount + + core::fmt::Debug + + codec::Codec; + type Signature: Clone + + PartialEq + + core::fmt::Debug + + codec::Codec; } pub trait SendTransactionTypes: SigningTypes { @@ -322,7 +328,7 @@ pub mod new { /// in any way it wants. /// Returns `None` if signed extrinsic could not be created (either because signing failed /// or because of any other runtime-specific reason). - fn create_transaction>( + fn create_transaction>( call: Self::OverarchingCall, // TODO [ToDr] This probably should be replaced with `SignedPayload` somehow. // i.e. split `create_transaction` into two parts and let it create some @@ -380,12 +386,13 @@ pub mod new { pub trait SignedPayload: Encode { fn public(&self) -> T::Public; - fn sign>(&self) -> Option { + fn sign>(&self) -> Option { + // TODO [ToDr] use `using_encoded` instead sign::(&self.encode(), self.public()) } // TODO [ToDr] Clean up variable names, code and conversions here and in sign. - fn verify>(&self, signature: T::Signature) -> bool { + fn verify>(&self, signature: T::Signature) -> bool { let p: C::GenericPublic = match self.public().try_into() { Ok(a) => a, _ => return false @@ -398,11 +405,12 @@ pub mod new { let signature = Into::<< C::RuntimeAppPublic as RuntimeAppPublic >::Signature>::into(signature); + // TODO [ToDr] use `using_encoded` instead x.verify(&self.encode(), &signature) } } - fn sign>(payload: &[u8], public: T::Public) -> Option { + fn sign>(payload: &[u8], public: T::Public) -> Option { let p: C::GenericPublic = public.try_into().ok()?; let x = Into::::into(p); x.sign(&payload) From 59ce1ba4d4bb48e038083f2bd6eaafd6a785b554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 3 Mar 2020 16:55:34 +0100 Subject: [PATCH 022/108] Fix compilation of unsigned validator. --- frame/example-offchain-worker/src/lib.rs | 27 ++++++++++++------------ 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 7049ada185a9d..66d61ba75d61c 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -611,18 +611,19 @@ impl frame_support::unsigned::ValidateUnsigned for Module { use new::SignedPayload; // Firstly let's check that we call the right function. - // if let Call::submit_price_unsigned_with_signed_payload(block_number, payload, signature) = call { - // let signature_valid = payload.verify(&signature); - // if !signature_valid { - // return InvalidTransaction::BadProof.into(); - // } - // Self::validate_transaction_parameters(block_number, &payload.price) - // } - // else if let Call::submit_price_unsigned(block_number, new_price) = call { - // Self::validate_transaction_parameters(block_number, new_price) - // } else { - // InvalidTransaction::Call.into() - // } - unimplemented!() + if let Call::submit_price_unsigned_with_signed_payload( + ref block_number, ref payload, ref signature + ) = call { + //let signature_valid = payload.verify::(signature.clone()); + let signature_valid = false; + if !signature_valid { + return InvalidTransaction::BadProof.into(); + } + Self::validate_transaction_parameters(block_number, &payload.price) + } else if let Call::submit_price_unsigned(block_number, new_price) = call { + Self::validate_transaction_parameters(block_number, new_price) + } else { + InvalidTransaction::Call.into() + } } } From 748d065d30c6c9e4fb18bd108d0875f2fdef2729 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 3 Mar 2020 19:47:40 +0100 Subject: [PATCH 023/108] Pass price payload to the transaction --- frame/example-offchain-worker/src/lib.rs | 33 ++++++++++++------------ frame/system/src/offchain.rs | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index a451ab99fa406..c0b6cc33efb1c 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -44,7 +44,12 @@ use frame_system::{ self as system, ensure_signed, ensure_none, - offchain::{self, new::{self, SendUnsignedTransaction, SendRawUnsignedTransaction}} + offchain::{self, new::{ + self, + SendUnsignedTransaction, + SendRawUnsignedTransaction, + SignedPayload + }} }; use frame_support::{ debug, @@ -54,6 +59,7 @@ use frame_support::{ }; use sp_core::crypto::KeyTypeId; use sp_runtime::{ + RuntimeDebug, offchain::{http, Duration, storage::StorageValueRef}, traits::Zero, transaction_validity::{InvalidTransaction, ValidTransaction, TransactionValidity}, @@ -114,21 +120,12 @@ pub trait Trait: new::SendTransactionTypes> { type UnsignedInterval: Get; } -#[derive(Encode, Decode, Clone, PartialEq, Eq)] -struct PricePayload { +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] +pub struct PricePayload { price: u32, public: Public } -impl PricePayload { - fn new(price: u32, public: Public) -> PricePayload { - PricePayload { - price, - public - } - } -} - impl new::SignedPayload for PricePayload { fn public(&self) -> T::Public { self.public.clone() @@ -219,7 +216,7 @@ decl_module! { pub fn submit_price_unsigned_with_signed_payload( origin, block_number: T::BlockNumber, - price_payload: PricePayload, + price_payload: PricePayload, signature: T::Signature ) -> DispatchResult { @@ -437,7 +434,11 @@ impl Module { .map_err(|()| "Unable to submit unsigned transaction.".into()); let _result_signed_payload = new::Signer::::all_accounts().send_unsigned_transaction( - |account| PricePayload::new(price, account.public.clone()), + |account| PricePayload + { + price: price, + public: account.public.clone() + }, |payload, signature| { Call::submit_price_unsigned_with_signed_payload(block_number, payload, signature) } @@ -609,11 +610,11 @@ impl frame_support::unsigned::ValidateUnsigned for Module { fn validate_unsigned(call: &Self::Call) -> TransactionValidity { // Firstly let's check that we call the right function. if let Call::submit_price_unsigned_with_signed_payload(block_number, payload, signature) = call { - let signature_valid = payload.verify(&signature); + let signature_valid = SignedPayload::::verify::(payload, signature.clone()); if !signature_valid { return InvalidTransaction::BadProof.into(); } - Self::validate_transaction_parameters(block_number, payload.price) + Self::validate_transaction_parameters(block_number, &payload.price) } else if let Call::submit_price_unsigned(block_number, new_price) = call { Self::validate_transaction_parameters(block_number, new_price) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index e6f51fe971462..33ab163c60a90 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -305,7 +305,7 @@ pub mod new { pub trait SigningTypes: crate::Trait { //type AccountId; // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? - type Public: Clone + codec::Codec + type Public: Debug + Clone + PartialEq + codec::Codec + IdentifyAccount; type Signature: Debug + Clone + PartialEq + codec::Codec; } From 064812ddd2aa23c79c061376990bf48162ed79ac Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 3 Mar 2020 21:01:16 +0100 Subject: [PATCH 024/108] Make block number part of the signed payload --- frame/example-offchain-worker/src/lib.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 312b2e57c3f59..96fa5b52358d7 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -48,7 +48,6 @@ use frame_system::{ self, SendUnsignedTransaction, SendRawUnsignedTransaction, - SignedPayload }} }; use frame_support::{ @@ -121,12 +120,13 @@ pub trait Trait: new::SendTransactionTypes> { } #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] -pub struct PricePayload { +pub struct PricePayload { + block_number: BlockNumber, price: u32, public: Public } -impl new::SignedPayload for PricePayload { +impl new::SignedPayload for PricePayload { fn public(&self) -> T::Public { self.public.clone() } @@ -215,9 +215,8 @@ decl_module! { pub fn submit_price_unsigned_with_signed_payload( origin, - block_number: T::BlockNumber, - price_payload: PricePayload, - signature: T::Signature + price_payload: PricePayload, + _signature: T::Signature ) -> DispatchResult { // This ensures that the function can only be called via unsigned transaction. @@ -434,13 +433,13 @@ impl Module { .map_err(|()| "Unable to submit unsigned transaction.".into()); let _result_signed_payload = new::Signer::::all_accounts().send_unsigned_transaction( - |account| PricePayload - { - price: price, + |account| PricePayload { + price, + block_number, public: account.public.clone() }, |payload, signature| { - Call::submit_price_unsigned_with_signed_payload(block_number, payload, signature) + Call::submit_price_unsigned_with_signed_payload(payload, signature) } ); @@ -612,13 +611,13 @@ impl frame_support::unsigned::ValidateUnsigned for Module { // Firstly let's check that we call the right function. if let Call::submit_price_unsigned_with_signed_payload( - ref block_number, ref payload, ref signature + ref payload, ref signature ) = call { let signature_valid = SignedPayload::::verify::(payload, signature.clone()); if !signature_valid { return InvalidTransaction::BadProof.into(); } - Self::validate_transaction_parameters(block_number, &payload.price) + Self::validate_transaction_parameters(&payload.block_number, &payload.price) } else if let Call::submit_price_unsigned(block_number, new_price) = call { Self::validate_transaction_parameters(block_number, new_price) } else { From 88c95b348d5d88ffe9e01c7917ae3c6ba26ad08c Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 4 Mar 2020 11:00:39 +0100 Subject: [PATCH 025/108] Send signed transaction --- frame/example-offchain-worker/src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 96fa5b52358d7..0f76420cf6761 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -47,6 +47,7 @@ use frame_system::{ offchain::{self, new::{ self, SendUnsignedTransaction, + SendSignedTransaction, SendRawUnsignedTransaction, }} }; @@ -429,9 +430,12 @@ impl Module { // by writing `UnsignedValidator`. Note that it's EXTREMELY important to carefuly // implement unsigned validation logic, as any mistakes can lead to opening DoS or spam // attack vectors. See validation logic docs for more details. + // + // Method 1 let _result_raw: Result<(), String> = new::Signer::::send_raw_unsigned_transaction(call) .map_err(|()| "Unable to submit unsigned transaction.".into()); + // Method 2 let _result_signed_payload = new::Signer::::all_accounts().send_unsigned_transaction( |account| PricePayload { price, @@ -443,6 +447,11 @@ impl Module { } ); + // Method 3 + let _result_signed_transaction = new::Signer::::all_accounts().send_signed_transaction( + |_account| Call::submit_price(price) + ); + Ok(()) // T::SubmitUnsignedTransaction::submit_unsigned(call) // .map_err(|()| "Unable to submit unsigned transaction.".into()) From 83fe3991f4cf4672c79735aa06153a9e52003873 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 4 Mar 2020 16:47:33 +0100 Subject: [PATCH 026/108] Implement all_accounts, any_account --- frame/system/src/offchain.rs | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 0503c93c8f2a4..15ffe2559a6cd 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -75,7 +75,17 @@ pub mod new { }) .collect() } else { - unimplemented!() + C::RuntimeAppPublic::all() + .into_iter() + .enumerate() + .filter_map(|(index, key)| { + let generic_public = C::GenericPublic::from(key); + let public = generic_public.into(); + let account_id = public.clone().into_account(); + let account = Account::new(index, account_id, public.clone()); + f(&account).map(|res| (account, res)) + }) + .collect() } } } @@ -94,7 +104,20 @@ pub mod new { } } } else { - unimplemented!() + let runtime_keys = C::RuntimeAppPublic::all() + .into_iter() + .enumerate(); + + for (index, key) in runtime_keys { + let generic_public = C::GenericPublic::from(key); + let public = generic_public.into(); + let account_id = public.clone().into_account(); + let account = Account::new(index, account_id, public.clone()); + let res = f(&account); + if let Some(res) = res { + return Some((account, res)); + } + } } None @@ -269,10 +292,6 @@ pub mod new { } pub trait AppCrypto { - // TODO [ToDr] - // since now the `SignintTypes` trait extends `System` trait, we can't - // really have a single `RuntimeAppPublic` here. - // `RuntimeAppPublic` thus needs to be passed in some other way to all the functions. type RuntimeAppPublic: RuntimeAppPublic; // TODO [ToDr] The conversions are messy, clean them up. // From e46b0eef7d060acf13680dcf44af5c3ec09028a0 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 4 Mar 2020 16:49:50 +0100 Subject: [PATCH 027/108] Fix formatting --- frame/system/src/offchain.rs | 50 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 15ffe2559a6cd..9e3bdb00e65b4 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -75,17 +75,17 @@ pub mod new { }) .collect() } else { - C::RuntimeAppPublic::all() - .into_iter() - .enumerate() - .filter_map(|(index, key)| { - let generic_public = C::GenericPublic::from(key); - let public = generic_public.into(); - let account_id = public.clone().into_account(); - let account = Account::new(index, account_id, public.clone()); - f(&account).map(|res| (account, res)) - }) - .collect() + C::RuntimeAppPublic::all() + .into_iter() + .enumerate() + .filter_map(|(index, key)| { + let generic_public = C::GenericPublic::from(key); + let public = generic_public.into(); + let account_id = public.clone().into_account(); + let account = Account::new(index, account_id, public.clone()); + f(&account).map(|res| (account, res)) + }) + .collect() } } } @@ -104,20 +104,20 @@ pub mod new { } } } else { - let runtime_keys = C::RuntimeAppPublic::all() - .into_iter() - .enumerate(); - - for (index, key) in runtime_keys { - let generic_public = C::GenericPublic::from(key); - let public = generic_public.into(); - let account_id = public.clone().into_account(); - let account = Account::new(index, account_id, public.clone()); - let res = f(&account); - if let Some(res) = res { - return Some((account, res)); - } - } + let runtime_keys = C::RuntimeAppPublic::all() + .into_iter() + .enumerate(); + + for (index, key) in runtime_keys { + let generic_public = C::GenericPublic::from(key); + let public = generic_public.into(); + let account_id = public.clone().into_account(); + let account = Account::new(index, account_id, public.clone()); + let res = f(&account); + if let Some(res) = res { + return Some((account, res)); + } + } } None From 763f1d99fb41d727a232e0bc17addddf7cdccdda Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 4 Mar 2020 22:16:48 +0100 Subject: [PATCH 028/108] Implement submit_transaction --- frame/example-offchain-worker/src/lib.rs | 18 ++--- frame/system/src/offchain.rs | 85 +++++++++++++----------- 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 0f76420cf6761..f62d39ba0cfea 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -41,15 +41,15 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_system::{ - self as system, - ensure_signed, - ensure_none, - offchain::{self, new::{ - self, - SendUnsignedTransaction, - SendSignedTransaction, - SendRawUnsignedTransaction, - }} + self as system, + ensure_signed, + ensure_none, + offchain::{self, new::{ + self, + SendUnsignedTransaction, + SendSignedTransaction, + SendRawUnsignedTransaction, + }} }; use frame_support::{ debug, diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 9e3bdb00e65b4..ca9d492d4f2f0 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -119,7 +119,6 @@ pub mod new { } } } - None } } @@ -170,58 +169,35 @@ pub mod new { T: CreateSignedTransaction, C: AppCrypto, LocalCall, - > SendSignedTransaction for Signer { + > SendSignedTransaction for Signer { type Result = Option<(Account, Result<(), ()>)>; fn send_signed_transaction( &self, f: impl Fn(&Account) -> LocalCall, ) -> Self::Result { - self.for_any(|account| { + self.for_any(|account| { let call = f(account); - let mut account_data = crate::Account::::get(&account.id); - debug::native::debug!( - target: "offchain", - "Creating signed transaction from account: {:?} (nonce: {:?})", - account.id, - account_data.nonce, - ); - let p: C::GenericPublic = account.public.clone().try_into().ok()?; - let x = Into::::into(p); - let (call, signature) = T::create_transaction::( - call.into(), - x, - account.public.clone(), - account.id.clone(), - account_data.nonce - )?; - let xt = T::Extrinsic::new(call, Some(signature))?; - let res = sp_io::offchain::submit_transaction(xt.encode()); - - if res.is_ok() { - // increment the nonce. This is fine, since the code should always - // be running in off-chain context, so we NEVER persists data. - account_data.nonce += One::one(); - crate::Account::::insert(&account.id, account_data); - } - - Some(res) + self.submit_transaction(account, call) }) } } impl< - T: SendTransactionTypes, + T: CreateSignedTransaction, C: AppCrypto, LocalCall, - > SendSignedTransaction for Signer { + > SendSignedTransaction for Signer { type Result = Vec<(Account, Result<(), ()>)>; fn send_signed_transaction( &self, - _f: impl Fn(&Account) -> LocalCall, + f: impl Fn(&Account) -> LocalCall, ) -> Self::Result { - unimplemented!() + self.for_all(|account| { + let call = f(account); + self.submit_transaction(account, call) + }) } } @@ -371,15 +347,50 @@ pub mod new { } pub trait SendSignedTransaction< - T: SigningTypes + SendTransactionTypes, - C, + T: SigningTypes + CreateSignedTransaction, + C: AppCrypto, + LocalCall > { type Result; fn send_signed_transaction( &self, - f: impl Fn(&Account) -> C, + f: impl Fn(&Account) -> LocalCall, ) -> Self::Result; + + fn submit_transaction( + &self, + account: &Account, + call: LocalCall + ) -> Option> { + let mut account_data = crate::Account::::get(&account.id); + debug::native::debug!( + target: "offchain", + "Creating signed transaction from account: {:?} (nonce: {:?})", + account.id, + account_data.nonce, + ); + let p: C::GenericPublic = account.public.clone().try_into().ok()?; + let x = Into::::into(p); + let (call, signature) = T::create_transaction::( + call.into(), + x, + account.public.clone(), + account.id.clone(), + account_data.nonce + )?; + let xt = T::Extrinsic::new(call, Some(signature))?; + let res = sp_io::offchain::submit_transaction(xt.encode()); + + if res.is_ok() { + // increment the nonce. This is fine, since the code should always + // be running in off-chain context, so we NEVER persists data. + account_data.nonce += One::one(); + crate::Account::::insert(&account.id, account_data); + } + + Some(res) + } } pub trait SendUnsignedTransaction< From eb287548637c7137701786147c460f87b9288a21 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 4 Mar 2020 23:01:19 +0100 Subject: [PATCH 029/108] Submit signed transaction (ForAll, ForAny) --- frame/system/src/offchain.rs | 46 +++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index ca9d492d4f2f0..b72c818eb3c03 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -178,7 +178,7 @@ pub mod new { ) -> Self::Result { self.for_any(|account| { let call = f(account); - self.submit_transaction(account, call) + self.submit_signed_transaction(account, call) }) } } @@ -196,7 +196,7 @@ pub mod new { ) -> Self::Result { self.for_all(|account| { let call = f(account); - self.submit_transaction(account, call) + self.submit_signed_transaction(account, call) }) } } @@ -206,18 +206,23 @@ pub mod new { C: AppCrypto, LocalCall, > SendUnsignedTransaction for Signer { - type Result = (Account, Result<(), ()>); + type Result = Option<(Account, Result<(), ()>)>; fn send_unsigned_transaction( &self, - _f: F, - _f2: impl Fn(TPayload, T::Signature) -> LocalCall, + f: F, + f2: impl Fn(TPayload, T::Signature) -> LocalCall, ) -> Self::Result where F: Fn(&Account) -> TPayload, TPayload: SignedPayload { - unimplemented!() + self.for_any(|account| { + let payload = f(account); + let signature= payload.sign::()?; + let call = f2(payload, signature); + self.submit_unsigned_transaction(call) + }) } } @@ -226,17 +231,22 @@ pub mod new { C: AppCrypto, LocalCall, > SendUnsignedTransaction for Signer { - type Result = Vec>; + type Result = Vec<(Account, Result<(), ()>)>; fn send_unsigned_transaction( &self, - _f: F, - _f2: impl Fn(TPayload, T::Signature) -> LocalCall, + f: F, + f2: impl Fn(TPayload, T::Signature) -> LocalCall, ) -> Self::Result where F: Fn(&Account) -> TPayload, TPayload: SignedPayload { - unimplemented!() + self.for_all(|account| { + let payload = f(account); + let signature = payload.sign::()?; + let call = f2(payload, signature); + self.submit_unsigned_transaction(call) + }) } } @@ -358,7 +368,7 @@ pub mod new { f: impl Fn(&Account) -> LocalCall, ) -> Self::Result; - fn submit_transaction( + fn submit_signed_transaction( &self, account: &Account, call: LocalCall @@ -394,19 +404,27 @@ pub mod new { } pub trait SendUnsignedTransaction< - T: SigningTypes + SendTransactionTypes, - C, + T: SigningTypes + SendTransactionTypes, + LocalCall, > { type Result; fn send_unsigned_transaction( &self, f: F, - f2: impl Fn(TPayload, T::Signature) -> C, + f2: impl Fn(TPayload, T::Signature) -> LocalCall, ) -> Self::Result where F: Fn(&Account) -> TPayload, TPayload: SignedPayload; + + fn submit_unsigned_transaction( + &self, + call: LocalCall + ) -> Option> { + let xt = T::Extrinsic::new(call.into(), None)?; + Some(sp_io::offchain::submit_transaction(xt.encode())) + } } pub trait SendRawUnsignedTransaction, C> { From 2e27baafa19accf16671876edba8d293262ea394 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Fri, 6 Mar 2020 10:21:37 +0100 Subject: [PATCH 030/108] Fix formatting --- frame/example-offchain-worker/src/lib.rs | 64 ++++++++++++------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index f62d39ba0cfea..d36a29771033a 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -91,7 +91,7 @@ pub mod crypto { /// This pallet's configuration trait pub trait Trait: new::SendTransactionTypes> { /// The identifier type for an offchain worker. - type AuthorityId: new::AppCrypto; + type AuthorityId: new::AppCrypto; /// The type to sign and submit transactions. type SubmitSignedTransaction: @@ -122,15 +122,15 @@ pub trait Trait: new::SendTransactionTypes> { #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct PricePayload { - block_number: BlockNumber, - price: u32, - public: Public + block_number: BlockNumber, + price: u32, + public: Public } impl new::SignedPayload for PricePayload { - fn public(&self) -> T::Public { - self.public.clone() - } + fn public(&self) -> T::Public { + self.public.clone() + } } decl_storage! { @@ -214,12 +214,12 @@ decl_module! { Ok(()) } - pub fn submit_price_unsigned_with_signed_payload( - origin, - price_payload: PricePayload, - _signature: T::Signature - ) -> DispatchResult - { + pub fn submit_price_unsigned_with_signed_payload( + origin, + price_payload: PricePayload, + _signature: T::Signature + ) -> DispatchResult + { // This ensures that the function can only be called via unsigned transaction. ensure_none(origin)?; // Add the price to the on-chain list, but mark it as coming from an empty address. @@ -228,7 +228,7 @@ decl_module! { let current_block = >::block_number(); >::put(current_block + T::UnsignedInterval::get()); Ok(()) - } + } /// Offchain Worker entry point. /// @@ -435,24 +435,24 @@ impl Module { let _result_raw: Result<(), String> = new::Signer::::send_raw_unsigned_transaction(call) .map_err(|()| "Unable to submit unsigned transaction.".into()); - // Method 2 - let _result_signed_payload = new::Signer::::all_accounts().send_unsigned_transaction( - |account| PricePayload { - price, - block_number, - public: account.public.clone() - }, - |payload, signature| { - Call::submit_price_unsigned_with_signed_payload(payload, signature) - } - ); - - // Method 3 - let _result_signed_transaction = new::Signer::::all_accounts().send_signed_transaction( - |_account| Call::submit_price(price) - ); - - Ok(()) + // Method 2 + let _result_signed_payload = new::Signer::::all_accounts().send_unsigned_transaction( + |account| PricePayload { + price, + block_number, + public: account.public.clone() + }, + |payload, signature| { + Call::submit_price_unsigned_with_signed_payload(payload, signature) + } + ); + + // Method 3 + let _result_signed_transaction = new::Signer::::all_accounts().send_signed_transaction( + |_account| Call::submit_price(price) + ); + + Ok(()) // T::SubmitUnsignedTransaction::submit_unsigned(call) // .map_err(|()| "Unable to submit unsigned transaction.".into()) From 5c86a375f38bfda8aadd80f78603d5050da5a288 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Fri, 6 Mar 2020 10:21:51 +0100 Subject: [PATCH 031/108] Implement CreateSignedTransaction --- bin/node/runtime/src/lib.rs | 114 ++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 44 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a66f0a4dab6fb..e115d13d9c969 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -31,7 +31,7 @@ pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Index, Moment}; use sp_api::impl_runtime_apis; use sp_runtime::{ - Permill, Perbill, Percent, ApplyExtrinsicResult, RuntimeString, + Permill, Perbill, Percent, ApplyExtrinsicResult, RuntimeString, RuntimeAppPublic, impl_opaque_keys, generic, create_runtime_str, }; use sp_runtime::curve::PiecewiseLinear; @@ -46,7 +46,7 @@ use sp_version::NativeVersion; use sp_core::OpaqueMetadata; use pallet_grandpa::AuthorityList as GrandpaAuthorityList; use pallet_grandpa::fg_primitives; -use pallet_im_online::sr25519::{AuthorityId as ImOnlineId}; +use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; use pallet_contracts_rpc_runtime_api::ContractExecResult; @@ -60,7 +60,7 @@ pub use pallet_balances::Call as BalancesCall; pub use pallet_contracts::Gas; pub use frame_support::StorageValue; pub use pallet_staking::StakerStatus; - +use codec::Encode; /// Implementations of some helper traits passed into runtime modules as associated types. pub mod impls; use impls::{CurrencyToVoteHandler, Author, LinearWeightToFee, TargetedFeeAdjustment}; @@ -469,7 +469,73 @@ impl frame_system::offchain::new::CreateSignedTransaction account: AccountId, nonce: Index, ) -> Option<(Call, ::SignaturePayload)> { - unimplemented!() + // take the biggest period possible. + 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::() + // The `System::block_number` is initialized with `n+1`, + // so the actual block number is `n`. + .saturating_sub(1); + let tip = 0; + let extra: SignedExtra = ( + frame_system::CheckVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + Default::default(), + ); + let raw_payload = SignedPayload::new(call, extra).map_err(|e| { + debug::warn!("Unable to create signed payload: {:?}", e); + }).ok()?; + let signature = crypto.sign(&raw_payload.encode())?; + let address = Indices::unlookup(account); + let (call, extra, _) = raw_payload.deconstruct(); + Some((call, (address, signature.into(), extra))) + } +} + +impl frame_system::offchain::CreateTransaction for Runtime { + type Public = ::Signer; + type Signature = Signature; + + fn create_transaction>( + call: Call, + public: Self::Public, + account: AccountId, + index: Index, + ) -> Option<(Call, ::SignaturePayload)> { + // take the biggest period possible. + 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::() + // The `System::block_number` is initialized with `n+1`, + // so the actual block number is `n`. + .saturating_sub(1); + let tip = 0; + let extra: SignedExtra = ( + frame_system::CheckVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), + frame_system::CheckNonce::::from(index), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + Default::default(), + ); + 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 address = Indices::unlookup(account); + let (call, extra, _) = raw_payload.deconstruct(); + Some((call, (address, signature, extra))) } } @@ -545,46 +611,6 @@ impl pallet_identity::Trait for Runtime { type RegistrarOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; } -impl frame_system::offchain::CreateTransaction for Runtime { - type Public = ::Signer; - type Signature = Signature; - - fn create_transaction>( - call: Call, - public: Self::Public, - account: AccountId, - index: Index, - ) -> Option<(Call, ::SignaturePayload)> { - // take the biggest period possible. - 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::() - // The `System::block_number` is initialized with `n+1`, - // so the actual block number is `n`. - .saturating_sub(1); - let tip = 0; - let extra: SignedExtra = ( - frame_system::CheckVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), - frame_system::CheckNonce::::from(index), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - Default::default(), - ); - 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 address = Indices::unlookup(account); - let (call, extra, _) = raw_payload.deconstruct(); - Some((call, (address, signature, extra))) - } -} - parameter_types! { pub const ConfigDepositBase: Balance = 5 * DOLLARS; pub const FriendDepositFactor: Balance = 50 * CENTS; From 6c3f8b37fc7d5bdbfbf62cc6b44f05d7e80a3a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 6 Mar 2020 10:30:46 +0100 Subject: [PATCH 032/108] Move sign and verify to AppCrypto --- frame/system/src/offchain.rs | 61 ++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index b72c818eb3c03..375a938ea931b 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -139,7 +139,7 @@ pub mod new { type Result = Vec<(Account, T::Signature)>; fn sign_message(&self, message: &[u8]) -> Self::Result { - self.for_all(|account| sign::(message, account.public.clone())) + self.for_all(|account| C::sign(message, account.public.clone())) } fn sign(&self, f: F) -> Self::Result where @@ -154,7 +154,7 @@ pub mod new { type Result = Option<(Account, T::Signature)>; fn sign_message(&self, message: &[u8]) -> Self::Result { - self.for_any(|account| sign::(message, account.public.clone())) + self.for_any(|account| C::sign(message, account.public.clone())) } fn sign(&self, f: F) -> Self::Result where @@ -304,6 +304,35 @@ pub mod new { + Into<::Signature> + TryFrom + Into; + + fn sign(payload: &[u8], public: Public) -> Option { + let p: Self::GenericPublic = public.try_into().ok()?; + let x = Into::::into(p); + x.sign(&payload) + .map(|x| { + let sig: Self::GenericSignature = x.into(); + sig + }) + .map(Into::into) + } + + fn verify(payload: &[u8], public: Public, signature: Signature) -> bool { + let p: Self::GenericPublic = match public.try_into() { + Ok(a) => a, + _ => return false + }; + let x = Into::::into(p); + let signature: Self::GenericSignature = match signature.try_into() { + Ok(a) => a, + _ => return false + }; + let signature = Into::<< + Self::RuntimeAppPublic as RuntimeAppPublic + >::Signature>::into(signature); + + x.verify(&payload, &signature) + } + } pub trait SigningTypes: crate::Trait { @@ -338,6 +367,7 @@ pub mod new { // TODO [ToDr] This probably should be replaced with `SignedPayload` somehow. // i.e. split `create_transaction` into two parts and let it create some // `SignedPayload`. + // Perhaps not really needed? crypto: C::RuntimeAppPublic, public: Self::Public, account: Self::AccountId, @@ -436,38 +466,15 @@ pub mod new { fn sign>(&self) -> Option { // TODO [ToDr] use `using_encoded` instead - sign::(&self.encode(), self.public()) + C::sign(&self.encode(), self.public()) } // TODO [ToDr] Clean up variable names, code and conversions here and in sign. fn verify>(&self, signature: T::Signature) -> bool { - let p: C::GenericPublic = match self.public().try_into() { - Ok(a) => a, - _ => return false - }; - let x = Into::::into(p); - let signature: C::GenericSignature = match signature.try_into() { - Ok(a) => a, - _ => return false - }; - let signature = Into::<< - C::RuntimeAppPublic as RuntimeAppPublic - >::Signature>::into(signature); // TODO [ToDr] use `using_encoded` instead - x.verify(&self.encode(), &signature) + C::verify(&self.encode(), self.public(), signature) } } - - fn sign>(payload: &[u8], public: T::Public) -> Option { - let p: C::GenericPublic = public.try_into().ok()?; - let x = Into::::into(p); - x.sign(&payload) - .map(|x| { - let sig: C::GenericSignature = x.into(); - sig - }) - .map(Into::into) - } } /// Creates runtime-specific signed transaction. From 428b3da394a9068a83308bcf51a4be07b6933de1 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Fri, 6 Mar 2020 11:27:22 +0100 Subject: [PATCH 033/108] Sign transaction --- bin/node/runtime/src/lib.rs | 5 ++--- frame/system/src/offchain.rs | 8 -------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e115d13d9c969..72f4986aa840e 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -31,7 +31,7 @@ pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Index, Moment}; use sp_api::impl_runtime_apis; use sp_runtime::{ - Permill, Perbill, Percent, ApplyExtrinsicResult, RuntimeString, RuntimeAppPublic, + Permill, Perbill, Percent, ApplyExtrinsicResult, RuntimeString, impl_opaque_keys, generic, create_runtime_str, }; use sp_runtime::curve::PiecewiseLinear; @@ -464,7 +464,6 @@ impl frame_system::offchain::new::CreateSignedTransaction { fn create_transaction>( call: Call, - crypto: C::RuntimeAppPublic, public: ::Signer, account: AccountId, nonce: Index, @@ -492,7 +491,7 @@ impl frame_system::offchain::new::CreateSignedTransaction let raw_payload = SignedPayload::new(call, extra).map_err(|e| { debug::warn!("Unable to create signed payload: {:?}", e); }).ok()?; - let signature = crypto.sign(&raw_payload.encode())?; + let signature = C::sign(&raw_payload.encode(), public)?; let address = Indices::unlookup(account); let (call, extra, _) = raw_payload.deconstruct(); Some((call, (address, signature.into(), extra))) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 375a938ea931b..2ac66fb8e623f 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -364,11 +364,6 @@ pub mod new { /// or because of any other runtime-specific reason). fn create_transaction>( call: Self::OverarchingCall, - // TODO [ToDr] This probably should be replaced with `SignedPayload` somehow. - // i.e. split `create_transaction` into two parts and let it create some - // `SignedPayload`. - // Perhaps not really needed? - crypto: C::RuntimeAppPublic, public: Self::Public, account: Self::AccountId, nonce: Self::Index, @@ -410,11 +405,8 @@ pub mod new { account.id, account_data.nonce, ); - let p: C::GenericPublic = account.public.clone().try_into().ok()?; - let x = Into::::into(p); let (call, signature) = T::create_transaction::( call.into(), - x, account.public.clone(), account.id.clone(), account_data.nonce From f8b8dd1d3db8592b75aad77e3c48298d498d39d0 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Fri, 6 Mar 2020 14:23:14 +0100 Subject: [PATCH 034/108] Call `use_encoded` --- frame/system/src/offchain.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 2ac66fb8e623f..8f9abe56dd63b 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -457,14 +457,11 @@ pub mod new { fn public(&self) -> T::Public; fn sign>(&self) -> Option { - // TODO [ToDr] use `using_encoded` instead - C::sign(&self.encode(), self.public()) + self.using_encoded(|payload| C::sign(payload, self.public())) } - // TODO [ToDr] Clean up variable names, code and conversions here and in sign. fn verify>(&self, signature: T::Signature) -> bool { - // TODO [ToDr] use `using_encoded` instead - C::verify(&self.encode(), self.public(), signature) + self.using_encoded(|payload| C::verify(payload, self.public(), signature)) } } } From e6b2f3336158038216d09055af2d52b9979fe2a6 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Sat, 7 Mar 2020 22:26:53 +0100 Subject: [PATCH 035/108] Remove SubmitAndSignTransaction --- frame/system/src/offchain.rs | 1046 ++++++++++++---------------------- 1 file changed, 351 insertions(+), 695 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 8f9abe56dd63b..6860f5c874281 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -19,790 +19,446 @@ use codec::Encode; use sp_std::convert::{TryInto, TryFrom}; use sp_std::prelude::Vec; -use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature}; +use sp_runtime::app_crypto::RuntimeAppPublic; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; use frame_support::{debug, storage::StorageMap}; -pub mod new { - use super::*; +pub enum ForAll {} +pub enum ForAny {} - pub enum ForAll {} - pub enum ForAny {} - - pub struct Signer, X = ForAny> { - accounts: Option>, - _phantom: sp_std::marker::PhantomData<(X, C)>, - } +pub struct Signer, X = ForAny> { + accounts: Option>, + _phantom: sp_std::marker::PhantomData<(X, C)>, +} - impl, X> Default for Signer { - fn default() -> Self { - Self { - accounts: Default::default(), - _phantom: Default::default(), - } +impl, X> Default for Signer { + fn default() -> Self { + Self { + accounts: Default::default(), + _phantom: Default::default(), } } +} - impl, X> Signer { - pub fn all_accounts() -> Signer { - Default::default() - } - - pub fn any_account() -> Signer { - Default::default() - } - - pub fn with_filter(mut self, accounts: Vec) -> Self { - self.accounts = Some(accounts); - self - } +impl, X> Signer { + pub fn all_accounts() -> Signer { + Default::default() } + pub fn any_account() -> Signer { + Default::default() + } - impl> Signer { - fn for_all(&self, f: F) -> Vec<(Account, R)> where - F: Fn(&Account) -> Option, - { - if let Some(ref accounts) = self.accounts { - accounts - .iter() - .enumerate() - .filter_map(|(index, key)| { - let account_id = key.clone().into_account(); - let account = Account::new(index, account_id, key.clone()); - f(&account).map(|res| (account, res)) - }) - .collect() - } else { - C::RuntimeAppPublic::all() - .into_iter() - .enumerate() - .filter_map(|(index, key)| { - let generic_public = C::GenericPublic::from(key); - let public = generic_public.into(); - let account_id = public.clone().into_account(); - let account = Account::new(index, account_id, public.clone()); - f(&account).map(|res| (account, res)) - }) - .collect() - } - } + pub fn with_filter(mut self, accounts: Vec) -> Self { + self.accounts = Some(accounts); + self } +} - impl> Signer { - fn for_any(&self, f: F) -> Option<(Account, R)> where - F: Fn(&Account) -> Option, - { - if let Some(ref accounts) = self.accounts { - for (index, key) in accounts.iter().enumerate() { + +impl> Signer { + fn for_all(&self, f: F) -> Vec<(Account, R)> where + F: Fn(&Account) -> Option, + { + if let Some(ref accounts) = self.accounts { + accounts + .iter() + .enumerate() + .filter_map(|(index, key)| { let account_id = key.clone().into_account(); let account = Account::new(index, account_id, key.clone()); - let res = f(&account); - if let Some(res) = res { - return Some((account, res)); - } - } - } else { - let runtime_keys = C::RuntimeAppPublic::all() - .into_iter() - .enumerate(); - - for (index, key) in runtime_keys { + f(&account).map(|res| (account, res)) + }) + .collect() + } else { + C::RuntimeAppPublic::all() + .into_iter() + .enumerate() + .filter_map(|(index, key)| { let generic_public = C::GenericPublic::from(key); let public = generic_public.into(); let account_id = public.clone().into_account(); let account = Account::new(index, account_id, public.clone()); - let res = f(&account); - if let Some(res) = res { - return Some((account, res)); - } - } - } - None - } - } - - impl< - T: SigningTypes + SendTransactionTypes, - C: AppCrypto, - X, - LocalCall, - > SendRawUnsignedTransaction for Signer { - fn send_raw_unsigned_transaction(call: LocalCall) -> Result<(), ()> { - let xt = T::Extrinsic::new(call.into(), None).ok_or(())?; - sp_io::offchain::submit_transaction(xt.encode()) - } - } - - impl> SignMessage for Signer { - type Result = Vec<(Account, T::Signature)>; - - fn sign_message(&self, message: &[u8]) -> Self::Result { - self.for_all(|account| C::sign(message, account.public.clone())) - } - - fn sign(&self, f: F) -> Self::Result where - F: Fn(&Account) -> TPayload, - TPayload: SignedPayload, - { - self.for_all(|account| f(account).sign::()) + f(&account).map(|res| (account, res)) + }) + .collect() } } +} - impl> SignMessage for Signer { - type Result = Option<(Account, T::Signature)>; - - fn sign_message(&self, message: &[u8]) -> Self::Result { - self.for_any(|account| C::sign(message, account.public.clone())) - } - - fn sign(&self, f: F) -> Self::Result where - F: Fn(&Account) -> TPayload, - TPayload: SignedPayload, - { - self.for_any(|account| f(account).sign::()) +impl> Signer { + fn for_any(&self, f: F) -> Option<(Account, R)> where + F: Fn(&Account) -> Option, + { + if let Some(ref accounts) = self.accounts { + for (index, key) in accounts.iter().enumerate() { + let account_id = key.clone().into_account(); + let account = Account::new(index, account_id, key.clone()); + let res = f(&account); + if let Some(res) = res { + return Some((account, res)); + } + } + } else { + let runtime_keys = C::RuntimeAppPublic::all() + .into_iter() + .enumerate(); + + for (index, key) in runtime_keys { + let generic_public = C::GenericPublic::from(key); + let public = generic_public.into(); + let account_id = public.clone().into_account(); + let account = Account::new(index, account_id, public.clone()); + let res = f(&account); + if let Some(res) = res { + return Some((account, res)); + } + } } + None } +} - impl< - T: CreateSignedTransaction, - C: AppCrypto, - LocalCall, - > SendSignedTransaction for Signer { - type Result = Option<(Account, Result<(), ()>)>; - - fn send_signed_transaction( - &self, - f: impl Fn(&Account) -> LocalCall, - ) -> Self::Result { - self.for_any(|account| { - let call = f(account); - self.submit_signed_transaction(account, call) - }) - } +impl< + T: SigningTypes + SendTransactionTypes, + C: AppCrypto, + X, + LocalCall, +> SendRawUnsignedTransaction for Signer { + fn send_raw_unsigned_transaction(call: LocalCall) -> Result<(), ()> { + let xt = T::Extrinsic::new(call.into(), None).ok_or(())?; + sp_io::offchain::submit_transaction(xt.encode()) } +} - impl< - T: CreateSignedTransaction, - C: AppCrypto, - LocalCall, - > SendSignedTransaction for Signer { - type Result = Vec<(Account, Result<(), ()>)>; - - fn send_signed_transaction( - &self, - f: impl Fn(&Account) -> LocalCall, - ) -> Self::Result { - self.for_all(|account| { - let call = f(account); - self.submit_signed_transaction(account, call) - }) - } - } +impl> SignMessage for Signer { + type Result = Vec<(Account, T::Signature)>; - impl< - T: SigningTypes + SendTransactionTypes, - C: AppCrypto, - LocalCall, - > SendUnsignedTransaction for Signer { - type Result = Option<(Account, Result<(), ()>)>; - - fn send_unsigned_transaction( - &self, - f: F, - f2: impl Fn(TPayload, T::Signature) -> LocalCall, - ) -> Self::Result - where - F: Fn(&Account) -> TPayload, - TPayload: SignedPayload - { - self.for_any(|account| { - let payload = f(account); - let signature= payload.sign::()?; - let call = f2(payload, signature); - self.submit_unsigned_transaction(call) - }) - } + fn sign_message(&self, message: &[u8]) -> Self::Result { + self.for_all(|account| C::sign(message, account.public.clone())) } - impl< - T: SigningTypes + SendTransactionTypes, - C: AppCrypto, - LocalCall, - > SendUnsignedTransaction for Signer { - type Result = Vec<(Account, Result<(), ()>)>; - - fn send_unsigned_transaction( - &self, - f: F, - f2: impl Fn(TPayload, T::Signature) -> LocalCall, - ) -> Self::Result - where - F: Fn(&Account) -> TPayload, - TPayload: SignedPayload { - self.for_all(|account| { - let payload = f(account); - let signature = payload.sign::()?; - let call = f2(payload, signature); - self.submit_unsigned_transaction(call) - }) - } + fn sign(&self, f: F) -> Self::Result where + F: Fn(&Account) -> TPayload, + TPayload: SignedPayload, + { + self.for_all(|account| f(account).sign::()) } +} - /// traits - - pub struct Account { - pub index: usize, - pub id: T::AccountId, - pub public: T::Public, - } +impl> SignMessage for Signer { + type Result = Option<(Account, T::Signature)>; - impl Account { - pub fn new(index: usize, id: T::AccountId, public: T::Public) -> Self { - Self { index, id, public } - } + fn sign_message(&self, message: &[u8]) -> Self::Result { + self.for_any(|account| C::sign(message, account.public.clone())) } - impl Clone for Account where - T::AccountId: Clone, - T::Public: Clone, + fn sign(&self, f: F) -> Self::Result where + F: Fn(&Account) -> TPayload, + TPayload: SignedPayload, { - fn clone(&self) -> Self { - Self { - index: self.index, - id: self.id.clone(), - public: self.public.clone(), - } - } + self.for_any(|account| f(account).sign::()) } +} - pub trait AppCrypto { - type RuntimeAppPublic: RuntimeAppPublic; - // TODO [ToDr] The conversions are messy, clean them up. - // - // The idea would be to have some implementation for `RuntimeAppPublic` - // to convert to and from generic types. - // Maybe even a method like: - // impl RuntimeAppPublic { - // fn into_public>(&self) -> T; - // } - // so an ability to convert the runtime app public into - // some type that is reachable from the inner (wrapped) generic - // crypto type. - // So example: - // ImOnline(sr25519) = RuntimeAppPublic - // sr25519 = Generic - // MutliSigner = From - type GenericPublic: - From - + Into - + TryFrom - + Into; - type GenericSignature: - From<::Signature> - + Into<::Signature> - + TryFrom - + Into; - - fn sign(payload: &[u8], public: Public) -> Option { - let p: Self::GenericPublic = public.try_into().ok()?; - let x = Into::::into(p); - x.sign(&payload) - .map(|x| { - let sig: Self::GenericSignature = x.into(); - sig - }) - .map(Into::into) - } - - fn verify(payload: &[u8], public: Public, signature: Signature) -> bool { - let p: Self::GenericPublic = match public.try_into() { - Ok(a) => a, - _ => return false - }; - let x = Into::::into(p); - let signature: Self::GenericSignature = match signature.try_into() { - Ok(a) => a, - _ => return false - }; - let signature = Into::<< - Self::RuntimeAppPublic as RuntimeAppPublic - >::Signature>::into(signature); - - x.verify(&payload, &signature) - } - +impl< + T: CreateSignedTransaction, + C: AppCrypto, + LocalCall, +> SendSignedTransaction for Signer { + type Result = Option<(Account, Result<(), ()>)>; + + fn send_signed_transaction( + &self, + f: impl Fn(&Account) -> LocalCall, + ) -> Self::Result { + self.for_any(|account| { + let call = f(account); + self.submit_signed_transaction(account, call) + }) } +} - pub trait SigningTypes: crate::Trait { - //type AccountId; - // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? - // Seems that this may cause issues with bounds resolution. - type Public: Clone - + PartialEq - + IdentifyAccount - + core::fmt::Debug - + codec::Codec; - type Signature: Clone - + PartialEq - + core::fmt::Debug - + codec::Codec; +impl< + T: SigningTypes + CreateSignedTransaction, + C: AppCrypto, + LocalCall, +> SendSignedTransaction for Signer { + type Result = Vec<(Account, Result<(), ()>)>; + + fn send_signed_transaction( + &self, + f: impl Fn(&Account) -> LocalCall, + ) -> Self::Result { + self.for_all(|account| { + let call = f(account); + self.submit_signed_transaction(account, call) + }) } +} - pub trait SendTransactionTypes: SigningTypes { - type Extrinsic: ExtrinsicT + codec::Encode; - type OverarchingCall: From; +impl< + T: SigningTypes + SendTransactionTypes, + C: AppCrypto, + LocalCall, +> SendUnsignedTransaction for Signer { + type Result = Option<(Account, Result<(), ()>)>; + + fn send_unsigned_transaction( + &self, + f: F, + f2: impl Fn(TPayload, T::Signature) -> LocalCall, + ) -> Self::Result + where + F: Fn(&Account) -> TPayload, + TPayload: SignedPayload + { + self.for_any(|account| { + let payload = f(account); + let signature= payload.sign::()?; + let call = f2(payload, signature); + self.submit_unsigned_transaction(call) + }) } +} - pub trait CreateSignedTransaction: SendTransactionTypes { - /// Attempt to create signed extrinsic data that encodes call from given account. - /// - /// Runtime implementation is free to construct the payload to sign and the signature - /// in any way it wants. - /// Returns `None` if signed extrinsic could not be created (either because signing failed - /// or because of any other runtime-specific reason). - fn create_transaction>( - call: Self::OverarchingCall, - public: Self::Public, - account: Self::AccountId, - nonce: Self::Index, - ) -> Option<(Self::OverarchingCall, ::SignaturePayload)>; +impl< + T: SigningTypes + SendTransactionTypes, + C: AppCrypto, + LocalCall, +> SendUnsignedTransaction for Signer { + type Result = Vec<(Account, Result<(), ()>)>; + + fn send_unsigned_transaction( + &self, + f: F, + f2: impl Fn(TPayload, T::Signature) -> LocalCall, + ) -> Self::Result + where + F: Fn(&Account) -> TPayload, + TPayload: SignedPayload { + self.for_all(|account| { + let payload = f(account); + let signature = payload.sign::()?; + let call = f2(payload, signature); + self.submit_unsigned_transaction(call) + }) } +} - pub trait SignMessage { - type Result; +/// traits - fn sign_message(&self, message: &[u8]) -> Self::Result; +pub struct Account { + pub index: usize, + pub id: T::AccountId, + pub public: T::Public, +} - fn sign(&self, f: F) -> Self::Result where - F: Fn(&Account) -> TPayload, - TPayload: SignedPayload, - ; +impl Account { + pub fn new(index: usize, id: T::AccountId, public: T::Public) -> Self { + Self { index, id, public } } +} - pub trait SendSignedTransaction< - T: SigningTypes + CreateSignedTransaction, - C: AppCrypto, - LocalCall - > { - type Result; - - fn send_signed_transaction( - &self, - f: impl Fn(&Account) -> LocalCall, - ) -> Self::Result; - - fn submit_signed_transaction( - &self, - account: &Account, - call: LocalCall - ) -> Option> { - let mut account_data = crate::Account::::get(&account.id); - debug::native::debug!( - target: "offchain", - "Creating signed transaction from account: {:?} (nonce: {:?})", - account.id, - account_data.nonce, - ); - let (call, signature) = T::create_transaction::( - call.into(), - account.public.clone(), - account.id.clone(), - account_data.nonce - )?; - let xt = T::Extrinsic::new(call, Some(signature))?; - let res = sp_io::offchain::submit_transaction(xt.encode()); - - if res.is_ok() { - // increment the nonce. This is fine, since the code should always - // be running in off-chain context, so we NEVER persists data. - account_data.nonce += One::one(); - crate::Account::::insert(&account.id, account_data); - } - - Some(res) +impl Clone for Account where + T::AccountId: Clone, + T::Public: Clone, +{ + fn clone(&self) -> Self { + Self { + index: self.index, + id: self.id.clone(), + public: self.public.clone(), } } +} - pub trait SendUnsignedTransaction< - T: SigningTypes + SendTransactionTypes, - LocalCall, - > { - type Result; - - fn send_unsigned_transaction( - &self, - f: F, - f2: impl Fn(TPayload, T::Signature) -> LocalCall, - ) -> Self::Result - where - F: Fn(&Account) -> TPayload, - TPayload: SignedPayload; - - fn submit_unsigned_transaction( - &self, - call: LocalCall - ) -> Option> { - let xt = T::Extrinsic::new(call.into(), None)?; - Some(sp_io::offchain::submit_transaction(xt.encode())) - } +pub trait AppCrypto { + type RuntimeAppPublic: RuntimeAppPublic; + // TODO [ToDr] The conversions are messy, clean them up. + // + // The idea would be to have some implementation for `RuntimeAppPublic` + // to convert to and from generic types. + // Maybe even a method like: + // impl RuntimeAppPublic { + // fn into_public>(&self) -> T; + // } + // so an ability to convert the runtime app public into + // some type that is reachable from the inner (wrapped) generic + // crypto type. + // So example: + // ImOnline(sr25519) = RuntimeAppPublic + // sr25519 = Generic + // MutliSigner = From + type GenericPublic: + From + + Into + + TryFrom + + Into; + type GenericSignature: + From<::Signature> + + Into<::Signature> + + TryFrom + + Into; + + fn sign(payload: &[u8], public: Public) -> Option { + let p: Self::GenericPublic = public.try_into().ok()?; + let x = Into::::into(p); + x.sign(&payload) + .map(|x| { + let sig: Self::GenericSignature = x.into(); + sig + }) + .map(Into::into) } - pub trait SendRawUnsignedTransaction, C> { - fn send_raw_unsigned_transaction(call: C) -> Result<(), ()>; - } + fn verify(payload: &[u8], public: Public, signature: Signature) -> bool { + let p: Self::GenericPublic = match public.try_into() { + Ok(a) => a, + _ => return false + }; + let x = Into::::into(p); + let signature: Self::GenericSignature = match signature.try_into() { + Ok(a) => a, + _ => return false + }; + let signature = Into::<< + Self::RuntimeAppPublic as RuntimeAppPublic + >::Signature>::into(signature); - pub trait SignedPayload: Encode { - fn public(&self) -> T::Public; + x.verify(&payload, &signature) + } - fn sign>(&self) -> Option { - self.using_encoded(|payload| C::sign(payload, self.public())) - } +} - fn verify>(&self, signature: T::Signature) -> bool { - self.using_encoded(|payload| C::verify(payload, self.public(), signature)) - } - } +pub trait SigningTypes: crate::Trait { + //type AccountId; + // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? + // Seems that this may cause issues with bounds resolution. + type Public: Clone + + PartialEq + + IdentifyAccount + + core::fmt::Debug + + codec::Codec; + type Signature: Clone + + PartialEq + + core::fmt::Debug + + codec::Codec; } -/// Creates runtime-specific signed transaction. -/// -/// This trait should be implemented by your `Runtime` to be able -/// to submit `SignedTransaction`s` to the pool from off-chain code. -pub trait CreateTransaction { - /// A `Public` key representing a particular `AccountId`. - type Public: IdentifyAccount + Clone; - /// A `Signature` generated by the `Signer`. - type Signature; +pub trait SendTransactionTypes: SigningTypes { + type Extrinsic: ExtrinsicT + codec::Encode; + type OverarchingCall: From; +} +pub trait CreateSignedTransaction: SendTransactionTypes { /// Attempt to create signed extrinsic data that encodes call from given account. /// /// Runtime implementation is free to construct the payload to sign and the signature /// in any way it wants. /// Returns `None` if signed extrinsic could not be created (either because signing failed /// or because of any other runtime-specific reason). - fn create_transaction>( - call: Extrinsic::Call, + fn create_transaction>( + call: Self::OverarchingCall, public: Self::Public, - account: T::AccountId, - nonce: T::Index, - ) -> Option<(Extrinsic::Call, Extrinsic::SignaturePayload)>; + account: Self::AccountId, + nonce: Self::Index, + ) -> Option<(Self::OverarchingCall, ::SignaturePayload)>; } -/// A trait responsible for signing a payload using given account. -/// -/// This trait is usually going to represent a local public key -/// that has ability to sign arbitrary `Payloads`. -/// -/// NOTE: Most likely you don't need to implement this trait manually. -/// It has a blanket implementation for all `RuntimeAppPublic` types, -/// so it's enough to pass an application-specific crypto type. -/// -/// To easily create `SignedTransaction`s have a look at the -/// [`TransactionSubmitter`] type. -pub trait Signer { - /// Sign any encodable payload with given account and produce a signature. - /// - /// Returns `Some` if signing succeeded and `None` in case the `account` couldn't - /// be used (for instance we couldn't convert it to required application specific crypto). - fn sign(public: Public, payload: &Payload) -> Option; -} +pub trait SignMessage { + type Result; -/// A `Signer` implementation for any `AppPublic` type. -/// -/// This implementation additionally supports conversion to/from multi-signature/multi-signer -/// wrappers. -/// If the wrapped crypto doesn't match `AppPublic`s crypto `None` is returned. -impl Signer for TAnyAppPublic where - TAnyAppPublic: RuntimeAppPublic - + AppPublic - + From<::Generic>, - ::Signature: AppSignature, - Signature: From< - <::Signature as AppSignature>::Generic - >, - Public: TryInto<::Generic> -{ - fn sign(public: Public, raw_payload: &Payload) -> Option { - raw_payload.using_encoded(|payload| { - let public = public.try_into().ok()?; - TAnyAppPublic::from(public).sign(&payload) - .map( - <::Signature as AppSignature> - ::Generic::from - ) - .map(Signature::from) - }) - } + fn sign_message(&self, message: &[u8]) -> Self::Result; + + fn sign(&self, f: F) -> Self::Result where + F: Fn(&Account) -> TPayload, + TPayload: SignedPayload, + ; } -/// Retrieves a public key type for given `SignAndSubmitTransaction`. -pub type PublicOf = < - >::CreateTransaction - as - CreateTransaction>::Extrinsic> ->::Public; - -/// A trait to sign and submit transactions in off-chain calls. -/// -/// NOTE: Most likely you should not implement this trait yourself. -/// There is an implementation for -/// [`TransactionSubmitter`] type, which -/// you should use. -pub trait SignAndSubmitTransaction { - /// Unchecked extrinsic type. - type Extrinsic: ExtrinsicT + codec::Encode; - - /// A runtime-specific type to produce signed data for the extrinsic. - type CreateTransaction: CreateTransaction; - - /// A type used to sign transactions created using `CreateTransaction`. - type Signer: Signer< - PublicOf, - >::Signature, - >; - - /// Sign given call and submit it to the transaction pool. - /// - /// Returns `Ok` if the transaction was submitted correctly - /// and `Err` if the key for given `id` was not found or the - /// transaction was rejected from the pool. - fn sign_and_submit(call: impl Into, public: PublicOf) -> Result<(), ()> { - let call = call.into(); - let id = public.clone().into_account(); - let mut account = super::Account::::get(&id); +pub trait SendSignedTransaction< + T: SigningTypes + CreateSignedTransaction, + C: AppCrypto, + LocalCall +> { + type Result; + + fn send_signed_transaction( + &self, + f: impl Fn(&Account) -> LocalCall, + ) -> Self::Result; + + fn submit_signed_transaction( + &self, + account: &Account, + call: LocalCall + ) -> Option> { + let mut account_data = crate::Account::::get(&account.id); debug::native::debug!( target: "offchain", "Creating signed transaction from account: {:?} (nonce: {:?})", - id, - account.nonce, + account.id, + account_data.nonce, ); - let (call, signature_data) = Self::CreateTransaction - ::create_transaction::(call, public, id.clone(), account.nonce) - .ok_or(())?; - // increment the nonce. This is fine, since the code should always - // be running in off-chain context, so we NEVER persists data. - account.nonce += One::one(); - super::Account::::insert(&id, account); - - let xt = Self::Extrinsic::new(call, Some(signature_data)).ok_or(())?; - sp_io::offchain::submit_transaction(xt.encode()) - } -} - -/// A trait to submit unsigned transactions in off-chain calls. -/// -/// NOTE: Most likely you should not implement this trait yourself. -/// There is an implementation for -/// [`TransactionSubmitter`] type, which -/// you should use. -pub trait SubmitUnsignedTransaction { - /// Unchecked extrinsic type. - type Extrinsic: ExtrinsicT + codec::Encode; - - /// Submit given call to the transaction pool as unsigned transaction. - /// - /// Returns `Ok` if the transaction was submitted correctly - /// and `Err` if transaction was rejected from the pool. - fn submit_unsigned(call: impl Into) -> Result<(), ()> { - let xt = Self::Extrinsic::new(call.into(), None).ok_or(())?; - sp_io::offchain::submit_transaction(xt.encode()) - } -} - -/// A utility trait to easily create signed transactions -/// from accounts in node's local keystore. -/// -/// NOTE: Most likely you should not implement this trait yourself. -/// There is an implementation for -/// [`TransactionSubmitter`] type, which -/// you should use. -pub trait SubmitSignedTransaction { - /// A `SignAndSubmitTransaction` implementation. - type SignAndSubmit: SignAndSubmitTransaction; - - /// Find local keys that match given list of accounts. - /// - /// Technically it finds an intersection between given list of `AccountId`s - /// and accounts that are represented by public keys in local keystore. - /// If `None` is passed it returns all accounts in the keystore. - /// - /// Returns both public keys and `AccountId`s of accounts that are available. - /// Such accounts can later be used to sign a payload or send signed transactions. - fn find_local_keys(accounts: Option>) -> Vec<( - T::AccountId, - PublicOf, - )>; - - /// Find all available local keys. - /// - /// This is equivalent of calling `find_local_keys(None)`. - fn find_all_local_keys() -> Vec<(T::AccountId, PublicOf)> { - Self::find_local_keys(None as Option>) - } - - /// Check if there are keys for any of given accounts that could be used to send a transaction. - /// - /// This check can be used as an early-exit condition to avoid doing too - /// much work, before we actually realise that there are no accounts that you - /// we could use for signing. - fn can_sign_with(accounts: Option>) -> bool { - !Self::find_local_keys(accounts).is_empty() - } - - /// Check if there are any keys that could be used for signing. - /// - /// This is equivalent of calling `can_sign_with(None)`. - fn can_sign() -> bool { - Self::can_sign_with(None as Option>) - } - - /// Create and submit signed transactions from supported accounts. - /// - /// This method should intersect given list of accounts with the ones - /// supported locally and submit signed transaction containing given `Call` - /// with every of them. - /// - /// Returns a vector of results and account ids that were supported. - #[must_use] - fn submit_signed_from( - call: impl Into + Clone, - accounts: impl IntoIterator, - ) -> Vec<(T::AccountId, Result<(), ()>)> { - let keys = Self::find_local_keys(Some(accounts)); - keys.into_iter().map(|(account, pub_key)| { - let call = call.clone().into(); - ( - account, - Self::SignAndSubmit::sign_and_submit(call, pub_key) - ) - }).collect() - } + let (call, signature) = T::create_transaction::( + call.into(), + account.public.clone(), + account.id.clone(), + account_data.nonce + )?; + let xt = T::Extrinsic::new(call, Some(signature))?; + let res = sp_io::offchain::submit_transaction(xt.encode()); + + if res.is_ok() { + // increment the nonce. This is fine, since the code should always + // be running in off-chain context, so we NEVER persists data. + account_data.nonce += One::one(); + crate::Account::::insert(&account.id, account_data); + } - /// Create and submit signed transactions from all local accounts. - /// - /// This method submits a signed transaction from all local accounts - /// for given application crypto. - /// - /// Returns a vector of results and account ids that were supported. - #[must_use] - fn submit_signed( - call: impl Into + Clone, - ) -> Vec<(T::AccountId, Result<(), ()>)> { - let keys = Self::find_all_local_keys(); - keys.into_iter().map(|(account, pub_key)| { - let call = call.clone().into(); - ( - account, - Self::SignAndSubmit::sign_and_submit(call, pub_key) - ) - }).collect() + Some(res) } } -/// A default type used to submit transactions to the pool. -/// -/// This is passed into each runtime as an opaque associated type that can have either of: -/// - [`SignAndSubmitTransaction`] -/// - [`SubmitUnsignedTransaction`] -/// - [`SubmitSignedTransaction`] -/// and used accordingly. -/// -/// This struct should be constructed by providing the following generic parameters: -/// * `Signer` - Usually the application specific key type (see `app_crypto`). -/// * `CreateTransaction` - A type that is able to produce signed transactions, -/// usually it's going to be the entire `Runtime` object. -/// * `Extrinsic` - A runtime-specific type for in-block extrinsics. -/// -/// If you only need the ability to submit unsigned transactions, -/// you may substitute both `Signer` and `CreateTransaction` with any type. -pub struct TransactionSubmitter { - _signer: sp_std::marker::PhantomData<(Signer, CreateTransaction, Extrinsic)>, -} - -impl Default for TransactionSubmitter { - fn default() -> Self { - Self { - _signer: Default::default(), - } +pub trait SendUnsignedTransaction< + T: SigningTypes + SendTransactionTypes, + LocalCall, +> { + type Result; + + fn send_unsigned_transaction( + &self, + f: F, + f2: impl Fn(TPayload, T::Signature) -> LocalCall, + ) -> Self::Result + where + F: Fn(&Account) -> TPayload, + TPayload: SignedPayload; + + fn submit_unsigned_transaction( + &self, + call: LocalCall + ) -> Option> { + let xt = T::Extrinsic::new(call.into(), None)?; + Some(sp_io::offchain::submit_transaction(xt.encode())) } } -/// A blanket implementation to simplify creation of transaction signer & submitter in the runtime. -impl SignAndSubmitTransaction for TransactionSubmitter where - T: crate::Trait, - C: CreateTransaction, - S: Signer<>::Public, >::Signature>, - E: ExtrinsicT + codec::Encode, -{ - type Extrinsic = E; - type CreateTransaction = C; - type Signer = S; +pub trait SendRawUnsignedTransaction, C> { + fn send_raw_unsigned_transaction(call: C) -> Result<(), ()>; } -/// A blanket implementation to use the same submitter for unsigned transactions as well. -impl SubmitUnsignedTransaction for TransactionSubmitter where - T: crate::Trait, - E: ExtrinsicT + codec::Encode, -{ - type Extrinsic = E; -} +pub trait SignedPayload: Encode { + fn public(&self) -> T::Public; -/// A blanket implementation to support local keystore of application-crypto types. -impl SubmitSignedTransaction for TransactionSubmitter where - T: crate::Trait, - C: CreateTransaction, - E: ExtrinsicT + codec::Encode, - S: Signer<>::Public, >::Signature>, - // Make sure we can unwrap the app crypto key. - S: RuntimeAppPublic + AppPublic + Into<::Generic>, - // Make sure we can convert from wrapped crypto to public key (e.g. `MultiSigner`) - S::Generic: Into>, - // For simplicity we require the same trait to implement `SignAndSubmitTransaction` too. - Self: SignAndSubmitTransaction, -{ - type SignAndSubmit = Self; - - fn find_local_keys(accounts: Option>) -> Vec<( - T::AccountId, - PublicOf, - )> { - // Convert app-specific keys into generic ones. - let local_accounts_and_keys = S::all() - .into_iter() - .map(|app_key| { - // unwrap app-crypto - let generic_pub_key: ::Generic = app_key.into(); - // convert to expected public key type (might be MultiSigner) - let signer_pub_key: PublicOf = generic_pub_key.into(); - // lookup accountid for that pubkey - let account = signer_pub_key.clone().into_account(); - (account, signer_pub_key) - }).collect::>(); - - if let Some(accounts) = accounts { - let mut local_accounts_and_keys = local_accounts_and_keys; - // sort by accountId to allow bin-search. - local_accounts_and_keys.sort_by(|a, b| a.0.cmp(&b.0)); - - // get all the matching accounts - accounts.into_iter().filter_map(|acc| { - let idx = local_accounts_and_keys.binary_search_by(|a| a.0.cmp(&acc)).ok()?; - local_accounts_and_keys.get(idx).cloned() - }).collect() - } else { - // just return all account ids and keys - local_accounts_and_keys - } + fn sign>(&self) -> Option { + self.using_encoded(|payload| C::sign(payload, self.public())) } - fn can_sign_with(accounts: Option>) -> bool { - // early exit if we care about any account. - if accounts.is_none() { - !S::all().is_empty() - } else { - !Self::find_local_keys(accounts).is_empty() - } + fn verify>(&self, signature: T::Signature) -> bool { + self.using_encoded(|payload| C::verify(payload, self.public(), signature)) } } + From 1dc8bf0b68e175d035a38d83f7d2e761a1f2e7ab Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Sat, 7 Mar 2020 22:27:48 +0100 Subject: [PATCH 036/108] Implement runtime using new SigningTypes --- bin/node/runtime/src/lib.rs | 59 ++++++------------------------------- 1 file changed, 9 insertions(+), 50 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 72f4986aa840e..f6f38c3c8d438 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -50,7 +50,6 @@ use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; use pallet_contracts_rpc_runtime_api::ContractExecResult; -use frame_system::offchain::TransactionSubmitter; use sp_inherents::{InherentData, CheckInherentsResult}; #[cfg(any(feature = "std", test))] @@ -447,22 +446,19 @@ impl pallet_sudo::Trait for Runtime { type Call = Call; } -/// A runtime transaction submitter. -pub type SubmitTransaction = TransactionSubmitter; - parameter_types! { pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _; } -impl frame_system::offchain::new::SigningTypes for Runtime { +impl frame_system::offchain::SigningTypes for Runtime { type Public = ::Signer; type Signature = Signature; } -impl frame_system::offchain::new::CreateSignedTransaction for Runtime where +impl frame_system::offchain::CreateSignedTransaction for Runtime where Call: From, { - fn create_transaction>( + fn create_transaction>( call: Call, public: ::Signer, account: AccountId, @@ -491,62 +487,25 @@ impl frame_system::offchain::new::CreateSignedTransaction let raw_payload = SignedPayload::new(call, extra).map_err(|e| { debug::warn!("Unable to create signed payload: {:?}", e); }).ok()?; - let signature = C::sign(&raw_payload.encode(), public)?; + let signature = raw_payload.using_encoded(|payload| { + C::sign(payload, public) + })?; let address = Indices::unlookup(account); let (call, extra, _) = raw_payload.deconstruct(); Some((call, (address, signature.into(), extra))) } } -impl frame_system::offchain::CreateTransaction for Runtime { - type Public = ::Signer; - type Signature = Signature; - - fn create_transaction>( - call: Call, - public: Self::Public, - account: AccountId, - index: Index, - ) -> Option<(Call, ::SignaturePayload)> { - // take the biggest period possible. - 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::() - // The `System::block_number` is initialized with `n+1`, - // so the actual block number is `n`. - .saturating_sub(1); - let tip = 0; - let extra: SignedExtra = ( - frame_system::CheckVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), - frame_system::CheckNonce::::from(index), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - Default::default(), - ); - 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 address = Indices::unlookup(account); - let (call, extra, _) = raw_payload.deconstruct(); - Some((call, (address, signature, extra))) - } -} - -impl frame_system::offchain::new::SendTransactionTypes for Runtime where +impl frame_system::offchain::SendTransactionTypes for Runtime where Call: From, { + type OverarchingCall = Call; type Extrinsic = UncheckedExtrinsic; } pub struct ImOnlineAuthId; -impl frame_system::offchain::new::AppCrypto<::Signer, Signature> for ImOnlineAuthId { +impl frame_system::offchain::AppCrypto<::Signer, Signature> for ImOnlineAuthId { // TODO [ToDr] Get rid of this trait and instead // have `RuntimeAppPublic` be able to give you `GenericSignature/GenericPublic` // or see the proposal at `system/src/offchain.rs`. From 9f6685890a3d7fb32526440e5474d3a75954fbda Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Sat, 7 Mar 2020 22:28:04 +0100 Subject: [PATCH 037/108] Adapt offchain example to changes --- frame/example-offchain-worker/src/lib.rs | 192 ++++++++++------------- 1 file changed, 87 insertions(+), 105 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index d36a29771033a..d05dd7973a76b 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -44,12 +44,15 @@ use frame_system::{ self as system, ensure_signed, ensure_none, - offchain::{self, new::{ - self, + offchain::{ + AppCrypto, + CreateSignedTransaction, SendUnsignedTransaction, - SendSignedTransaction, SendRawUnsignedTransaction, - }} + SignedPayload, + SigningTypes, + Signer, + } }; use frame_support::{ debug, @@ -89,16 +92,9 @@ pub mod crypto { } /// This pallet's configuration trait -pub trait Trait: new::SendTransactionTypes> { +pub trait Trait: CreateSignedTransaction> { /// The identifier type for an offchain worker. - type AuthorityId: new::AppCrypto; - - /// The type to sign and submit transactions. - type SubmitSignedTransaction: - offchain::SubmitSignedTransaction::Call>; - /// The type to submit unsigned transactions. - type SubmitUnsignedTransaction: - offchain::SubmitUnsignedTransaction::Call>; + type AuthorityId: AppCrypto; /// The overarching event type. type Event: From> + Into<::Event>; @@ -127,7 +123,7 @@ pub struct PricePayload { public: Public } -impl new::SignedPayload for PricePayload { +impl SignedPayload for PricePayload { fn public(&self) -> T::Public { self.public.clone() } @@ -365,35 +361,28 @@ impl Module { /// A helper function to fetch the price and send signed transaction. fn fetch_price_and_send_signed() -> Result<(), String> { - use system::offchain::SubmitSignedTransaction; - // Firstly we check if there are any accounts in the local keystore that are capable of - // signing the transaction. - // If not it doesn't even make sense to make external HTTP requests, since we won't be able - // to put the results back on-chain. - if !T::SubmitSignedTransaction::can_sign() { - return Err( - "No local accounts available. Consider adding one via `author_insertKey` RPC." - )? - } - + use frame_system::offchain::SendSignedTransaction; // Make an external HTTP request to fetch the current price. // Note this call will block until response is received. let price = Self::fetch_price().map_err(|e| format!("{:?}", e))?; - // Received price is wrapped into a call to `submit_price` public function of this pallet. - // This means that the transaction, when executed, will simply call that function passing - // `price` as an argument. - let call = Call::submit_price(price); - - // Using `SubmitSignedTransaction` associated type we create and submit a transaction + // Using `send_signed_transaction` associated type we create and submit a transaction // representing the call, we've just created. // Submit signed will return a vector of results for all accounts that were found in the // local keystore with expected `KEY_TYPE`. - let results = T::SubmitSignedTransaction::submit_signed(call); + let results = Signer::::all_accounts().send_signed_transaction( + |_account| { + // Received price is wrapped into a call to `submit_price` public function of this pallet. + // This means that the transaction, when executed, will simply call that function passing + // `price` as an argument. + Call::submit_price(price) + } + ); + // let results = T::SubmitSignedTransaction::submit_signed(call); for (acc, res) in &results { match res { - Ok(()) => debug::info!("[{:?}] Submitted price of {} cents", acc, price), - Err(e) => debug::error!("[{:?}] Failed to submit transaction: {:?}", acc, e), + Ok(()) => debug::info!("[{:?}] Submitted price of {} cents", acc.id, price), + Err(e) => debug::error!("[{:?}] Failed to submit transaction: {:?}", acc.id, e), } } @@ -421,22 +410,21 @@ impl Module { let call = Call::submit_price_unsigned(block_number, price); // Now let's create a transaction out of this call and submit it to the pool. - // Here we showcase multiple ways to send a transaction: + // Here we showcase two ways to send a transaction: // 1. An unsigned transaction / unsigned payload (raw) // 2. An unsigned transaction with a signed payload - // 3. A signed transaction // // By default unsigned transactions are disallowed, so we need to whitelist this case // by writing `UnsignedValidator`. Note that it's EXTREMELY important to carefuly // implement unsigned validation logic, as any mistakes can lead to opening DoS or spam // attack vectors. See validation logic docs for more details. // - // Method 1 - let _result_raw: Result<(), String> = new::Signer::::send_raw_unsigned_transaction(call) + // Method 1: Unsigned transaction / Unsigned payload + let _result_raw: Result<(), String> = Signer::::send_raw_unsigned_transaction(call) .map_err(|()| "Unable to submit unsigned transaction.".into()); - // Method 2 - let _result_signed_payload = new::Signer::::all_accounts().send_unsigned_transaction( + // Method 2: Unsigned transction / signed payload + let _result_signed_payload = Signer::::all_accounts().send_unsigned_transaction( |account| PricePayload { price, block_number, @@ -447,10 +435,6 @@ impl Module { } ); - // Method 3 - let _result_signed_transaction = new::Signer::::all_accounts().send_signed_transaction( - |_account| Call::submit_price(price) - ); Ok(()) // T::SubmitUnsignedTransaction::submit_unsigned(call) @@ -553,57 +537,57 @@ impl Module { } } - fn validate_transaction_parameters( - block_number: &T::BlockNumber, - new_price: &u32 - ) -> TransactionValidity { - // Now let's check if the transaction has any chance to succeed. - let next_unsigned_at = >::get(); - if &next_unsigned_at > block_number { - return InvalidTransaction::Stale.into(); - } - // Let's make sure to reject transactions from the future. - let current_block = >::block_number(); - if ¤t_block < block_number { - return InvalidTransaction::Future.into(); - } - - // We prioritize transactions that are more far away from current average. - // - // Note this doesn't make much sense when building an actual oracle, but this example - // is here mostly to show off offchain workers capabilities, not about building an - // oracle. - let avg_price = Self::average_price() - .map(|price| if &price > new_price { price - new_price } else { new_price - price }) - .unwrap_or(0); - - Ok(ValidTransaction { - // We set base priority to 2**20 to make sure it's included before any other - // transactions in the pool. Next we tweak the priority depending on how much - // it differs from the current average. (the more it differs the more priority it - // has). - priority: (1 << 20) + avg_price as u64, - // This transaction does not require anything else to go before into the pool. - // In theory we could require `previous_unsigned_at` transaction to go first, - // but it's not necessary in our case. - requires: vec![], - // We set the `provides` tag to be the same as `next_unsigned_at`. This makes - // sure only one transaction produced after `next_unsigned_at` will ever - // get to the transaction pool and will end up in the block. - // We can still have multiple transactions compete for the same "spot", - // and the one with higher priority will replace other one in the pool. - provides: vec![codec::Encode::encode(&(KEY_TYPE.0, next_unsigned_at))], - // The transaction is only valid for next 5 blocks. After that it's - // going to be revalidated by the pool. - longevity: 5, - // It's fine to propagate that transaction to other peers, which means it can be - // created even by nodes that don't produce blocks. - // Note that sometimes it's better to keep it for yourself (if you are the block - // producer), since for instance in some schemes others may copy your solution and - // claim a reward. - propagate: true, - }) - } + fn validate_transaction_parameters( + block_number: &T::BlockNumber, + new_price: &u32 + ) -> TransactionValidity { + // Now let's check if the transaction has any chance to succeed. + let next_unsigned_at = >::get(); + if &next_unsigned_at > block_number { + return InvalidTransaction::Stale.into(); + } + // Let's make sure to reject transactions from the future. + let current_block = >::block_number(); + if ¤t_block < block_number { + return InvalidTransaction::Future.into(); + } + + // We prioritize transactions that are more far away from current average. + // + // Note this doesn't make much sense when building an actual oracle, but this example + // is here mostly to show off offchain workers capabilities, not about building an + // oracle. + let avg_price = Self::average_price() + .map(|price| if &price > new_price { price - new_price } else { new_price - price }) + .unwrap_or(0); + + Ok(ValidTransaction { + // We set base priority to 2**20 to make sure it's included before any other + // transactions in the pool. Next we tweak the priority depending on how much + // it differs from the current average. (the more it differs the more priority it + // has). + priority: (1 << 20) + avg_price as u64, + // This transaction does not require anything else to go before into the pool. + // In theory we could require `previous_unsigned_at` transaction to go first, + // but it's not necessary in our case. + requires: vec![], + // We set the `provides` tag to be the same as `next_unsigned_at`. This makes + // sure only one transaction produced after `next_unsigned_at` will ever + // get to the transaction pool and will end up in the block. + // We can still have multiple transactions compete for the same "spot", + // and the one with higher priority will replace other one in the pool. + provides: vec![codec::Encode::encode(&(KEY_TYPE.0, next_unsigned_at))], + // The transaction is only valid for next 5 blocks. After that it's + // going to be revalidated by the pool. + longevity: 5, + // It's fine to propagate that transaction to other peers, which means it can be + // created even by nodes that don't produce blocks. + // Note that sometimes it's better to keep it for yourself (if you are the block + // producer), since for instance in some schemes others may copy your solution and + // claim a reward. + propagate: true, + }) + } } #[allow(deprecated)] // ValidateUnsigned @@ -616,21 +600,19 @@ impl frame_support::unsigned::ValidateUnsigned for Module { /// here we make sure that some particular calls (the ones produced by offchain worker) /// are being whitelisted and marked as valid. fn validate_unsigned(call: &Self::Call) -> TransactionValidity { - use new::SignedPayload; - // Firstly let's check that we call the right function. if let Call::submit_price_unsigned_with_signed_payload( ref payload, ref signature ) = call { - let signature_valid = SignedPayload::::verify::(payload, signature.clone()); + let signature_valid = SignedPayload::::verify::(payload, signature.clone()); if !signature_valid { return InvalidTransaction::BadProof.into(); } - Self::validate_transaction_parameters(&payload.block_number, &payload.price) - } else if let Call::submit_price_unsigned(block_number, new_price) = call { - Self::validate_transaction_parameters(block_number, new_price) - } else { - InvalidTransaction::Call.into() - } - } + Self::validate_transaction_parameters(&payload.block_number, &payload.price) + } else if let Call::submit_price_unsigned(block_number, new_price) = call { + Self::validate_transaction_parameters(block_number, new_price) + } else { + InvalidTransaction::Call.into() + } + } } From 6e14b44a959105172d5361e2f6b02eaab8981c1d Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Sat, 7 Mar 2020 22:57:56 +0100 Subject: [PATCH 038/108] Fix im-online pallet --- frame/im-online/src/lib.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 6f70baeffac1b..75083e7231579 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -94,7 +94,12 @@ use frame_support::{ traits::Get, }; use frame_system::{self as system, ensure_none}; -use frame_system::offchain::new::{self, SendRawUnsignedTransaction}; +use frame_system::offchain::{ + AppCrypto, + Signer, + SendRawUnsignedTransaction, + SendTransactionTypes, +}; pub mod sr25519 { mod app_sr25519 { @@ -217,12 +222,12 @@ pub struct Heartbeat pub authority_index: AuthIndex, } -pub trait Trait: new::SendTransactionTypes> + pallet_session::historical::Trait { +pub trait Trait: SendTransactionTypes> + pallet_session::historical::Trait { /// The identifier type for an authority. type AuthorityId: Member + Parameter + RuntimeAppPublic + Default + Ord; - /// TODO when we remove `AppCrypto` it should just be the same as `AuthorityId` - type AuthorityId2: new::AppCrypto; + /// TODO How can this be used in place of AuthorityId + type OffchainAuthorityId: AppCrypto; /// The overarching event type. type Event: From> + Into<::Event>; @@ -480,7 +485,7 @@ impl Module { call, ); - new::Signer:: + Signer:: ::send_raw_unsigned_transaction(call) .map_err(|_| OffchainErr::SubmitTransaction)?; From 2c75fb58fdee40e1280cbbd2ffa1d84029c522ef Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Fri, 13 Mar 2020 21:29:41 +0100 Subject: [PATCH 039/108] Quick fix: rename AuthorityId2 --- bin/node/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 8d810e7a83b04..c4bcdbe1eb539 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -539,7 +539,7 @@ impl frame_system::offchain::AppCrypto<::Signer, Si impl pallet_im_online::Trait for Runtime { type AuthorityId = ImOnlineId; - type AuthorityId2 = ImOnlineAuthId; + type OffchainAuthorityId = ImOnlineAuthId; type Event = Event; type SessionDuration = SessionDuration; type ReportUnresponsiveness = Offences; From 1f54eae844683856822d00078bdfad38b87ce7ba Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Mon, 16 Mar 2020 11:48:37 +0100 Subject: [PATCH 040/108] Fix offchain example tests --- frame/example-offchain-worker/src/tests.rs | 66 +++++++++++++++------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index 9b6a567a17840..95cea78b86199 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -16,7 +16,7 @@ use crate::*; -use codec::Decode; +use codec::{Encode, Decode}; use frame_support::{ assert_ok, impl_outer_origin, parameter_types, weights::{GetDispatchInfo, Weight}, @@ -24,13 +24,20 @@ use frame_support::{ use sp_core::{ H256, offchain::{OffchainExt, TransactionPoolExt, testing}, + sr25519::Signature, testing::KeyStore, traits::KeystoreExt, }; use sp_runtime::{ Perbill, RuntimeAppPublic, testing::{Header, TestXt}, - traits::{BlakeTwo256, IdentityLookup, Extrinsic as ExtrinsicsT}, + traits::{ + BlakeTwo256, + IdentityLookup, + Extrinsic as ExtrinsicsT, + IdentifyAccount, + Verify + }, }; impl_outer_origin! { @@ -40,7 +47,7 @@ impl_outer_origin! { // For testing the module, we construct most of a mock runtime. This means // first constructing a configuration type (`Test`) which `impl`s each of the // configuration traits of modules we want to use. -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq, Encode, Decode)] pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; @@ -71,22 +78,37 @@ impl frame_system::Trait for Test { } type Extrinsic = TestXt, ()>; -type SubmitTransaction = frame_system::offchain::TransactionSubmitter< - crypto::Public, - Test, - Extrinsic ->; - -impl frame_system::offchain::CreateTransaction for Test { - type Public = sp_core::sr25519::Public; - type Signature = sp_core::sr25519::Signature; - - fn create_transaction>( - call: ::Call, - _public: Self::Public, - _account: ::AccountId, - nonce: ::Index, - ) -> Option<(::Call, ::SignaturePayload)> { +type AccountId = <::Signer as IdentifyAccount>::AccountId; + +impl frame_system::offchain::SigningTypes for Test { + type Public = ::Signer; + type Signature = Signature; +} + +impl frame_system::offchain::SendTransactionTypes for Test where + Call: From, +{ + + type OverarchingCall = Call; + type Extrinsic = Extrinsic; +} + +pub struct TestAuthId; +impl frame_system::offchain::AppCrypto<::Signer, Signature> for TestAuthId { + type RuntimeAppPublic = crypto::Public; + type GenericSignature = sp_core::sr25519::Signature; + type GenericPublic = sp_core::sr25519::Public; +} + +impl frame_system::offchain::CreateSignedTransaction for Test where + Call: From, +{ + fn create_transaction>( + call: Call, + _public: ::Signer, + _account: AccountId, + nonce: u64, + ) -> Option<(Call, ::SignaturePayload)> { Some((call, (nonce, ()))) } } @@ -98,9 +120,8 @@ parameter_types! { impl Trait for Test { type Event = (); + type AuthorityId = TestAuthId; type Call = Call; - type SubmitSignedTransaction = SubmitTransaction; - type SubmitUnsignedTransaction = SubmitTransaction; type GracePeriod = GracePeriod; type UnsignedInterval = UnsignedInterval; } @@ -172,9 +193,12 @@ fn should_submit_signed_transaction_on_chain() { fn should_submit_unsigned_transaction_on_chain() { let (offchain, offchain_state) = testing::TestOffchainExt::new(); let (pool, pool_state) = testing::TestTransactionPoolExt::new(); + let keystore = KeyStore::new(); + let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainExt::new(offchain)); t.register_extension(TransactionPoolExt::new(pool)); + t.register_extension(KeystoreExt(keystore)); price_oracle_response(&mut offchain_state.write()); From 887e0e96dca2ff3a682d9bd2decfd569cfef6f4b Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Mon, 16 Mar 2020 11:50:49 +0100 Subject: [PATCH 041/108] Add a comment on why keystore is required in unsigned transaction test --- frame/example-offchain-worker/src/tests.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index 95cea78b86199..b56b430e702f4 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -193,6 +193,10 @@ fn should_submit_signed_transaction_on_chain() { fn should_submit_unsigned_transaction_on_chain() { let (offchain, offchain_state) = testing::TestOffchainExt::new(); let (pool, pool_state) = testing::TestTransactionPoolExt::new(); + + // Keystore is required here to be used + // if the signed payload / unsigned transaction + // method is used let keystore = KeyStore::new(); let mut t = sp_io::TestExternalities::default(); From 34afbf2331b3f198ab24ccadca11e8e09e38d5a1 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 18 Mar 2020 11:24:19 +0100 Subject: [PATCH 042/108] Use UintAuthorityId instead of u64 --- frame/example-offchain-worker/src/tests.rs | 4 +- frame/im-online/src/mock.rs | 53 ++++++++++++++++------ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index b56b430e702f4..f1445aee7141e 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -34,7 +34,7 @@ use sp_runtime::{ traits::{ BlakeTwo256, IdentityLookup, - Extrinsic as ExtrinsicsT, + Extrinsic as ExtrinsicT, IdentifyAccount, Verify }, @@ -108,7 +108,7 @@ impl frame_system::offchain::CreateSignedTransaction for T _public: ::Signer, _account: AccountId, nonce: u64, - ) -> Option<(Call, ::SignaturePayload)> { + ) -> Option<(Call, ::SignaturePayload)> { Some((call, (nonce, ()))) } } diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 78b6409d543eb..128d656f6c9a3 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -24,8 +24,8 @@ use crate::{Module, Trait}; use sp_runtime::Perbill; use sp_staking::{SessionIndex, offence::{ReportOffence, OffenceError}}; use sp_runtime::testing::{Header, UintAuthorityId, TestXt}; -use sp_runtime::traits::{IdentityLookup, BlakeTwo256, ConvertInto}; -use sp_core::H256; +use sp_runtime::traits::{IdentityLookup, BlakeTwo256, ConvertInto, Extrinsic as ExtrinsicT, Verify}; +use sp_core::{H256, sr25519::{Public, Signature}}; use frame_support::{impl_outer_origin, impl_outer_dispatch, parameter_types, weights::Weight}; use frame_system as system; @@ -44,16 +44,16 @@ thread_local! { } pub struct TestSessionManager; -impl pallet_session::SessionManager for TestSessionManager { - fn new_session(_new_index: SessionIndex) -> Option> { +impl pallet_session::SessionManager for TestSessionManager { + fn new_session(_new_index: SessionIndex) -> Option> { VALIDATORS.with(|l| l.borrow_mut().take()) } fn end_session(_: SessionIndex) {} fn start_session(_: SessionIndex) {} } -impl pallet_session::historical::SessionManager for TestSessionManager { - fn new_session(_new_index: SessionIndex) -> Option> { +impl pallet_session::historical::SessionManager for TestSessionManager { + fn new_session(_new_index: SessionIndex) -> Option> { VALIDATORS.with(|l| l .borrow_mut() .take() @@ -68,8 +68,7 @@ impl pallet_session::historical::SessionManager for TestSessionManager /// An extrinsic type used for tests. pub type Extrinsic = TestXt; -type SubmitTransaction = frame_system::offchain::TransactionSubmitter<(), Call, Extrinsic>; -type IdentificationTuple = (u64, u64); +type IdentificationTuple = (UintAuthorityId, UintAuthorityId); type Offence = crate::UnresponsivenessOffence; thread_local! { @@ -78,8 +77,8 @@ thread_local! { /// A mock offence report handler. pub struct OffenceHandler; -impl ReportOffence for OffenceHandler { - fn report_offence(reporters: Vec, offence: Offence) -> Result<(), OffenceError> { +impl ReportOffence for OffenceHandler { + fn report_offence(reporters: Vec, offence: Offence) -> Result<(), OffenceError> { OFFENCES.with(|l| l.borrow_mut().push((reporters, offence))); Ok(()) } @@ -108,7 +107,7 @@ impl frame_system::Trait for Runtime { type Call = Call; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = UintAuthorityId; type Lookup = IdentityLookup; type Header = Header; type Event = (); @@ -136,7 +135,7 @@ impl pallet_session::Trait for Runtime { type ShouldEndSession = pallet_session::PeriodicSessions; type SessionManager = pallet_session::historical::NoteHistoricalRoot; type SessionHandler = (ImOnline, ); - type ValidatorId = u64; + type ValidatorId = UintAuthorityId; type ValidatorIdOf = ConvertInto; type Keys = UintAuthorityId; type Event = (); @@ -144,7 +143,7 @@ impl pallet_session::Trait for Runtime { } impl pallet_session::historical::Trait for Runtime { - type FullIdentification = u64; + type FullIdentification = UintAuthorityId; type FullIdentificationOf = ConvertInto; } @@ -162,12 +161,36 @@ impl pallet_authorship::Trait for Runtime { impl Trait for Runtime { type AuthorityId = UintAuthorityId; type Event = (); - type Call = Call; - type SubmitTransaction = SubmitTransaction; type ReportUnresponsiveness = OffenceHandler; type SessionDuration = Period; } +impl frame_system::offchain::SendTransactionTypes for Runtime where + Call: From, +{ + + type OverarchingCall = Call; + type Extrinsic = Extrinsic; +} + +impl frame_system::offchain::SigningTypes for Runtime { + type Public = ::Signer; + type Signature = Signature; +} + +impl frame_system::offchain::CreateSignedTransaction for Runtime where + Call: From, +{ + fn create_transaction>( + call: Call, + _public: ::Signer, + _account: u64, + nonce: u64, + ) -> Option<(Call, ::SignaturePayload)> { + Some((call, (nonce, ()))) + } +} + /// Im Online module. pub type ImOnline = Module; pub type System = frame_system::Module; From d04bd9a797b40eb04c91d16e96b632a971e96617 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 18 Mar 2020 11:24:56 +0100 Subject: [PATCH 043/108] WIP --- frame/im-online/src/mock.rs | 12 ++++++++++-- primitives/runtime/src/testing.rs | 17 +++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 128d656f6c9a3..98df5910cb43c 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -20,7 +20,7 @@ use std::cell::RefCell; -use crate::{Module, Trait}; +use crate::{Module, Trait, sr25519}; use sp_runtime::Perbill; use sp_staking::{SessionIndex, offence::{ReportOffence, OffenceError}}; use sp_runtime::testing::{Header, UintAuthorityId, TestXt}; @@ -43,6 +43,13 @@ thread_local! { pub static VALIDATORS: RefCell>> = RefCell::new(Some(vec![1, 2, 3])); } +pub struct ImOnlineAuthId; +impl frame_system::offchain::AppCrypto<::Signer, Signature> for ImOnlineAuthId { + type RuntimeAppPublic = sr25519::AuthorityId; + type GenericSignature = Signature; + type GenericPublic = Public; +} + pub struct TestSessionManager; impl pallet_session::SessionManager for TestSessionManager { fn new_session(_new_index: SessionIndex) -> Option> { @@ -160,6 +167,7 @@ impl pallet_authorship::Trait for Runtime { impl Trait for Runtime { type AuthorityId = UintAuthorityId; + type OffchainAuthorityId = ImOnlineAuthId; type Event = (); type ReportUnresponsiveness = OffenceHandler; type SessionDuration = Period; @@ -174,7 +182,7 @@ impl frame_system::offchain::SendTransactionTypes for Runt } impl frame_system::offchain::SigningTypes for Runtime { - type Public = ::Signer; + type Public = Public; type Signature = Signature; } diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index 2439070054211..509ffae345fe7 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -17,11 +17,11 @@ //! Testing utilities. use serde::{Serialize, Serializer, Deserialize, de::Error as DeError, Deserializer}; -use std::{fmt::Debug, ops::Deref, fmt, cell::RefCell}; +use std::{fmt::{self, Debug}, ops::Deref, cell::RefCell}; use crate::codec::{Codec, Encode, Decode}; use crate::traits::{ self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, - SignedExtension, Dispatchable, + SignedExtension, Dispatchable, IdentifyAccount }; use crate::traits::ValidateUnsigned; use crate::{generic, KeyTypeId, ApplyExtrinsicResult}; @@ -33,6 +33,19 @@ use crate::transaction_validity::{TransactionValidity, TransactionValidityError} #[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug, Hash, Serialize, Deserialize, PartialOrd, Ord)] pub struct UintAuthorityId(pub u64); +impl fmt::Display for UintAuthorityId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl IdentifyAccount for UintAuthorityId { + type AccountId = Self; + fn into_account(self) -> Self::AccountId { + self + } +} + impl From for UintAuthorityId { fn from(id: u64) -> Self { UintAuthorityId(id) From 824940a0be7f50d8d43c6ad6f9505dc54f2adfa6 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 18 Mar 2020 17:23:21 +0100 Subject: [PATCH 044/108] Remove IdentifyAccount from UintAuthorityId --- primitives/runtime/src/testing.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index 509ffae345fe7..5879894bbe17c 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -21,7 +21,7 @@ use std::{fmt::{self, Debug}, ops::Deref, cell::RefCell}; use crate::codec::{Codec, Encode, Decode}; use crate::traits::{ self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, - SignedExtension, Dispatchable, IdentifyAccount + SignedExtension, Dispatchable }; use crate::traits::ValidateUnsigned; use crate::{generic, KeyTypeId, ApplyExtrinsicResult}; @@ -39,13 +39,6 @@ impl fmt::Display for UintAuthorityId { } } -impl IdentifyAccount for UintAuthorityId { - type AccountId = Self; - fn into_account(self) -> Self::AccountId { - self - } -} - impl From for UintAuthorityId { fn from(id: u64) -> Self { UintAuthorityId(id) From 259cb981dfb9c2f637f633729a0fb8fd16d85b38 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 18 Mar 2020 17:24:12 +0100 Subject: [PATCH 045/108] Implement PublicWrapper type --- frame/im-online/src/mock.rs | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 98df5910cb43c..b305b54cd6ffe 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -20,11 +20,12 @@ use std::cell::RefCell; +use codec::{Encode, Decode}; use crate::{Module, Trait, sr25519}; use sp_runtime::Perbill; use sp_staking::{SessionIndex, offence::{ReportOffence, OffenceError}}; use sp_runtime::testing::{Header, UintAuthorityId, TestXt}; -use sp_runtime::traits::{IdentityLookup, BlakeTwo256, ConvertInto, Extrinsic as ExtrinsicT, Verify}; +use sp_runtime::traits::{IdentityLookup, IdentifyAccount, BlakeTwo256, ConvertInto, Extrinsic as ExtrinsicT}; use sp_core::{H256, sr25519::{Public, Signature}}; use frame_support::{impl_outer_origin, impl_outer_dispatch, parameter_types, weights::Weight}; @@ -44,7 +45,7 @@ thread_local! { } pub struct ImOnlineAuthId; -impl frame_system::offchain::AppCrypto<::Signer, Signature> for ImOnlineAuthId { +impl frame_system::offchain::AppCrypto for ImOnlineAuthId { type RuntimeAppPublic = sr25519::AuthorityId; type GenericSignature = Signature; type GenericPublic = Public; @@ -96,6 +97,31 @@ pub fn new_test_ext() -> sp_io::TestExternalities { t.into() } +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode)] +pub struct PublicWrapper(Public); + +impl IdentifyAccount for PublicWrapper { + type AccountId = UintAuthorityId; + fn into_account(self) -> Self::AccountId { + let bytes = self.0.to_vec(); + let mut truncated = [0u8; 8]; + truncated.copy_from_slice(bytes.as_slice()); + UintAuthorityId(u64::from_be_bytes(truncated)) + } +} + +impl From for PublicWrapper { + fn from(public: Public) -> Self { + Self(public) + } +} + +impl Into for PublicWrapper { + fn into(self) -> Public { + self.0 + } +} + #[derive(Clone, PartialEq, Eq, Debug)] pub struct Runtime; @@ -182,7 +208,7 @@ impl frame_system::offchain::SendTransactionTypes for Runt } impl frame_system::offchain::SigningTypes for Runtime { - type Public = Public; + type Public = PublicWrapper; type Signature = Signature; } @@ -191,8 +217,8 @@ impl frame_system::offchain::CreateSignedTransaction for R { fn create_transaction>( call: Call, - _public: ::Signer, - _account: u64, + _public: PublicWrapper, + _account: UintAuthorityId, nonce: u64, ) -> Option<(Call, ::SignaturePayload)> { Some((call, (nonce, ()))) From ea0ffaedce0c52f26e0143cb98f6f129ee35bacc Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 18 Mar 2020 17:24:34 +0100 Subject: [PATCH 046/108] Fix im-online tests --- frame/im-online/src/mock.rs | 10 ++- frame/im-online/src/tests.rs | 124 +++++++++++++++++++++++++++-------- 2 files changed, 105 insertions(+), 29 deletions(-) diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index b305b54cd6ffe..308182b83958a 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -41,7 +41,11 @@ impl_outer_dispatch! { } thread_local! { - pub static VALIDATORS: RefCell>> = RefCell::new(Some(vec![1, 2, 3])); + pub static VALIDATORS: RefCell>> = RefCell::new(Some(vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3) + ])); } pub struct ImOnlineAuthId; @@ -66,7 +70,7 @@ impl pallet_session::historical::SessionManager; thread_local! { - pub static OFFENCES: RefCell, Offence)>> = RefCell::new(vec![]); + pub static OFFENCES: RefCell, Offence)>> = RefCell::new(vec![]); } /// A mock offence report handler. diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index b43adca0fd485..591ab93eafb6c 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -63,7 +63,14 @@ fn should_report_offline_validators() { // buffer new validators Session::rotate_session(); // enact the change and buffer another one - let validators = vec![1, 2, 3, 4, 5, 6]; + let validators = vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3), + UintAuthorityId(4), + UintAuthorityId(5), + UintAuthorityId(6) + ]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); Session::rotate_session(); @@ -78,9 +85,9 @@ fn should_report_offline_validators() { session_index: 2, validator_set_count: 3, offenders: vec![ - (1, 1), - (2, 2), - (3, 3), + (UintAuthorityId(1), UintAuthorityId(1)), + (UintAuthorityId(2), UintAuthorityId(2)), + (UintAuthorityId(3), UintAuthorityId(3)), ], }) ]); @@ -98,8 +105,8 @@ fn should_report_offline_validators() { session_index: 3, validator_set_count: 6, offenders: vec![ - (5, 5), - (6, 6), + (UintAuthorityId(5), UintAuthorityId(5)), + (UintAuthorityId(6), UintAuthorityId(6)), ], }) ]); @@ -139,13 +146,25 @@ fn should_mark_online_validator_when_heartbeat_is_received() { new_test_ext().execute_with(|| { advance_session(); // given - VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); - assert_eq!(Session::validators(), Vec::::new()); + let validators = vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3), + UintAuthorityId(4), + UintAuthorityId(5), + UintAuthorityId(6) + ]; + VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); assert_eq!(Session::current_index(), 2); - assert_eq!(Session::validators(), vec![1, 2, 3]); + assert_eq!(Session::validators(), vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3) + ]); assert!(!ImOnline::is_online(0)); assert!(!ImOnline::is_online(1)); @@ -174,13 +193,25 @@ fn late_heartbeat_should_fail() { new_test_ext().execute_with(|| { advance_session(); // given - VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 4, 4, 5, 6])); - assert_eq!(Session::validators(), Vec::::new()); + let validators = vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3), + UintAuthorityId(4), + UintAuthorityId(5), + UintAuthorityId(6) + ]; + VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); assert_eq!(Session::current_index(), 2); - assert_eq!(Session::validators(), vec![1, 2, 3]); + assert_eq!(Session::validators(), vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3) + ]); // when assert_noop!(heartbeat(1, 3, 0, 1.into()), "Transaction is outdated"); @@ -206,7 +237,15 @@ fn should_generate_heartbeats() { // buffer new validators Session::rotate_session(); // enact the change and buffer another one - VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); + let validators = vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3), + UintAuthorityId(4), + UintAuthorityId(5), + UintAuthorityId(6) + ]; + VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); Session::rotate_session(); // when @@ -238,14 +277,23 @@ fn should_cleanup_received_heartbeats_on_session_end() { new_test_ext().execute_with(|| { advance_session(); - VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3])); - assert_eq!(Session::validators(), Vec::::new()); + let validators = vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3), + ]; + VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); assert_eq!(Session::current_index(), 2); - assert_eq!(Session::validators(), vec![1, 2, 3]); + assert_eq!(Session::validators(), vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3) + ]); // send an heartbeat from authority id 0 at session 2 let _ = heartbeat(1, 2, 0, 1.into()).unwrap(); @@ -269,21 +317,33 @@ fn should_mark_online_validator_when_block_is_authored() { new_test_ext().execute_with(|| { advance_session(); // given - VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); - assert_eq!(Session::validators(), Vec::::new()); + let validators = vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3), + UintAuthorityId(4), + UintAuthorityId(5), + UintAuthorityId(6) + ]; + VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); assert_eq!(Session::current_index(), 2); - assert_eq!(Session::validators(), vec![1, 2, 3]); + assert_eq!(Session::validators(), vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3) + ]); for i in 0..3 { assert!(!ImOnline::is_online(i)); } // when - ImOnline::note_author(1); - ImOnline::note_uncle(2, 0); + ImOnline::note_author(UintAuthorityId(1)); + ImOnline::note_uncle(UintAuthorityId(2), 0); // then assert!(ImOnline::is_online(0)); @@ -305,14 +365,26 @@ fn should_not_send_a_report_if_already_online() { ext.execute_with(|| { advance_session(); // given - VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); - assert_eq!(Session::validators(), Vec::::new()); + let validators = vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3), + UintAuthorityId(4), + UintAuthorityId(5), + UintAuthorityId(6) + ]; + VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); assert_eq!(Session::current_index(), 2); - assert_eq!(Session::validators(), vec![1, 2, 3]); - ImOnline::note_author(2); - ImOnline::note_uncle(3, 0); + assert_eq!(Session::validators(), vec![ + UintAuthorityId(1), + UintAuthorityId(2), + UintAuthorityId(3) + ]); + ImOnline::note_author(UintAuthorityId(2)); + ImOnline::note_uncle(UintAuthorityId(3), 0); // when UintAuthorityId::set_all_keys(vec![0]); // all authorities use pallet_session key 0 From 5fd154d491a837b0abc5751432ecb40559fbf099 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 18 Mar 2020 21:54:47 +0100 Subject: [PATCH 047/108] Fix runtime test --- bin/node/runtime/src/lib.rs | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index f20a189e6f55a..551cff3160209 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -933,29 +933,15 @@ impl_runtime_apis! { #[cfg(test)] mod tests { use super::*; - use frame_system::offchain::{SignAndSubmitTransaction, SubmitSignedTransaction}; + use frame_system::offchain::CreateSignedTransaction; #[test] fn validate_transaction_submitter_bounds() { fn is_submit_signed_transaction() where - T: SubmitSignedTransaction< - Runtime, - Call, - >, + T: CreateSignedTransaction, {} - fn is_sign_and_submit_transaction() where - T: SignAndSubmitTransaction< - Runtime, - Call, - Extrinsic=UncheckedExtrinsic, - CreateTransaction=Runtime, - Signer=ImOnlineId, - >, - {} - - is_submit_signed_transaction::(); - is_sign_and_submit_transaction::(); + is_submit_signed_transaction::(); } #[test] From f629bab17ca7388df150dd77a2ec116ba98b9ba1 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 18 Mar 2020 22:01:20 +0100 Subject: [PATCH 048/108] Bump spec version --- bin/node/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 551cff3160209..147c7ac37a312 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -81,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 237, + spec_version: 238, impl_version: 0, apis: RUNTIME_API_VERSIONS, }; From 69518877232427ca2ca26bf4a09defb10fa4d3af Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 18 Mar 2020 23:20:18 +0100 Subject: [PATCH 049/108] Fix executor tests --- bin/node/executor/tests/submit_transaction.rs | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 1a92aeca6ba77..18b3fcf14e664 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -15,7 +15,7 @@ // along with Substrate. If not, see . use node_runtime::{ - Call, Executive, Indices, Runtime, SubmitTransaction, UncheckedExtrinsic, + Executive, Indices, Runtime, UncheckedExtrinsic, ImOnlineAuthId }; use sp_application_crypto::AppKey; use sp_core::testing::KeyStore; @@ -24,7 +24,11 @@ use sp_core::offchain::{ TransactionPoolExt, testing::TestTransactionPoolExt, }; -use frame_system::offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction}; +use frame_system::offchain::{ + SendRawUnsignedTransaction, + SendSignedTransaction, + Signer, +}; use pallet_im_online::sr25519::AuthorityPair as Key; use codec::Decode; @@ -47,8 +51,7 @@ fn should_submit_unsigned_transaction() { }; let call = pallet_im_online::Call::heartbeat(heartbeat_data, signature); - > - ::submit_unsigned(call) + Signer::::send_raw_unsigned_transaction(call) .unwrap(); assert_eq!(state.read().transactions.len(), 1) @@ -70,17 +73,10 @@ fn should_submit_signed_transaction() { t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let keys = > - ::find_all_local_keys(); - assert_eq!(keys.len(), 3, "Missing keys: {:?}", keys); - - let can_sign = > - ::can_sign(); - assert!(can_sign, "Since there are keys, `can_sign` should return true"); - - let call = pallet_balances::Call::transfer(Default::default(), Default::default()); - let results = - >::submit_signed(call); + let results = Signer::::all_accounts() + .send_signed_transaction(|_| { + pallet_balances::Call::transfer(Default::default(), Default::default()) + }); let len = results.len(); assert_eq!(len, 3); @@ -100,9 +96,10 @@ fn should_submit_signed_twice_from_the_same_account() { t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let call = pallet_balances::Call::transfer(Default::default(), Default::default()); - let results = - >::submit_signed(call); + let results = Signer::::all_accounts() + .send_signed_transaction(|_| { + pallet_balances::Call::transfer(Default::default(), Default::default()) + }); let len = results.len(); assert_eq!(len, 1); @@ -110,9 +107,10 @@ fn should_submit_signed_twice_from_the_same_account() { assert_eq!(state.read().transactions.len(), 1); // submit another one from the same account. The nonce should be incremented. - let call = pallet_balances::Call::transfer(Default::default(), Default::default()); - let results = - >::submit_signed(call); + let results = Signer::::all_accounts() + .send_signed_transaction(|_| { + pallet_balances::Call::transfer(Default::default(), Default::default()) + }); let len = results.len(); assert_eq!(len, 1); @@ -150,9 +148,10 @@ fn submitted_transaction_should_be_valid() { t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let call = pallet_balances::Call::transfer(Default::default(), Default::default()); - let results = - >::submit_signed(call); + let results = Signer::::all_accounts() + .send_signed_transaction(|_| { + pallet_balances::Call::transfer(Default::default(), Default::default()) + }); let len = results.len(); assert_eq!(len, 1); assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len); From b8702acec87075011d93a2bec6cb566a12011ebc Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Mon, 23 Mar 2020 22:11:40 +0100 Subject: [PATCH 050/108] Rename ImOnlineAuthId -> ImOnlineAuthorityId and formatting --- bin/node/executor/tests/submit_transaction.rs | 12 +++++----- bin/node/runtime/src/lib.rs | 7 +++--- frame/example-offchain-worker/src/lib.rs | 24 +++++++------------ frame/example-offchain-worker/src/tests.rs | 8 ++----- frame/im-online/src/lib.rs | 2 -- frame/im-online/src/mock.rs | 9 ++++--- frame/im-online/src/tests.rs | 22 ++++++++--------- primitives/runtime/src/testing.rs | 2 +- 8 files changed, 35 insertions(+), 51 deletions(-) diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 18b3fcf14e664..27fd9e4f3ad6e 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -15,7 +15,7 @@ // along with Substrate. If not, see . use node_runtime::{ - Executive, Indices, Runtime, UncheckedExtrinsic, ImOnlineAuthId + Executive, Indices, Runtime, UncheckedExtrinsic, ImOnlineAuthorityId, }; use sp_application_crypto::AppKey; use sp_core::testing::KeyStore; @@ -51,7 +51,7 @@ fn should_submit_unsigned_transaction() { }; let call = pallet_im_online::Call::heartbeat(heartbeat_data, signature); - Signer::::send_raw_unsigned_transaction(call) + Signer::::send_raw_unsigned_transaction(call) .unwrap(); assert_eq!(state.read().transactions.len(), 1) @@ -73,7 +73,7 @@ fn should_submit_signed_transaction() { t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); @@ -96,7 +96,7 @@ fn should_submit_signed_twice_from_the_same_account() { t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); @@ -107,7 +107,7 @@ fn should_submit_signed_twice_from_the_same_account() { assert_eq!(state.read().transactions.len(), 1); // submit another one from the same account. The nonce should be incremented. - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); @@ -148,7 +148,7 @@ fn submitted_transaction_should_be_valid() { t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 147c7ac37a312..96d4d7aea427b 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -524,9 +524,8 @@ impl frame_system::offchain::SendTransactionTypes for Runtime where type OverarchingCall = Call; type Extrinsic = UncheckedExtrinsic; } - -pub struct ImOnlineAuthId; -impl frame_system::offchain::AppCrypto<::Signer, Signature> for ImOnlineAuthId { +pub struct ImOnlineAuthorityId; +impl frame_system::offchain::AppCrypto<::Signer, Signature> for ImOnlineAuthorityId { // TODO [ToDr] Get rid of this trait and instead // have `RuntimeAppPublic` be able to give you `GenericSignature/GenericPublic` // or see the proposal at `system/src/offchain.rs`. @@ -540,7 +539,7 @@ impl frame_system::offchain::AppCrypto<::Signer, Si impl pallet_im_online::Trait for Runtime { type AuthorityId = ImOnlineId; - type OffchainAuthorityId = ImOnlineAuthId; + type OffchainAuthorityId = ImOnlineAuthorityId; type Event = Event; type SessionDuration = SessionDuration; type ReportUnresponsiveness = Offences; diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 04bd79908c8fb..97bd8cd2314e5 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -45,13 +45,8 @@ use frame_system::{ ensure_signed, ensure_none, offchain::{ - AppCrypto, - CreateSignedTransaction, - SendUnsignedTransaction, - SendRawUnsignedTransaction, - SignedPayload, - SigningTypes, - Signer, + AppCrypto, CreateSignedTransaction, SendUnsignedTransaction, + SendRawUnsignedTransaction, SignedPayload, SigningTypes, Signer, } }; use frame_support::{ @@ -120,7 +115,7 @@ pub trait Trait: CreateSignedTransaction> { pub struct PricePayload { block_number: BlockNumber, price: u32, - public: Public + public: Public, } impl SignedPayload for PricePayload { @@ -213,7 +208,7 @@ decl_module! { pub fn submit_price_unsigned_with_signed_payload( origin, price_payload: PricePayload, - _signature: T::Signature + _signature: T::Signature, ) -> DispatchResult { // This ensures that the function can only be called via unsigned transaction. @@ -421,7 +416,7 @@ impl Module { // // Method 1: Unsigned transaction / Unsigned payload let _result_raw: Result<(), String> = Signer::::send_raw_unsigned_transaction(call) - .map_err(|()| "Unable to submit unsigned transaction.".into()); + .map_err(|()| "Unable to submit unsigned transaction.".into()); // Method 2: Unsigned transction / signed payload let _result_signed_payload = Signer::::all_accounts().send_unsigned_transaction( @@ -437,9 +432,6 @@ impl Module { Ok(()) - // T::SubmitUnsignedTransaction::submit_unsigned(call) - // .map_err(|()| "Unable to submit unsigned transaction.".into()) - } /// Fetch current price and return the result in cents. @@ -539,7 +531,7 @@ impl Module { fn validate_transaction_parameters( block_number: &T::BlockNumber, - new_price: &u32 + new_price: &u32, ) -> TransactionValidity { // Now let's check if the transaction has any chance to succeed. let next_unsigned_at = >::get(); @@ -562,7 +554,7 @@ impl Module { .unwrap_or(0); Ok(ValidTransaction { - // We set base priority to 2**20 to make sure it's included before any other + // We set base priority to 2**20 and hope it's included before any other // transactions in the pool. Next we tweak the priority depending on how much // it differs from the current average. (the more it differs the more priority it // has). @@ -576,7 +568,7 @@ impl Module { // get to the transaction pool and will end up in the block. // We can still have multiple transactions compete for the same "spot", // and the one with higher priority will replace other one in the pool. - provides: vec![codec::Encode::encode(&(KEY_TYPE.0, next_unsigned_at))], + provides: vec![Encode::encode(&(KEY_TYPE.0, next_unsigned_at))], // The transaction is only valid for next 5 blocks. After that it's // going to be revalidated by the pool. longevity: 5, diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index a32098e8fe115..7d742e40f2914 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -32,11 +32,8 @@ use sp_runtime::{ Perbill, RuntimeAppPublic, testing::{Header, TestXt}, traits::{ - BlakeTwo256, - IdentityLookup, - Extrinsic as ExtrinsicT, - IdentifyAccount, - Verify + BlakeTwo256, IdentityLookup, Extrinsic as ExtrinsicT, + IdentifyAccount, Verify }, }; @@ -88,7 +85,6 @@ impl frame_system::offchain::SigningTypes for Test { impl frame_system::offchain::SendTransactionTypes for Test where Call: From, { - type OverarchingCall = Call; type Extrinsic = Extrinsic; } diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index f537467126563..ed32af95fe1b5 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -534,8 +534,6 @@ impl Module { // All `ImOnline` public (+private) keys currently in the local keystore. let mut local_keys = T::AuthorityId::all(); - - local_keys.sort(); authorities.into_iter() diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index b521a0e9e9841..e15a6469344d4 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -44,12 +44,12 @@ thread_local! { pub static VALIDATORS: RefCell>> = RefCell::new(Some(vec![ UintAuthorityId(1), UintAuthorityId(2), - UintAuthorityId(3) + UintAuthorityId(3), ])); } -pub struct ImOnlineAuthId; -impl frame_system::offchain::AppCrypto for ImOnlineAuthId { +pub struct ImOnlineAuthorityId; +impl frame_system::offchain::AppCrypto for ImOnlineAuthorityId { type RuntimeAppPublic = sr25519::AuthorityId; type GenericSignature = Signature; type GenericPublic = Public; @@ -197,7 +197,7 @@ impl pallet_authorship::Trait for Runtime { impl Trait for Runtime { type AuthorityId = UintAuthorityId; - type OffchainAuthorityId = ImOnlineAuthId; + type OffchainAuthorityId = ImOnlineAuthorityId; type Event = (); type ReportUnresponsiveness = OffenceHandler; type SessionDuration = Period; @@ -206,7 +206,6 @@ impl Trait for Runtime { impl frame_system::offchain::SendTransactionTypes for Runtime where Call: From, { - type OverarchingCall = Call; type Extrinsic = Extrinsic; } diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 591ab93eafb6c..36eb99c9761bb 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -69,7 +69,7 @@ fn should_report_offline_validators() { UintAuthorityId(3), UintAuthorityId(4), UintAuthorityId(5), - UintAuthorityId(6) + UintAuthorityId(6), ]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); Session::rotate_session(); @@ -152,7 +152,7 @@ fn should_mark_online_validator_when_heartbeat_is_received() { UintAuthorityId(3), UintAuthorityId(4), UintAuthorityId(5), - UintAuthorityId(6) + UintAuthorityId(6), ]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); assert_eq!(Session::validators(), Vec::::new()); @@ -163,7 +163,7 @@ fn should_mark_online_validator_when_heartbeat_is_received() { assert_eq!(Session::validators(), vec![ UintAuthorityId(1), UintAuthorityId(2), - UintAuthorityId(3) + UintAuthorityId(3), ]); assert!(!ImOnline::is_online(0)); @@ -199,7 +199,7 @@ fn late_heartbeat_should_fail() { UintAuthorityId(3), UintAuthorityId(4), UintAuthorityId(5), - UintAuthorityId(6) + UintAuthorityId(6), ]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); assert_eq!(Session::validators(), Vec::::new()); @@ -210,7 +210,7 @@ fn late_heartbeat_should_fail() { assert_eq!(Session::validators(), vec![ UintAuthorityId(1), UintAuthorityId(2), - UintAuthorityId(3) + UintAuthorityId(3), ]); // when @@ -243,7 +243,7 @@ fn should_generate_heartbeats() { UintAuthorityId(3), UintAuthorityId(4), UintAuthorityId(5), - UintAuthorityId(6) + UintAuthorityId(6), ]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); Session::rotate_session(); @@ -292,7 +292,7 @@ fn should_cleanup_received_heartbeats_on_session_end() { assert_eq!(Session::validators(), vec![ UintAuthorityId(1), UintAuthorityId(2), - UintAuthorityId(3) + UintAuthorityId(3), ]); // send an heartbeat from authority id 0 at session 2 @@ -323,7 +323,7 @@ fn should_mark_online_validator_when_block_is_authored() { UintAuthorityId(3), UintAuthorityId(4), UintAuthorityId(5), - UintAuthorityId(6) + UintAuthorityId(6), ]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); assert_eq!(Session::validators(), Vec::::new()); @@ -334,7 +334,7 @@ fn should_mark_online_validator_when_block_is_authored() { assert_eq!(Session::validators(), vec![ UintAuthorityId(1), UintAuthorityId(2), - UintAuthorityId(3) + UintAuthorityId(3), ]); for i in 0..3 { @@ -371,7 +371,7 @@ fn should_not_send_a_report_if_already_online() { UintAuthorityId(3), UintAuthorityId(4), UintAuthorityId(5), - UintAuthorityId(6) + UintAuthorityId(6), ]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); assert_eq!(Session::validators(), Vec::::new()); @@ -381,7 +381,7 @@ fn should_not_send_a_report_if_already_online() { assert_eq!(Session::validators(), vec![ UintAuthorityId(1), UintAuthorityId(2), - UintAuthorityId(3) + UintAuthorityId(3), ]); ImOnline::note_author(UintAuthorityId(2)); ImOnline::note_uncle(UintAuthorityId(3), 0); diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index 5879894bbe17c..d6fc1e7a71a90 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -21,7 +21,7 @@ use std::{fmt::{self, Debug}, ops::Deref, cell::RefCell}; use crate::codec::{Codec, Encode, Decode}; use crate::traits::{ self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, - SignedExtension, Dispatchable + SignedExtension, Dispatchable, }; use crate::traits::ValidateUnsigned; use crate::{generic, KeyTypeId, ApplyExtrinsicResult}; From 670e26b0218082b39681ebd52d845c1938f08f15 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Mon, 23 Mar 2020 22:27:18 +0100 Subject: [PATCH 051/108] Fix merge --- bin/node/runtime/src/lib.rs | 1 - frame/im-online/src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e7773e54ecaaa..82e4ad2eb5d3c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -523,7 +523,6 @@ impl frame_system::offchain::CreateSignedTransaction for R impl frame_system::offchain::SendTransactionTypes for Runtime where Call: From, { - type OverarchingCall = Call; type Extrinsic = UncheckedExtrinsic; } diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index f34f6af36bc18..c699343dc7a2d 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -92,7 +92,7 @@ use sp_staking::{ }; use frame_support::{ decl_module, decl_event, decl_storage, Parameter, debug, decl_error, - traits::{Get, MigrateAccount}, + traits::Get, }; use frame_system::{self as system, ensure_none}; use frame_system::offchain::{ From f5939cd69480198f2ac695dd26da89a1cbf6f9d5 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 24 Mar 2020 12:13:12 +0100 Subject: [PATCH 052/108] Documentation --- frame/example-offchain-worker/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 84014d4159c3d..403f611216cbc 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -112,6 +112,8 @@ pub trait Trait: CreateSignedTransaction> { type UnsignedInterval: Get; } +/// Payload used by this example crate to hold price +/// data required to submit a transaction. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct PricePayload { block_number: BlockNumber, From 0ffd1b94331af507566574341a2a564ed96b6eff Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 24 Mar 2020 12:54:26 +0100 Subject: [PATCH 053/108] Revert u64 -> UintAuthorityId conversion --- frame/im-online/src/mock.rs | 36 +++++------ frame/im-online/src/tests.rs | 118 ++++++++--------------------------- 2 files changed, 44 insertions(+), 110 deletions(-) diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 4137e7287af43..e260bf4bad44e 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -41,10 +41,10 @@ impl_outer_dispatch! { } thread_local! { - pub static VALIDATORS: RefCell>> = RefCell::new(Some(vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), + pub static VALIDATORS: RefCell>> = RefCell::new(Some(vec![ + 1, + 2, + 3, ])); } @@ -56,16 +56,16 @@ impl frame_system::offchain::AppCrypto for ImOnlineAut } pub struct TestSessionManager; -impl pallet_session::SessionManager for TestSessionManager { - fn new_session(_new_index: SessionIndex) -> Option> { +impl pallet_session::SessionManager for TestSessionManager { + fn new_session(_new_index: SessionIndex) -> Option> { VALIDATORS.with(|l| l.borrow_mut().take()) } fn end_session(_: SessionIndex) {} fn start_session(_: SessionIndex) {} } -impl pallet_session::historical::SessionManager for TestSessionManager { - fn new_session(_new_index: SessionIndex) -> Option> { +impl pallet_session::historical::SessionManager for TestSessionManager { + fn new_session(_new_index: SessionIndex) -> Option> { VALIDATORS.with(|l| l .borrow_mut() .take() @@ -80,17 +80,17 @@ impl pallet_session::historical::SessionManager; -type IdentificationTuple = (UintAuthorityId, UintAuthorityId); +type IdentificationTuple = (u64, u64); type Offence = crate::UnresponsivenessOffence; thread_local! { - pub static OFFENCES: RefCell, Offence)>> = RefCell::new(vec![]); + pub static OFFENCES: RefCell, Offence)>> = RefCell::new(vec![]); } /// A mock offence report handler. pub struct OffenceHandler; -impl ReportOffence for OffenceHandler { - fn report_offence(reporters: Vec, offence: Offence) -> Result<(), OffenceError> { +impl ReportOffence for OffenceHandler { + fn report_offence(reporters: Vec, offence: Offence) -> Result<(), OffenceError> { OFFENCES.with(|l| l.borrow_mut().push((reporters, offence))); Ok(()) } @@ -105,12 +105,12 @@ pub fn new_test_ext() -> sp_io::TestExternalities { pub struct PublicWrapper(Public); impl IdentifyAccount for PublicWrapper { - type AccountId = UintAuthorityId; + type AccountId = u64; fn into_account(self) -> Self::AccountId { let bytes = self.0.to_vec(); let mut truncated = [0u8; 8]; truncated.copy_from_slice(bytes.as_slice()); - UintAuthorityId(u64::from_be_bytes(truncated)) + u64::from_be_bytes(truncated) } } @@ -144,7 +144,7 @@ impl frame_system::Trait for Runtime { type Call = Call; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = UintAuthorityId; + type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; type Event = (); @@ -172,7 +172,7 @@ impl pallet_session::Trait for Runtime { type ShouldEndSession = pallet_session::PeriodicSessions; type SessionManager = pallet_session::historical::NoteHistoricalRoot; type SessionHandler = (ImOnline, ); - type ValidatorId = UintAuthorityId; + type ValidatorId = u64; type ValidatorIdOf = ConvertInto; type Keys = UintAuthorityId; type Event = (); @@ -180,7 +180,7 @@ impl pallet_session::Trait for Runtime { } impl pallet_session::historical::Trait for Runtime { - type FullIdentification = UintAuthorityId; + type FullIdentification = u64; type FullIdentificationOf = ConvertInto; } @@ -221,7 +221,7 @@ impl frame_system::offchain::CreateSignedTransaction for R fn create_transaction>( call: Call, _public: PublicWrapper, - _account: UintAuthorityId, + _account: u64, nonce: u64, ) -> Option<(Call, ::SignaturePayload)> { Some((call, (nonce, ()))) diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 36eb99c9761bb..f408ba3c0ee5c 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -63,14 +63,7 @@ fn should_report_offline_validators() { // buffer new validators Session::rotate_session(); // enact the change and buffer another one - let validators = vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - UintAuthorityId(4), - UintAuthorityId(5), - UintAuthorityId(6), - ]; + let validators = vec![1, 2, 3, 4, 5, 6]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); Session::rotate_session(); @@ -85,9 +78,9 @@ fn should_report_offline_validators() { session_index: 2, validator_set_count: 3, offenders: vec![ - (UintAuthorityId(1), UintAuthorityId(1)), - (UintAuthorityId(2), UintAuthorityId(2)), - (UintAuthorityId(3), UintAuthorityId(3)), + (1, 1), + (2, 2), + (3, 3), ], }) ]); @@ -105,8 +98,8 @@ fn should_report_offline_validators() { session_index: 3, validator_set_count: 6, offenders: vec![ - (UintAuthorityId(5), UintAuthorityId(5)), - (UintAuthorityId(6), UintAuthorityId(6)), + (5, 5), + (6, 6), ], }) ]); @@ -146,25 +139,14 @@ fn should_mark_online_validator_when_heartbeat_is_received() { new_test_ext().execute_with(|| { advance_session(); // given - let validators = vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - UintAuthorityId(4), - UintAuthorityId(5), - UintAuthorityId(6), - ]; + let validators = vec![1, 2, 3, 4, 5, 6]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); - assert_eq!(Session::validators(), Vec::::new()); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); assert_eq!(Session::current_index(), 2); - assert_eq!(Session::validators(), vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - ]); + assert_eq!(Session::validators(), vec![1, 2, 3]); assert!(!ImOnline::is_online(0)); assert!(!ImOnline::is_online(1)); @@ -193,25 +175,14 @@ fn late_heartbeat_should_fail() { new_test_ext().execute_with(|| { advance_session(); // given - let validators = vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - UintAuthorityId(4), - UintAuthorityId(5), - UintAuthorityId(6), - ]; + let validators = vec![1, 2, 3, 4, 5, 6]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); - assert_eq!(Session::validators(), Vec::::new()); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); assert_eq!(Session::current_index(), 2); - assert_eq!(Session::validators(), vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - ]); + assert_eq!(Session::validators(), vec![1, 2, 3]); // when assert_noop!(heartbeat(1, 3, 0, 1.into()), "Transaction is outdated"); @@ -237,14 +208,7 @@ fn should_generate_heartbeats() { // buffer new validators Session::rotate_session(); // enact the change and buffer another one - let validators = vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - UintAuthorityId(4), - UintAuthorityId(5), - UintAuthorityId(6), - ]; + let validators = vec![1, 2, 3, 4, 5, 6]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); Session::rotate_session(); @@ -277,23 +241,15 @@ fn should_cleanup_received_heartbeats_on_session_end() { new_test_ext().execute_with(|| { advance_session(); - let validators = vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - ]; + let validators = vec![1, 2, 3]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); - assert_eq!(Session::validators(), Vec::::new()); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); assert_eq!(Session::current_index(), 2); - assert_eq!(Session::validators(), vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - ]); + assert_eq!(Session::validators(), vec![1, 2, 3]); // send an heartbeat from authority id 0 at session 2 let _ = heartbeat(1, 2, 0, 1.into()).unwrap(); @@ -317,33 +273,22 @@ fn should_mark_online_validator_when_block_is_authored() { new_test_ext().execute_with(|| { advance_session(); // given - let validators = vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - UintAuthorityId(4), - UintAuthorityId(5), - UintAuthorityId(6), - ]; + let validators = vec![1, 2, 3, 4, 5, 6]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); - assert_eq!(Session::validators(), Vec::::new()); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); assert_eq!(Session::current_index(), 2); - assert_eq!(Session::validators(), vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - ]); + assert_eq!(Session::validators(), vec![1, 2, 3]); for i in 0..3 { assert!(!ImOnline::is_online(i)); } // when - ImOnline::note_author(UintAuthorityId(1)); - ImOnline::note_uncle(UintAuthorityId(2), 0); + ImOnline::note_author(1); + ImOnline::note_uncle(2, 0); // then assert!(ImOnline::is_online(0)); @@ -365,26 +310,15 @@ fn should_not_send_a_report_if_already_online() { ext.execute_with(|| { advance_session(); // given - let validators = vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - UintAuthorityId(4), - UintAuthorityId(5), - UintAuthorityId(6), - ]; + let validators = vec![1, 2, 3, 4, 5, 6]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); - assert_eq!(Session::validators(), Vec::::new()); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); assert_eq!(Session::current_index(), 2); - assert_eq!(Session::validators(), vec![ - UintAuthorityId(1), - UintAuthorityId(2), - UintAuthorityId(3), - ]); - ImOnline::note_author(UintAuthorityId(2)); - ImOnline::note_uncle(UintAuthorityId(3), 0); + assert_eq!(Session::validators(), vec![1, 2, 3]); + ImOnline::note_author(2); + ImOnline::note_uncle(3, 0); // when UintAuthorityId::set_all_keys(vec![0]); // all authorities use pallet_session key 0 From 324d436781b3cc38a363ccc168f3a462d0090492 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 25 Mar 2020 11:54:06 +0100 Subject: [PATCH 054/108] Fix string errors --- frame/example-offchain-worker/src/lib.rs | 8 ++++---- primitives/application-crypto/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 403f611216cbc..4df9932d4a2dd 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -358,7 +358,7 @@ impl Module { } /// A helper function to fetch the price and send signed transaction. - fn fetch_price_and_send_signed() -> Result<(), String> { + fn fetch_price_and_send_signed() -> Result<(), &'static str> { use frame_system::offchain::SendSignedTransaction; // Make an external HTTP request to fetch the current price. // Note this call will block until response is received. @@ -388,7 +388,7 @@ impl Module { } /// A helper function to fetch the price and send unsigned transaction. - fn fetch_price_and_send_unsigned(block_number: T::BlockNumber) -> Result<(), String> { + fn fetch_price_and_send_unsigned(block_number: T::BlockNumber) -> Result<(), &'static str> { // Make sure we don't fetch the price if unsigned transaction is going to be rejected // anyway. let next_unsigned_at = >::get(); @@ -416,8 +416,8 @@ impl Module { // attack vectors. See validation logic docs for more details. // // Method 1: Unsigned transaction / Unsigned payload - let _result_raw: Result<(), String> = Signer::::send_raw_unsigned_transaction(call) - .map_err(|()| "Unable to submit unsigned transaction.".into()); + let _result_raw: Result<(), &'static str> = Signer::::send_raw_unsigned_transaction(call) + .map_err(|()| "Unable to submit unsigned transaction."); // Method 2: Unsigned transction / signed payload let _result_signed_payload = Signer::::all_accounts().send_unsigned_transaction( diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index b7c9ccaa9821a..73dbf90aa7f09 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -25,7 +25,7 @@ pub use sp_core::{self, crypto::{CryptoType, Public, Derive, IsWrappedBy, Wraps} #[doc(hidden)] #[cfg(feature = "full_crypto")] pub use sp_core::crypto::{SecretStringError, DeriveJunction, Ss58Codec, Pair}; -pub use sp_core::{crypto::{KeyTypeId, key_types}}; +pub use sp_core::crypto::{KeyTypeId, key_types}; #[doc(hidden)] pub use codec; From 9fe440918bcb3309eecf5d88140b1210e711c5b7 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 25 Mar 2020 11:54:14 +0100 Subject: [PATCH 055/108] Document public members in offchain module --- frame/system/src/offchain.rs | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 6860f5c874281..dc0212c46a28a 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -23,10 +23,24 @@ use sp_runtime::app_crypto::RuntimeAppPublic; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; use frame_support::{debug, storage::StorageMap}; - +/// Marker enum used to flag using all supported keys to sign a payload. pub enum ForAll {} +/// Marker enum used to flag using any of the supported keys to sign a payload. pub enum ForAny {} +/// Provides an implementation for signing transaction payloads +/// +/// Keys used for signing are defined when instantiating the signer object. +/// Signing can be done using: +/// +/// - All supported keys in the keystore +/// - Any of the supported keys in the keystore +/// - A list of provided keys +/// +/// The signer is then able to: +/// - Submit a raw unsigned transaction +/// - Submit a unsigned transaction with a signed payload +/// - Submit a signed transaction pub struct Signer, X = ForAny> { accounts: Option>, _phantom: sp_std::marker::PhantomData<(X, C)>, @@ -247,8 +261,7 @@ impl< } } -/// traits - +/// Account information used for signing payloads pub struct Account { pub index: usize, pub id: T::AccountId, @@ -274,6 +287,10 @@ impl Clone for Account where } } +/// App specific crypto trait that provides sign/verify +/// abilities to offchain workers. Implementations of this +/// trait should specify the app-specific public/signature +/// types. pub trait AppCrypto { type RuntimeAppPublic: RuntimeAppPublic; // TODO [ToDr] The conversions are messy, clean them up. @@ -332,6 +349,8 @@ pub trait AppCrypto { } +/// A wrapper around the types which are used for signing transactions. +/// This trait should be implemented on the runtime. pub trait SigningTypes: crate::Trait { //type AccountId; // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? @@ -347,11 +366,15 @@ pub trait SigningTypes: crate::Trait { + codec::Codec; } +/// A wrapper around the transaction and call types pub trait SendTransactionTypes: SigningTypes { type Extrinsic: ExtrinsicT + codec::Encode; type OverarchingCall: From; } +/// Create signed transaction +/// +/// Should be implemented by the runtime to sign transaction data pub trait CreateSignedTransaction: SendTransactionTypes { /// Attempt to create signed extrinsic data that encodes call from given account. /// @@ -367,6 +390,7 @@ pub trait CreateSignedTransaction: SendTransactionTypes { ) -> Option<(Self::OverarchingCall, ::SignaturePayload)>; } +/// Sign message payload pub trait SignMessage { type Result; @@ -378,6 +402,7 @@ pub trait SignMessage { ; } +/// Submit a signed transaction onchain pub trait SendSignedTransaction< T: SigningTypes + CreateSignedTransaction, C: AppCrypto, @@ -422,6 +447,7 @@ pub trait SendSignedTransaction< } } +/// Submit an unsigned transaction onchain with a signed payload pub trait SendUnsignedTransaction< T: SigningTypes + SendTransactionTypes, LocalCall, @@ -446,10 +472,13 @@ pub trait SendUnsignedTransaction< } } +/// Submit a raw unsigned transaction onchain pub trait SendRawUnsignedTransaction, C> { fn send_raw_unsigned_transaction(call: C) -> Result<(), ()>; } +/// Utility trait to be implemented on payloads +/// that should be signed and submitted onchain. pub trait SignedPayload: Encode { fn public(&self) -> T::Public; From 884dc5c3eb13a9ec5e514c72407d5c46e01ef47d Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 31 Mar 2020 13:35:19 +0200 Subject: [PATCH 056/108] Introduce SubmitTransaction --- frame/system/src/offchain.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index da9b304d753a0..8a39928ec5229 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -28,6 +28,23 @@ pub enum ForAll {} /// Marker enum used to flag using any of the supported keys to sign a payload. pub enum ForAny {} +pub struct SubmitTransaction, LocalCall> { + _phantom: sp_std::marker::PhantomData<(T, LocalCall)> +} + +impl SubmitTransaction +where + T: SendTransactionTypes, +{ + pub fn submit_transaction( + call: LocalCall, + signature: Option<::SignaturePayload> + ) -> Result<(), ()> { + let xt = T::Extrinsic::new(call.into(), signature).ok_or(())?; + sp_io::offchain::submit_transaction(xt.encode()) + } +} + /// Provides an implementation for signing transaction payloads /// /// Keys used for signing are defined when instantiating the signer object. @@ -134,18 +151,6 @@ impl> Signer, - C: AppCrypto, - X, - LocalCall, -> SendRawUnsignedTransaction for Signer { - fn send_raw_unsigned_transaction(call: LocalCall) -> Result<(), ()> { - let xt = T::Extrinsic::new(call.into(), None).ok_or(())?; - sp_io::offchain::submit_transaction(xt.encode()) - } -} - impl> SignMessage for Signer { type Result = Vec<(Account, T::Signature)>; @@ -472,11 +477,6 @@ pub trait SendUnsignedTransaction< } } -/// Submit a raw unsigned transaction onchain -pub trait SendRawUnsignedTransaction, C> { - fn send_raw_unsigned_transaction(call: C) -> Result<(), ()>; -} - /// Utility trait to be implemented on payloads /// that should be signed and submitted onchain. pub trait SignedPayload: Encode { From ac8845f24a8bcfef18e095c9e4200a839907c251 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 31 Mar 2020 13:35:57 +0200 Subject: [PATCH 057/108] Update pallets to use SubmitTransaction --- bin/node/runtime/src/lib.rs | 47 +----------------------- frame/example-offchain-worker/src/lib.rs | 8 ++-- frame/im-online/src/lib.rs | 6 +-- frame/staking/src/lib.rs | 7 +--- frame/staking/src/offchain_election.rs | 6 +-- 5 files changed, 13 insertions(+), 61 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 200edfb4b25a2..be712ce6e2f35 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -73,51 +73,6 @@ use constants::{time::*, currency::*}; #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -/// A transaction submitter with the given key type. -pub type TransactionSubmitterOf = TransactionSubmitter; - -/// Submits transaction with the node's public and signature type. Adheres to the signed extension -/// format of the chain. -impl frame_system::offchain::CreateTransaction for Runtime { - type Public = ::Signer; - type Signature = Signature; - - fn create_transaction>( - call: Call, - public: Self::Public, - account: AccountId, - index: Index, - ) -> Option<(Call, ::SignaturePayload)> { - // take the biggest period possible. - 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::() - // The `System::block_number` is initialized with `n+1`, - // so the actual block number is `n`. - .saturating_sub(1); - let tip = 0; - let extra: SignedExtra = ( - frame_system::CheckVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), - frame_system::CheckNonce::::from(index), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - Default::default(), - Default::default(), - ); - 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 address = Indices::unlookup(account); - let (call, extra, _) = raw_payload.deconstruct(); - Some((call, (address, signature, extra))) - } -} /// Runtime version. pub const VERSION: RuntimeVersion = RuntimeVersion { @@ -347,7 +302,6 @@ impl pallet_staking::Trait for Runtime { type NextNewSession = Session; type ElectionLookahead = ElectionLookahead; type Call = Call; - type SubmitTransaction = TransactionSubmitterOf<()>; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; } @@ -559,6 +513,7 @@ impl frame_system::offchain::CreateSignedTransaction for R frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), Default::default(), + Default::default(), ); let raw_payload = SignedPayload::new(call, extra).map_err(|e| { debug::warn!("Unable to create signed payload: {:?}", e); diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 46054405cf261..4ea234af6be28 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -46,7 +46,7 @@ use frame_system::{ ensure_none, offchain::{ AppCrypto, CreateSignedTransaction, SendUnsignedTransaction, - SendRawUnsignedTransaction, SignedPayload, SigningTypes, Signer, + SignedPayload, SigningTypes, Signer, SubmitTransaction, } }; use frame_support::{ @@ -210,6 +210,7 @@ decl_module! { Ok(()) } + #[weight = SimpleDispatchInfo::FixedNormal(10_000)] pub fn submit_price_unsigned_with_signed_payload( origin, price_payload: PricePayload, @@ -418,8 +419,9 @@ impl Module { // attack vectors. See validation logic docs for more details. // // Method 1: Unsigned transaction / Unsigned payload - let _result_raw: Result<(), &'static str> = Signer::::send_raw_unsigned_transaction(call) - .map_err(|()| "Unable to submit unsigned transaction."); + let _result_raw: Result<(), &'static str> = + SubmitTransaction::>::submit_transaction(call, None) + .map_err(|()| "Unable to submit unsigned transaction."); // Method 2: Unsigned transction / signed payload let _result_signed_payload = Signer::::all_accounts().send_unsigned_transaction( diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 6aab520dce459..9e51d13f29fac 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -98,8 +98,7 @@ use frame_support::{ use frame_system::{self as system, ensure_none}; use frame_system::offchain::{ AppCrypto, - Signer, - SendRawUnsignedTransaction, + SubmitTransaction, SendTransactionTypes, }; @@ -489,8 +488,7 @@ impl Module { call, ); - Signer:: - ::send_raw_unsigned_transaction(call) + SubmitTransaction::>::submit_transaction(call, None) .map_err(|_| OffchainErr::SubmitTransaction)?; Ok(()) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index b19caec258fa5..907dee4bacc4a 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -302,7 +302,7 @@ use sp_staking::{ use sp_runtime::{Serialize, Deserialize}; use frame_system::{ self as system, ensure_signed, ensure_root, ensure_none, - offchain::SubmitUnsignedTransaction, + offchain::SendTransactionTypes, }; use sp_phragmen::{ ExtendedBalance, Assignment, PhragmenScore, PhragmenResult, build_support_map, evaluate_support, @@ -712,7 +712,7 @@ impl SessionInterface<::AccountId> for T whe } } -pub trait Trait: frame_system::Trait { +pub trait Trait: SendTransactionTypes> { /// The staking balance. type Currency: LockableCurrency; @@ -772,9 +772,6 @@ pub trait Trait: frame_system::Trait { /// The overarching call type. type Call: From> + IsSubType, Self> + Clone; - /// A transaction submitter. - type SubmitTransaction: SubmitUnsignedTransaction::Call>; - /// The maximum number of nominator rewarded for each validator. /// /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can claim diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 0d4cf49f103b9..ab0aa730c6137 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -19,7 +19,7 @@ use crate::{ Call, CompactAssignments, Module, NominatorIndex, OffchainAccuracy, Trait, ValidatorIndex, }; -use frame_system::offchain::SubmitUnsignedTransaction; +use frame_system::offchain::SubmitTransaction; use sp_phragmen::{ build_support_map, evaluate_support, reduce, Assignment, ExtendedBalance, PhragmenResult, PhragmenScore, @@ -117,14 +117,14 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti let era = >::active_era().map(|e| e.index).unwrap_or_default(); // send it. - let call: ::Call = Call::submit_election_solution_unsigned( + let call = Call::submit_election_solution_unsigned( winners, compact, score, era, ).into(); - T::SubmitTransaction::submit_unsigned(call) + SubmitTransaction::>::submit_transaction(call, None) .map_err(|_| OffchainElectionError::PoolSubmissionFailed) } From 55f3fce7f2fee4eff515dfd96ecd2c43c1befca0 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 31 Mar 2020 13:36:46 +0200 Subject: [PATCH 058/108] WIP --- frame/system/src/offchain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 8a39928ec5229..6fee0025d997b 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -438,8 +438,8 @@ pub trait SendSignedTransaction< account.id.clone(), account_data.nonce )?; - let xt = T::Extrinsic::new(call, Some(signature))?; - let res = sp_io::offchain::submit_transaction(xt.encode()); + let res = SubmitTransaction::>::OverarchingCall> + ::submit_transaction(call, Some(signature)); if res.is_ok() { // increment the nonce. This is fine, since the code should always From fda2dd3948be5a0424b0b3d2395fa393b7489e5b Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 31 Mar 2020 23:18:56 +0200 Subject: [PATCH 059/108] Use SubmitTransaction in offchain --- frame/system/src/offchain.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 6fee0025d997b..ee9548af6d8df 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -28,8 +28,8 @@ pub enum ForAll {} /// Marker enum used to flag using any of the supported keys to sign a payload. pub enum ForAny {} -pub struct SubmitTransaction, LocalCall> { - _phantom: sp_std::marker::PhantomData<(T, LocalCall)> +pub struct SubmitTransaction, OverarchingCall> { + _phantom: sp_std::marker::PhantomData<(T, OverarchingCall)> } impl SubmitTransaction @@ -37,12 +37,18 @@ where T: SendTransactionTypes, { pub fn submit_transaction( - call: LocalCall, + call: >::OverarchingCall, signature: Option<::SignaturePayload> ) -> Result<(), ()> { let xt = T::Extrinsic::new(call.into(), signature).ok_or(())?; sp_io::offchain::submit_transaction(xt.encode()) } + + pub fn submit_unsigned_transaction( + call: >::OverarchingCall, + ) -> Result<(), ()> { + SubmitTransaction::::submit_transaction(call, None) + } } /// Provides an implementation for signing transaction payloads @@ -438,7 +444,7 @@ pub trait SendSignedTransaction< account.id.clone(), account_data.nonce )?; - let res = SubmitTransaction::>::OverarchingCall> + let res = SubmitTransaction:: ::submit_transaction(call, Some(signature)); if res.is_ok() { @@ -472,8 +478,8 @@ pub trait SendUnsignedTransaction< &self, call: LocalCall ) -> Option> { - let xt = T::Extrinsic::new(call.into(), None)?; - Some(sp_io::offchain::submit_transaction(xt.encode())) + Some(SubmitTransaction:: + ::submit_unsigned_transaction(call.into())) } } From 44b0701294f49e98b5ca664ab41718290c5a0c9b Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 31 Mar 2020 23:19:06 +0200 Subject: [PATCH 060/108] Use `submit_unsigned_transaction` --- frame/example-offchain-worker/src/lib.rs | 2 +- frame/im-online/src/lib.rs | 2 +- frame/staking/src/offchain_election.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 4ea234af6be28..9d634c39e36bf 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -420,7 +420,7 @@ impl Module { // // Method 1: Unsigned transaction / Unsigned payload let _result_raw: Result<(), &'static str> = - SubmitTransaction::>::submit_transaction(call, None) + SubmitTransaction::>::submit_unsigned_transaction(call.into()) .map_err(|()| "Unable to submit unsigned transaction."); // Method 2: Unsigned transction / signed payload diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 9e51d13f29fac..b47d9a7ba4d55 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -488,7 +488,7 @@ impl Module { call, ); - SubmitTransaction::>::submit_transaction(call, None) + SubmitTransaction::>::submit_unsigned_transaction(call.into()) .map_err(|_| OffchainErr::SubmitTransaction)?; Ok(()) diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index ab0aa730c6137..72723a8afe199 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -124,7 +124,7 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti era, ).into(); - SubmitTransaction::>::submit_transaction(call, None) + SubmitTransaction::>::submit_unsigned_transaction(call) .map_err(|_| OffchainElectionError::PoolSubmissionFailed) } From ab6c763c937e46c95f06e95d35b4ec22b6e81466 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 31 Mar 2020 23:36:34 +0200 Subject: [PATCH 061/108] Fix tests --- bin/node/executor/tests/submit_transaction.rs | 6 +-- frame/staking/src/mock.rs | 45 ++++++++++++++++--- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 3a203e8635d51..9f0946939c16b 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -25,9 +25,9 @@ use sp_core::offchain::{ testing::TestTransactionPoolExt, }; use frame_system::offchain::{ - SendRawUnsignedTransaction, SendSignedTransaction, Signer, + SubmitTransaction, }; use pallet_im_online::sr25519::AuthorityPair as Key; use codec::Decode; @@ -35,8 +35,6 @@ use codec::Decode; pub mod common; use self::common::*; -type SubmitTransaction = TransactionSubmitterOf; - #[test] fn should_submit_unsigned_transaction() { let mut t = new_test_ext(COMPACT_CODE, false); @@ -53,7 +51,7 @@ fn should_submit_unsigned_transaction() { }; let call = pallet_im_online::Call::heartbeat(heartbeat_data, signature); - Signer::::send_raw_unsigned_transaction(call) + SubmitTransaction::>::submit_unsigned_transaction(call.into()) .unwrap(); assert_eq!(state.read().transactions.len(), 1) diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index d8d9a55032b4f..c019165ae0fd2 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -17,19 +17,19 @@ //! Test utilities use std::{collections::{HashSet, HashMap}, cell::RefCell}; +use codec::{Encode, Decode}; use sp_runtime::Perbill; use sp_runtime::curve::PiecewiseLinear; -use sp_runtime::traits::{IdentityLookup, Convert, SaturatedConversion, Zero}; +use sp_runtime::traits::{IdentityLookup, IdentifyAccount, Convert, SaturatedConversion, Zero}; use sp_runtime::testing::{Header, UintAuthorityId, TestXt}; use sp_staking::{SessionIndex, offence::{OffenceDetails, OnOffenceHandler}}; -use sp_core::H256; +use sp_core::{H256, sr25519::{Public, Signature}}; use frame_support::{ assert_ok, impl_outer_origin, parameter_types, impl_outer_dispatch, impl_outer_event, StorageValue, StorageMap, StorageDoubleMap, IterableStorageMap, traits::{Currency, Get, FindAuthor, OnFinalize, OnInitialize}, weights::Weight, }; -use frame_system::offchain::TransactionSubmitter; use sp_io; use sp_phragmen::{ build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, PhragmenScore, @@ -184,6 +184,31 @@ impl FindAuthor for Author11 { } } +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode)] +pub struct PublicWrapper(Public); + +impl IdentifyAccount for PublicWrapper { + type AccountId = u64; + fn into_account(self) -> Self::AccountId { + let bytes = self.0.to_vec(); + let mut truncated = [0u8; 8]; + truncated.copy_from_slice(bytes.as_slice()); + u64::from_be_bytes(truncated) + } +} + +impl From for PublicWrapper { + fn from(public: Public) -> Self { + Self(public) + } +} + +impl Into for PublicWrapper { + fn into(self) -> Public { + self.0 + } +} + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, Eq, PartialEq, Debug)] pub struct Test; @@ -295,12 +320,22 @@ impl Trait for Test { type NextNewSession = Session; type ElectionLookahead = ElectionLookahead; type Call = Call; - type SubmitTransaction = SubmitTransaction; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; } +impl frame_system::offchain::SendTransactionTypes for Test where + Call: From, +{ + type OverarchingCall = Call; + type Extrinsic = Extrinsic; +} + +impl frame_system::offchain::SigningTypes for Test { + type Public = PublicWrapper; + type Signature = Signature; +} + pub type Extrinsic = TestXt; -type SubmitTransaction = TransactionSubmitter<(), Test, Extrinsic>; pub struct ExtBuilder { session_length: BlockNumber, From de959d69b00d183c33c8373d9a92f4eb6b910dc0 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 1 Apr 2020 11:40:25 +0200 Subject: [PATCH 062/108] Update docs --- frame/system/src/offchain.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index ee9548af6d8df..d6578c716b99f 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -28,6 +28,12 @@ pub enum ForAll {} /// Marker enum used to flag using any of the supported keys to sign a payload. pub enum ForAny {} +/// Provides the ability to directly submit signed and unsigned +/// transaction onchain. +/// +/// For submitting unsigned transactions, `submit_unsigned_transaction` +/// utility function can be used. However, this struct is used by `Signer` +/// to submit a signed transactions providing the signature along with the call. pub struct SubmitTransaction, OverarchingCall> { _phantom: sp_std::marker::PhantomData<(T, OverarchingCall)> } @@ -61,7 +67,6 @@ where /// - A list of provided keys /// /// The signer is then able to: -/// - Submit a raw unsigned transaction /// - Submit a unsigned transaction with a signed payload /// - Submit a signed transaction pub struct Signer, X = ForAny> { From 6d3c36b45936fca369f885b86be2353796453923 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Thu, 2 Apr 2020 16:21:10 +0200 Subject: [PATCH 063/108] Remove SigningTypes requirement from `SendTransactionTypes` --- Cargo.lock | 1 + bin/node/executor/Cargo.toml | 2 +- bin/node/executor/tests/submit_transaction.rs | 31 ++++++---- bin/node/runtime/src/lib.rs | 22 ++----- frame/im-online/src/lib.rs | 6 +- frame/im-online/src/mock.rs | 62 ++----------------- frame/im-online/src/tests.rs | 19 ++---- frame/staking/src/lib.rs | 2 +- frame/staking/src/mock.rs | 35 +---------- frame/system/Cargo.toml | 2 + frame/system/src/lib.rs | 3 + frame/system/src/mock.rs | 43 +++++++++++++ frame/system/src/offchain.rs | 6 +- 13 files changed, 90 insertions(+), 144 deletions(-) create mode 100644 frame/system/src/mock.rs diff --git a/Cargo.lock b/Cargo.lock index 4e16401288b31..2df1d38460ca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1558,6 +1558,7 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "serde", + "sp-application-crypto", "sp-core", "sp-externalities", "sp-io", diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 2f1060a998845..2e29dbd62765d 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -23,7 +23,7 @@ frame-benchmarking = { version = "2.0.0-alpha.5", path = "../../../frame/benchma [dev-dependencies] criterion = "0.3.0" frame-support = { version = "2.0.0-alpha.5", path = "../../../frame/support" } -frame-system = { version = "2.0.0-alpha.5", path = "../../../frame/system" } +frame-system = { version = "2.0.0-alpha.5", path = "../../../frame/system", features=["test-helpers"] } node-testing = { version = "2.0.0-alpha.5", path = "../testing" } pallet-balances = { version = "2.0.0-alpha.5", path = "../../../frame/balances" } pallet-contracts = { version = "2.0.0-alpha.5", path = "../../../frame/contracts" } diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index a0dc89fe5bb30..dc9f45ebe2879 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -15,19 +15,24 @@ // along with Substrate. If not, see . use node_runtime::{ - Executive, Indices, Runtime, UncheckedExtrinsic, ImOnlineAuthorityId, + Executive, Indices, Runtime, UncheckedExtrinsic, }; use sp_application_crypto::AppKey; use sp_core::testing::KeyStore; -use sp_core::traits::KeystoreExt; -use sp_core::offchain::{ - TransactionPoolExt, - testing::TestTransactionPoolExt, +use sp_core::{ + offchain::{ + TransactionPoolExt, + testing::TestTransactionPoolExt, + }, + traits::KeystoreExt, }; -use frame_system::offchain::{ - SendSignedTransaction, - Signer, - SubmitTransaction, +use frame_system::{ + mock, + offchain::{ + Signer, + SubmitTransaction, + SendSignedTransaction, + } }; use pallet_im_online::sr25519::AuthorityPair as Key; use codec::Decode; @@ -73,7 +78,7 @@ fn should_submit_signed_transaction() { t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); @@ -96,7 +101,7 @@ fn should_submit_signed_twice_from_the_same_account() { t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); @@ -107,7 +112,7 @@ fn should_submit_signed_twice_from_the_same_account() { assert_eq!(state.read().transactions.len(), 1); // submit another one from the same account. The nonce should be incremented. - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); @@ -148,7 +153,7 @@ fn submitted_transaction_should_be_valid() { t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 5be417eb85891..874d05d16d662 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -483,10 +483,6 @@ parameter_types! { pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _; } -impl frame_system::offchain::SigningTypes for Runtime { - type Public = ::Signer; - type Signature = Signature; -} impl frame_system::offchain::CreateSignedTransaction for Runtime where Call: From, @@ -530,28 +526,20 @@ impl frame_system::offchain::CreateSignedTransaction for R } } +impl frame_system::offchain::SigningTypes for Runtime { + type Public = ::Signer; + type Signature = Signature; +} + impl frame_system::offchain::SendTransactionTypes for Runtime where Call: From, { type OverarchingCall = Call; type Extrinsic = UncheckedExtrinsic; } -pub struct ImOnlineAuthorityId; -impl frame_system::offchain::AppCrypto<::Signer, Signature> for ImOnlineAuthorityId { - // TODO [ToDr] Get rid of this trait and instead - // have `RuntimeAppPublic` be able to give you `GenericSignature/GenericPublic` - // or see the proposal at `system/src/offchain.rs`. - // - // That way `AppCrypto` would just have a blanket impl for `RuntimeAppPublic`; - type RuntimeAppPublic = ImOnlineId; - type GenericSignature = sp_core::sr25519::Signature; - type GenericPublic = sp_core::sr25519::Public; -} - impl pallet_im_online::Trait for Runtime { type AuthorityId = ImOnlineId; - type OffchainAuthorityId = ImOnlineAuthorityId; type Event = Event; type SessionDuration = SessionDuration; type ReportUnresponsiveness = Offences; diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index b47d9a7ba4d55..e46dbcad63650 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -97,9 +97,8 @@ use frame_support::{ }; use frame_system::{self as system, ensure_none}; use frame_system::offchain::{ - AppCrypto, - SubmitTransaction, SendTransactionTypes, + SubmitTransaction, }; pub mod sr25519 { @@ -227,9 +226,6 @@ pub trait Trait: SendTransactionTypes> + pallet_session::historical:: /// The identifier type for an authority. type AuthorityId: Member + Parameter + RuntimeAppPublic + Default + Ord; - /// TODO How can this be used in place of AuthorityId - type OffchainAuthorityId: AppCrypto; - /// The overarching event type. type Event: From> + Into<::Event>; diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 93bb89f810055..59d2f3ea314de 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -20,15 +20,13 @@ use std::cell::RefCell; -use codec::{Encode, Decode}; -use crate::{Module, Trait, sr25519}; +use crate::{Module, Trait}; use sp_runtime::Perbill; use sp_staking::{SessionIndex, offence::{ReportOffence, OffenceError}}; use sp_runtime::testing::{Header, UintAuthorityId, TestXt}; -use sp_runtime::traits::{IdentityLookup, IdentifyAccount, BlakeTwo256, ConvertInto, Extrinsic as ExtrinsicT}; -use sp_core::{H256, sr25519::{Public, Signature}}; +use sp_runtime::traits::{IdentityLookup, BlakeTwo256, ConvertInto}; +use sp_core::H256; use frame_support::{impl_outer_origin, impl_outer_dispatch, parameter_types, weights::Weight}; - use frame_system as system; impl_outer_origin!{ pub enum Origin for Runtime {} @@ -48,13 +46,6 @@ thread_local! { ])); } -pub struct ImOnlineAuthorityId; -impl frame_system::offchain::AppCrypto for ImOnlineAuthorityId { - type RuntimeAppPublic = sr25519::AuthorityId; - type GenericSignature = Signature; - type GenericPublic = Public; -} - pub struct TestSessionManager; impl pallet_session::SessionManager for TestSessionManager { fn new_session(_new_index: SessionIndex) -> Option> { @@ -70,7 +61,7 @@ impl pallet_session::historical::SessionManager for TestSessionManager .borrow_mut() .take() .map(|validators| { - validators.iter().map(|v| (v.clone(), v.clone())).collect() + validators.iter().map(|v| (*v, *v)).collect() }) ) } @@ -101,32 +92,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities { t.into() } -#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode)] -pub struct PublicWrapper(Public); - -impl IdentifyAccount for PublicWrapper { - type AccountId = u64; - fn into_account(self) -> Self::AccountId { - let bytes = self.0.to_vec(); - let mut truncated = [0u8; 8]; - truncated.copy_from_slice(bytes.as_slice()); - u64::from_be_bytes(truncated) - } -} - -impl From for PublicWrapper { - fn from(public: Public) -> Self { - Self(public) - } -} - -impl Into for PublicWrapper { - fn into(self) -> Public { - self.0 - } -} - - #[derive(Clone, PartialEq, Eq, Debug)] pub struct Runtime; @@ -198,7 +163,6 @@ impl pallet_authorship::Trait for Runtime { impl Trait for Runtime { type AuthorityId = UintAuthorityId; - type OffchainAuthorityId = ImOnlineAuthorityId; type Event = (); type ReportUnresponsiveness = OffenceHandler; type SessionDuration = Period; @@ -211,24 +175,6 @@ impl frame_system::offchain::SendTransactionTypes for Runt type Extrinsic = Extrinsic; } -impl frame_system::offchain::SigningTypes for Runtime { - type Public = PublicWrapper; - type Signature = Signature; -} - -impl frame_system::offchain::CreateSignedTransaction for Runtime where - Call: From, -{ - fn create_transaction>( - call: Call, - _public: PublicWrapper, - _account: u64, - nonce: u64, - ) -> Option<(Call, ::SignaturePayload)> { - Some((call, (nonce, ()))) - } -} - /// Im Online module. pub type ImOnline = Module; pub type System = frame_system::Module; diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 58db1e333137d..5fa931f6c51c9 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -139,8 +139,7 @@ fn should_mark_online_validator_when_heartbeat_is_received() { new_test_ext().execute_with(|| { advance_session(); // given - let validators = vec![1, 2, 3, 4, 5, 6]; - VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); + VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); @@ -175,9 +174,7 @@ fn late_heartbeat_should_fail() { new_test_ext().execute_with(|| { advance_session(); // given - let validators = vec![1, 2, 3, 4, 5, 6]; - VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); - assert_eq!(Session::validators(), Vec::::new()); + VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); @@ -208,8 +205,7 @@ fn should_generate_heartbeats() { // buffer new validators Session::rotate_session(); // enact the change and buffer another one - let validators = vec![1, 2, 3, 4, 5, 6]; - VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); + VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); Session::rotate_session(); // when @@ -241,8 +237,7 @@ fn should_cleanup_received_heartbeats_on_session_end() { new_test_ext().execute_with(|| { advance_session(); - let validators = vec![1, 2, 3]; - VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); + VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3])); assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one @@ -273,8 +268,7 @@ fn should_mark_online_validator_when_block_is_authored() { new_test_ext().execute_with(|| { advance_session(); // given - let validators = vec![1, 2, 3, 4, 5, 6]; - VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); + VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); @@ -310,8 +304,7 @@ fn should_not_send_a_report_if_already_online() { ext.execute_with(|| { advance_session(); // given - let validators = vec![1, 2, 3, 4, 5, 6]; - VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); + VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 907dee4bacc4a..3b2f1ad627317 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -712,7 +712,7 @@ impl SessionInterface<::AccountId> for T whe } } -pub trait Trait: SendTransactionTypes> { +pub trait Trait: frame_system::Trait + SendTransactionTypes> { /// The staking balance. type Currency: LockableCurrency; diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 8c598329f092b..67884f92c75d4 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -17,13 +17,12 @@ //! Test utilities use std::{collections::{HashSet, HashMap}, cell::RefCell}; -use codec::{Encode, Decode}; use sp_runtime::Perbill; use sp_runtime::curve::PiecewiseLinear; -use sp_runtime::traits::{IdentityLookup, IdentifyAccount, Convert, SaturatedConversion, Zero}; +use sp_runtime::traits::{IdentityLookup, Convert, SaturatedConversion, Zero}; use sp_runtime::testing::{Header, UintAuthorityId, TestXt}; use sp_staking::{SessionIndex, offence::{OffenceDetails, OnOffenceHandler}}; -use sp_core::{H256, sr25519::{Public, Signature}}; +use sp_core::H256; use frame_support::{ assert_ok, impl_outer_origin, parameter_types, impl_outer_dispatch, impl_outer_event, StorageValue, StorageMap, StorageDoubleMap, IterableStorageMap, @@ -184,31 +183,6 @@ impl FindAuthor for Author11 { } } -#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode)] -pub struct PublicWrapper(Public); - -impl IdentifyAccount for PublicWrapper { - type AccountId = u64; - fn into_account(self) -> Self::AccountId { - let bytes = self.0.to_vec(); - let mut truncated = [0u8; 8]; - truncated.copy_from_slice(bytes.as_slice()); - u64::from_be_bytes(truncated) - } -} - -impl From for PublicWrapper { - fn from(public: Public) -> Self { - Self(public) - } -} - -impl Into for PublicWrapper { - fn into(self) -> Public { - self.0 - } -} - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, Eq, PartialEq, Debug)] pub struct Test; @@ -330,11 +304,6 @@ impl frame_system::offchain::SendTransactionTypes for Test type Extrinsic = Extrinsic; } -impl frame_system::offchain::SigningTypes for Test { - type Public = PublicWrapper; - type Signature = Signature; -} - pub type Extrinsic = TestXt; pub struct ExtBuilder { diff --git a/frame/system/Cargo.toml b/frame/system/Cargo.toml index 78288cff917e2..d1db8bcf1b261 100644 --- a/frame/system/Cargo.toml +++ b/frame/system/Cargo.toml @@ -16,6 +16,7 @@ sp-std = { version = "2.0.0-alpha.5", default-features = false, path = "../../pr sp-io = { path = "../../primitives/io", default-features = false, version = "2.0.0-alpha.5"} sp-runtime = { version = "2.0.0-alpha.5", default-features = false, path = "../../primitives/runtime" } sp-version = { version = "2.0.0-alpha.5", default-features = false, path = "../../primitives/version" } +sp-application-crypto = { version = "2.0.0-alpha.5", default-features = false, path = "../../primitives/application-crypto", optional = true} frame-support = { version = "2.0.0-alpha.5", default-features = false, path = "../support" } impl-trait-for-tuples = "0.1.3" @@ -36,6 +37,7 @@ std = [ "sp-runtime/std", "sp-version/std", ] +test-helpers = ["sp-application-crypto"] runtime-benchmarks = ["sp-runtime/runtime-benchmarks"] [[bench]] diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 2d965b44f0777..6a6ab19632647 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -128,6 +128,9 @@ use sp_io::TestExternalities; pub mod offchain; +#[cfg(any(feature = "test-helpers", test))] +pub mod mock; + /// Compute the trie root of a list of extrinsics. pub fn extrinsics_root(extrinsics: &[E]) -> H::Output { extrinsics_data_root::(extrinsics.iter().map(codec::Encode::encode).collect()) diff --git a/frame/system/src/mock.rs b/frame/system/src/mock.rs new file mode 100644 index 0000000000000..b1a5b54b62d05 --- /dev/null +++ b/frame/system/src/mock.rs @@ -0,0 +1,43 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Test utilities + +use sp_core::{ + crypto::KeyTypeId, + sr25519::Signature, +}; +use sp_runtime::{MultiSigner, MultiSignature}; +use crate::offchain::AppCrypto; + +pub const TEST_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"test"); + +pub mod sr25519 { + mod app_sr25519 { + use sp_application_crypto::{app_crypto, sr25519}; + use crate::mock::TEST_KEY_TYPE_ID; + app_crypto!(sr25519, TEST_KEY_TYPE_ID); + } + + pub type AuthorityId = app_sr25519::Public; +} + +pub struct TestAuthorityId; +impl AppCrypto for TestAuthorityId { + type RuntimeAppPublic = sr25519::AuthorityId; + type GenericSignature = Signature; + type GenericPublic = sp_core::sr25519::Public; +} diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index d6578c716b99f..46cca89a47a89 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -193,7 +193,7 @@ impl> SignMessage for } impl< - T: CreateSignedTransaction, + T: CreateSignedTransaction + SigningTypes, C: AppCrypto, LocalCall, > SendSignedTransaction for Signer { @@ -383,7 +383,7 @@ pub trait SigningTypes: crate::Trait { } /// A wrapper around the transaction and call types -pub trait SendTransactionTypes: SigningTypes { +pub trait SendTransactionTypes { type Extrinsic: ExtrinsicT + codec::Encode; type OverarchingCall: From; } @@ -391,7 +391,7 @@ pub trait SendTransactionTypes: SigningTypes { /// Create signed transaction /// /// Should be implemented by the runtime to sign transaction data -pub trait CreateSignedTransaction: SendTransactionTypes { +pub trait CreateSignedTransaction: SendTransactionTypes + SigningTypes { /// Attempt to create signed extrinsic data that encodes call from given account. /// /// Runtime implementation is free to construct the payload to sign and the signature From f37c7831050fc1908c8f798964f3765736db865c Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Thu, 2 Apr 2020 20:44:37 +0200 Subject: [PATCH 064/108] Fix tests --- bin/node/executor/tests/submit_transaction.rs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index dc9f45ebe2879..822cab4ef4e4b 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -27,14 +27,13 @@ use sp_core::{ traits::KeystoreExt, }; use frame_system::{ - mock, + mock::{sr25519::AuthorityId, TestAuthorityId}, offchain::{ Signer, SubmitTransaction, SendSignedTransaction, } }; -use pallet_im_online::sr25519::AuthorityPair as Key; use codec::Decode; pub mod common; @@ -72,13 +71,13 @@ fn should_submit_signed_transaction() { t.register_extension(TransactionPoolExt::new(pool)); let keystore = KeyStore::new(); - keystore.write().sr25519_generate_new(Key::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); - keystore.write().sr25519_generate_new(Key::ID, Some(&format!("{}/hunter2", PHRASE))).unwrap(); - keystore.write().sr25519_generate_new(Key::ID, Some(&format!("{}/hunter3", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(AuthorityId::ID, Some(&format!("{}/hunter3", PHRASE))).unwrap(); t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); @@ -97,11 +96,11 @@ fn should_submit_signed_twice_from_the_same_account() { t.register_extension(TransactionPoolExt::new(pool)); let keystore = KeyStore::new(); - keystore.write().sr25519_generate_new(Key::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); @@ -112,7 +111,7 @@ fn should_submit_signed_twice_from_the_same_account() { assert_eq!(state.read().transactions.len(), 1); // submit another one from the same account. The nonce should be incremented. - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); @@ -149,11 +148,11 @@ fn submitted_transaction_should_be_valid() { t.register_extension(TransactionPoolExt::new(pool)); let keystore = KeyStore::new(); - keystore.write().sr25519_generate_new(Key::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let results = Signer::::all_accounts() + let results = Signer::::all_accounts() .send_signed_transaction(|_| { pallet_balances::Call::transfer(Default::default(), Default::default()) }); From fd70a123822297b212021a05bc3fee45e4f92b3d Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 11:18:13 +0200 Subject: [PATCH 065/108] Update frame/system/src/offchain.rs Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/system/src/offchain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 46cca89a47a89..12d5c50b66386 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -57,7 +57,7 @@ where } } -/// Provides an implementation for signing transaction payloads +/// Provides an implementation for signing transaction payloads. /// /// Keys used for signing are defined when instantiating the signer object. /// Signing can be done using: From 848adbf411ed1f9256093913ca5f43446c8ee7ee Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 11:19:58 +0200 Subject: [PATCH 066/108] Update frame/system/src/offchain.rs Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/system/src/offchain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 12d5c50b66386..1c80556a4b1b5 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -242,7 +242,7 @@ impl< ) -> Self::Result where F: Fn(&Account) -> TPayload, - TPayload: SignedPayload + TPayload: SignedPayload, { self.for_any(|account| { let payload = f(account); From b4c5765ef37a13c60f519d27f94b33199fab04cd Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 11:20:22 +0200 Subject: [PATCH 067/108] Update frame/example-offchain-worker/src/tests.rs Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/example-offchain-worker/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index d21af9692a50e..3941227bf528b 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -33,7 +33,7 @@ use sp_runtime::{ testing::{Header, TestXt}, traits::{ BlakeTwo256, IdentityLookup, Extrinsic as ExtrinsicT, - IdentifyAccount, Verify + IdentifyAccount, Verify, }, }; From f88c1c19febf9837710d3fdb9304b7b0047facc5 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 11:20:33 +0200 Subject: [PATCH 068/108] Update frame/system/src/offchain.rs Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/system/src/offchain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 1c80556a4b1b5..76352eb91bb85 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -388,7 +388,7 @@ pub trait SendTransactionTypes { type OverarchingCall: From; } -/// Create signed transaction +/// Create signed transaction. /// /// Should be implemented by the runtime to sign transaction data pub trait CreateSignedTransaction: SendTransactionTypes + SigningTypes { From 13a8c84d699853c1989b3c241732b2cafd833e76 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 11:20:45 +0200 Subject: [PATCH 069/108] Update frame/system/src/offchain.rs Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/system/src/offchain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 76352eb91bb85..58e07b454260f 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -382,7 +382,7 @@ pub trait SigningTypes: crate::Trait { + codec::Codec; } -/// A wrapper around the transaction and call types +/// A wrapper around the transaction and call types. pub trait SendTransactionTypes { type Extrinsic: ExtrinsicT + codec::Encode; type OverarchingCall: From; From 61e69873d907ea06d9cef33747f165f9ef841d47 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 11:25:18 +0200 Subject: [PATCH 070/108] Remove leftover from previous iterations --- primitives/runtime/src/testing.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index fd42401eeed59..f6ef345e47f7e 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -33,12 +33,6 @@ use crate::transaction_validity::{TransactionValidity, TransactionValidityError, #[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug, Hash, Serialize, Deserialize, PartialOrd, Ord)] pub struct UintAuthorityId(pub u64); -impl fmt::Display for UintAuthorityId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - impl From for UintAuthorityId { fn from(id: u64) -> Self { UintAuthorityId(id) From c163b2248a288260bc9667c93b9a02f83ee392fe Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 11:25:31 +0200 Subject: [PATCH 071/108] Change enum to struct --- frame/system/src/offchain.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 46cca89a47a89..861306ba23c11 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -23,10 +23,10 @@ use sp_runtime::app_crypto::RuntimeAppPublic; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; use frame_support::{debug, storage::StorageMap}; -/// Marker enum used to flag using all supported keys to sign a payload. -pub enum ForAll {} -/// Marker enum used to flag using any of the supported keys to sign a payload. -pub enum ForAny {} +/// Marker struct used to flag using all supported keys to sign a payload. +pub struct ForAll {} +/// Marker struct used to flag using any of the supported keys to sign a payload. +pub struct ForAny {} /// Provides the ability to directly submit signed and unsigned /// transaction onchain. From 81ba429515825cd04bfbf7df85a4a1ae313d6ddc Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 11:25:39 +0200 Subject: [PATCH 072/108] Remove public --- frame/system/src/offchain.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 861306ba23c11..40f011b367f64 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -279,9 +279,9 @@ impl< /// Account information used for signing payloads pub struct Account { - pub index: usize, - pub id: T::AccountId, - pub public: T::Public, + index: usize, + id: T::AccountId, + public: T::Public, } impl Account { From 89b947198f25485c71811887cb8e5962f438bf2b Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 11:34:09 +0200 Subject: [PATCH 073/108] Move mock to node/executor/tests --- bin/node/executor/tests/common.rs | 26 +++++++++++++++++++ frame/system/src/mock.rs | 43 ------------------------------- 2 files changed, 26 insertions(+), 43 deletions(-) delete mode 100644 frame/system/src/mock.rs diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index 6b6ef272f8a35..0c9da61abca54 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -31,6 +31,32 @@ use node_primitives::{Hash, BlockNumber}; use node_testing::keyring::*; use sp_externalities::Externalities; +use sp_core::{ + crypto::KeyTypeId, + sr25519::Signature, +}; +use sp_runtime::{MultiSigner, MultiSignature}; +use frame_system::offchain::AppCrypto; + +pub const TEST_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"test"); + +pub mod sr25519 { + mod app_sr25519 { + use sp_application_crypto::{app_crypto, sr25519}; + use super::super::TEST_KEY_TYPE_ID; + app_crypto!(sr25519, TEST_KEY_TYPE_ID); + } + + pub type AuthorityId = app_sr25519::Public; +} + +pub struct TestAuthorityId; +impl AppCrypto for TestAuthorityId { + type RuntimeAppPublic = sr25519::AuthorityId; + type GenericSignature = Signature; + type GenericPublic = sp_core::sr25519::Public; +} + /// The wasm runtime code. /// /// `compact` since it is after post-processing with wasm-gc which performs tree-shaking thus diff --git a/frame/system/src/mock.rs b/frame/system/src/mock.rs deleted file mode 100644 index b1a5b54b62d05..0000000000000 --- a/frame/system/src/mock.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Test utilities - -use sp_core::{ - crypto::KeyTypeId, - sr25519::Signature, -}; -use sp_runtime::{MultiSigner, MultiSignature}; -use crate::offchain::AppCrypto; - -pub const TEST_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"test"); - -pub mod sr25519 { - mod app_sr25519 { - use sp_application_crypto::{app_crypto, sr25519}; - use crate::mock::TEST_KEY_TYPE_ID; - app_crypto!(sr25519, TEST_KEY_TYPE_ID); - } - - pub type AuthorityId = app_sr25519::Public; -} - -pub struct TestAuthorityId; -impl AppCrypto for TestAuthorityId { - type RuntimeAppPublic = sr25519::AuthorityId; - type GenericSignature = Signature; - type GenericPublic = sp_core::sr25519::Public; -} From e69848e45c78444ac594997e0b47a3ae33728a57 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 12:08:57 +0200 Subject: [PATCH 074/108] Cleanup test-helpers --- bin/node/executor/Cargo.toml | 2 +- frame/system/Cargo.toml | 2 -- frame/system/src/lib.rs | 3 --- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 2e29dbd62765d..2f1060a998845 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -23,7 +23,7 @@ frame-benchmarking = { version = "2.0.0-alpha.5", path = "../../../frame/benchma [dev-dependencies] criterion = "0.3.0" frame-support = { version = "2.0.0-alpha.5", path = "../../../frame/support" } -frame-system = { version = "2.0.0-alpha.5", path = "../../../frame/system", features=["test-helpers"] } +frame-system = { version = "2.0.0-alpha.5", path = "../../../frame/system" } node-testing = { version = "2.0.0-alpha.5", path = "../testing" } pallet-balances = { version = "2.0.0-alpha.5", path = "../../../frame/balances" } pallet-contracts = { version = "2.0.0-alpha.5", path = "../../../frame/contracts" } diff --git a/frame/system/Cargo.toml b/frame/system/Cargo.toml index d1db8bcf1b261..78288cff917e2 100644 --- a/frame/system/Cargo.toml +++ b/frame/system/Cargo.toml @@ -16,7 +16,6 @@ sp-std = { version = "2.0.0-alpha.5", default-features = false, path = "../../pr sp-io = { path = "../../primitives/io", default-features = false, version = "2.0.0-alpha.5"} sp-runtime = { version = "2.0.0-alpha.5", default-features = false, path = "../../primitives/runtime" } sp-version = { version = "2.0.0-alpha.5", default-features = false, path = "../../primitives/version" } -sp-application-crypto = { version = "2.0.0-alpha.5", default-features = false, path = "../../primitives/application-crypto", optional = true} frame-support = { version = "2.0.0-alpha.5", default-features = false, path = "../support" } impl-trait-for-tuples = "0.1.3" @@ -37,7 +36,6 @@ std = [ "sp-runtime/std", "sp-version/std", ] -test-helpers = ["sp-application-crypto"] runtime-benchmarks = ["sp-runtime/runtime-benchmarks"] [[bench]] diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index ccb1f3b9f550a..050ab2654b51f 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -128,9 +128,6 @@ use sp_io::TestExternalities; pub mod offchain; -#[cfg(any(feature = "test-helpers", test))] -pub mod mock; - /// Compute the trie root of a list of extrinsics. pub fn extrinsics_root(extrinsics: &[E]) -> H::Output { extrinsics_data_root::(extrinsics.iter().map(codec::Encode::encode).collect()) From ad83ed52f862357383f2d62f0202ed1277632fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 7 Apr 2020 13:18:45 +0200 Subject: [PATCH 075/108] Make `application-crypto` `std` feature internal The macros should not generate code that requires that the calling crate has a feature with the name `std` defined. --- primitives/application-crypto/src/lib.rs | 134 +++++++++++++++-------- 1 file changed, 88 insertions(+), 46 deletions(-) diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index 07e2b45106ee9..79572eb49d1e6 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -103,17 +103,8 @@ macro_rules! app_crypto_pair { type Signature = Signature; type DeriveError = <$pair as $crate::Pair>::DeriveError; - #[cfg(feature = "std")] - fn generate_with_phrase(password: Option<&str>) -> (Self, String, Self::Seed) { - let r = <$pair>::generate_with_phrase(password); - (Self(r.0), r.1, r.2) - } - #[cfg(feature = "std")] - fn from_phrase(phrase: &str, password: Option<&str>) - -> Result<(Self, Self::Seed), $crate::SecretStringError> - { - <$pair>::from_phrase(phrase, password).map(|r| (Self(r.0), r.1)) - } + $crate::app_crypto_pair_functions_if_std!($pair); + fn derive< Iter: Iterator >(&self, path: Iter, seed: Option) -> Result<(Self, Option), Self::DeriveError> { @@ -158,10 +149,38 @@ macro_rules! app_crypto_pair { }; } +/// Implements functions for the `Pair` trait when `feature = "std"` is enabled. +#[doc(hidden)] +#[cfg(feature = "std")] +#[macro_export] +macro_rules! app_crypto_pair_functions_if_std { + ($pair:ty) => { + fn generate_with_phrase(password: Option<&str>) -> (Self, String, Self::Seed) { + let r = <$pair>::generate_with_phrase(password); + (Self(r.0), r.1, r.2) + } + + fn from_phrase(phrase: &str, password: Option<&str>) + -> Result<(Self, Self::Seed), $crate::SecretStringError> + { + <$pair>::from_phrase(phrase, password).map(|r| (Self(r.0), r.1)) + } + } +} + +#[doc(hidden)] +#[cfg(not(feature = "std"))] +#[macro_export] +macro_rules! app_crypto_pair_functions_if_std { + ($pair:ty) => {} +} + + /// Declares Public type which is functionally equivalent to `$public`, but is new /// Application-specific type whose identifier is `$key_type`. /// can only be used together with `full_crypto` feature /// For full functionality, app_crypto_public_common! must be called too. +#[doc(hidden)] #[macro_export] macro_rules! app_crypto_public_full_crypto { ($public:ty, $key_type:expr) => { @@ -195,6 +214,7 @@ macro_rules! app_crypto_public_full_crypto { /// Application-specific type whose identifier is `$key_type`. /// can only be used without `full_crypto` feature /// For full functionality, app_crypto_public_common! must be called too. +#[doc(hidden)] #[macro_export] macro_rules! app_crypto_public_not_full_crypto { ($public:ty, $key_type:expr) => { @@ -223,44 +243,11 @@ macro_rules! app_crypto_public_not_full_crypto { /// Declares Public type which is functionally equivalent to `$public`, but is new /// Application-specific type whose identifier is `$key_type`. /// For full functionality, app_crypto_public_(not)_full_crypto! must be called too. +#[doc(hidden)] #[macro_export] macro_rules! app_crypto_public_common { ($public:ty, $sig:ty, $key_type:expr) => { - impl $crate::Derive for Public { - #[cfg(feature = "std")] - fn derive>(&self, - path: Iter - ) -> Option { - self.0.derive(path).map(Self) - } - } - - #[cfg(feature = "std")] - impl std::fmt::Display for Public { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - use $crate::Ss58Codec; - write!(f, "{}", self.0.to_ss58check()) - } - } - #[cfg(feature = "std")] - impl $crate::serde::Serialize for Public { - fn serialize(&self, serializer: S) -> std::result::Result where - S: $crate::serde::Serializer - { - use $crate::Ss58Codec; - serializer.serialize_str(&self.to_ss58check()) - } - } - #[cfg(feature = "std")] - impl<'de> $crate::serde::Deserialize<'de> for Public { - fn deserialize(deserializer: D) -> std::result::Result where - D: $crate::serde::Deserializer<'de> - { - use $crate::Ss58Codec; - Public::from_ss58check(&String::deserialize(deserializer)?) - .map_err(|e| $crate::serde::de::Error::custom(format!("{:?}", e))) - } - } + $crate::app_crypto_public_common_if_std!(); impl AsRef<[u8]> for Public { fn as_ref(&self) -> &[u8] { self.0.as_ref() } @@ -309,10 +296,63 @@ macro_rules! app_crypto_public_common { } } +/// Implements traits for the public key type if `feature = "std"` is enabled. +#[cfg(feature = "std")] +#[doc(hidden)] +#[macro_export] +macro_rules! app_crypto_public_common_if_std { + () => { + impl $crate::Derive for Public { + fn derive>(&self, + path: Iter + ) -> Option { + self.0.derive(path).map(Self) + } + } + + impl std::fmt::Display for Public { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use $crate::Ss58Codec; + write!(f, "{}", self.0.to_ss58check()) + } + } + + impl $crate::serde::Serialize for Public { + fn serialize(&self, serializer: S) -> std::result::Result where + S: $crate::serde::Serializer + { + use $crate::Ss58Codec; + serializer.serialize_str(&self.to_ss58check()) + } + } + + impl<'de> $crate::serde::Deserialize<'de> for Public { + fn deserialize(deserializer: D) -> std::result::Result where + D: $crate::serde::Deserializer<'de> + { + use $crate::Ss58Codec; + Public::from_ss58check(&String::deserialize(deserializer)?) + .map_err(|e| $crate::serde::de::Error::custom(format!("{:?}", e))) + } + } + } +} + +#[cfg(not(feature = "std"))] +#[doc(hidden)] +#[macro_export] +macro_rules! app_crypto_public_common_if_std { + () => { + impl $crate::Derive for Public {} + } +} + + /// Declares Signature type which is functionally equivalent to `$sig`, but is new /// Application-specific type whose identifier is `$key_type`. /// can only be used together with `full_crypto` feature /// For full functionality, app_crypto_public_common! must be called too. +#[doc(hidden)] #[macro_export] macro_rules! app_crypto_signature_full_crypto { ($sig:ty, $key_type:expr) => { @@ -345,6 +385,7 @@ macro_rules! app_crypto_signature_full_crypto { /// Application-specific type whose identifier is `$key_type`. /// can only be used without `full_crypto` feature /// For full functionality, app_crypto_public_common! must be called too. +#[doc(hidden)] #[macro_export] macro_rules! app_crypto_signature_not_full_crypto { ($sig:ty, $key_type:expr) => { @@ -372,6 +413,7 @@ macro_rules! app_crypto_signature_not_full_crypto { /// Declares Signature type which is functionally equivalent to `$sig`, but is new /// Application-specific type whose identifier is `$key_type`. /// For full functionality, app_crypto_public_(not)_full_crypto! must be called too. +#[doc(hidden)] #[macro_export] macro_rules! app_crypto_signature_common { ($sig:ty, $key_type:expr) => { From 143bfbe5e5de30f3d993c0d3a95500d041e00768 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 13:34:41 +0200 Subject: [PATCH 076/108] Revert cargo lock update --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 072bdde2d5af1..f0da8b5390c63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1570,7 +1570,6 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "serde", - "sp-application-crypto", "sp-core", "sp-externalities", "sp-io", From 9644f85d77f61d7af8f8b1061f8375eebe00f553 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 13:34:52 +0200 Subject: [PATCH 077/108] Use TestAuthorityId from common --- bin/node/executor/tests/submit_transaction.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 822cab4ef4e4b..fadc8c461f02a 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -27,7 +27,6 @@ use sp_core::{ traits::KeystoreExt, }; use frame_system::{ - mock::{sr25519::AuthorityId, TestAuthorityId}, offchain::{ Signer, SubmitTransaction, @@ -71,9 +70,9 @@ fn should_submit_signed_transaction() { t.register_extension(TransactionPoolExt::new(pool)); let keystore = KeyStore::new(); - keystore.write().sr25519_generate_new(AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); - keystore.write().sr25519_generate_new(AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE))).unwrap(); - keystore.write().sr25519_generate_new(AuthorityId::ID, Some(&format!("{}/hunter3", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter3", PHRASE))).unwrap(); t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { @@ -96,7 +95,7 @@ fn should_submit_signed_twice_from_the_same_account() { t.register_extension(TransactionPoolExt::new(pool)); let keystore = KeyStore::new(); - keystore.write().sr25519_generate_new(AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { @@ -148,7 +147,7 @@ fn submitted_transaction_should_be_valid() { t.register_extension(TransactionPoolExt::new(pool)); let keystore = KeyStore::new(); - keystore.write().sr25519_generate_new(AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { From aaae8ee563f33f59991d829f7e7c057fabe006ba Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 15:17:56 +0200 Subject: [PATCH 078/108] Restore members of account to public --- frame/system/src/offchain.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index cead6190a7141..32c72c7287e62 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -279,9 +279,9 @@ impl< /// Account information used for signing payloads pub struct Account { - index: usize, - id: T::AccountId, - public: T::Public, + pub index: usize, + pub id: T::AccountId, + pub public: T::Public, } impl Account { From 917b8a29897919fbb74e5e17f68a1e5ff45ea02b Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 22:27:38 +0200 Subject: [PATCH 079/108] Tidy up imports --- bin/node/executor/tests/common.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index 0c9da61abca54..f25f3f86f8132 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -15,10 +15,21 @@ // along with Substrate. If not, see . use codec::{Encode, Decode}; +use frame_system::offchain::AppCrypto; use frame_support::Hashable; use sp_state_machine::TestExternalities as CoreTestExternalities; -use sp_core::{NeverNativeValue, NativeOrEncoded, traits::{CodeExecutor, RuntimeCode}}; -use sp_runtime::{ApplyExtrinsicResult, traits::{Header as HeaderT, BlakeTwo256}}; +use sp_core::{ + NeverNativeValue, NativeOrEncoded, + crypto::KeyTypeId, + sr25519::Signature, + traits::{CodeExecutor, RuntimeCode}, +}; +use sp_runtime::{ + ApplyExtrinsicResult, + MultiSigner, + MultiSignature, + traits::{Header as HeaderT, BlakeTwo256} +}; use sc_executor::{NativeExecutor, WasmExecutionMethod}; use sc_executor::error::Result; @@ -31,13 +42,6 @@ use node_primitives::{Hash, BlockNumber}; use node_testing::keyring::*; use sp_externalities::Externalities; -use sp_core::{ - crypto::KeyTypeId, - sr25519::Signature, -}; -use sp_runtime::{MultiSigner, MultiSignature}; -use frame_system::offchain::AppCrypto; - pub const TEST_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"test"); pub mod sr25519 { From cdd1af9ae225bee0b080c3306484931ded6bc84f Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 22:27:45 +0200 Subject: [PATCH 080/108] Fix benchmarking pallet --- frame/session/benchmarking/src/mock.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 219a1904e04bb..5259cf0d41ad5 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -149,11 +149,13 @@ parameter_types! { } pub type Extrinsic = sp_runtime::testing::TestXt; -type SubmitTransaction = frame_system::offchain::TransactionSubmitter< - sp_runtime::testing::UintAuthorityId, - Test, - Extrinsic, ->; + +impl frame_system::offchain::SendTransactionTypes for Test where + Call: From, +{ + type OverarchingCall = Call; + type Extrinsic = Extrinsic; +} impl pallet_staking::Trait for Test { type Currency = Balances; @@ -172,7 +174,6 @@ impl pallet_staking::Trait for Test { type NextNewSession = Session; type ElectionLookahead = (); type Call = Call; - type SubmitTransaction = SubmitTransaction; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; } From ae477bb5de11dd94d869fb5017f1e03b541a2a32 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 23:03:02 +0200 Subject: [PATCH 081/108] Add tests demonstrating ForAll, ForAny on signer --- bin/node/executor/tests/submit_transaction.rs | 63 +++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index fadc8c461f02a..caabd810cb35c 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -96,6 +96,51 @@ fn should_submit_signed_twice_from_the_same_account() { let keystore = KeyStore::new(); keystore.write().sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE))).unwrap(); + t.register_extension(KeystoreExt(keystore)); + + t.execute_with(|| { + let result = Signer::::any_account() + .send_signed_transaction(|_| { + pallet_balances::Call::transfer(Default::default(), Default::default()) + }); + + assert!(result.is_some()); + assert_eq!(state.read().transactions.len(), 1); + + // submit another one from the same account. The nonce should be incremented. + let result = Signer::::any_account() + .send_signed_transaction(|_| { + pallet_balances::Call::transfer(Default::default(), Default::default()) + }); + + assert!(result.is_some()); + assert_eq!(state.read().transactions.len(), 2); + + // now check that the transaction nonces are not equal + let s = state.read(); + fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce { + let extra = tx.signature.unwrap().2; + extra.3 + } + let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap()); + let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap()); + assert!( + nonce1 != nonce2, + "Transactions should have different nonces. Got: {:?}", nonce1 + ); + }); +} + +#[test] +fn should_submit_signed_twice_from_all_accounts() { + let mut t = new_test_ext(COMPACT_CODE, false); + let (pool, state) = TestTransactionPoolExt::new(); + t.register_extension(TransactionPoolExt::new(pool)); + + let keystore = KeyStore::new(); + keystore.write().sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE))).unwrap(); t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { @@ -105,9 +150,9 @@ fn should_submit_signed_twice_from_the_same_account() { }); let len = results.len(); - assert_eq!(len, 1); + assert_eq!(len, 2); assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len); - assert_eq!(state.read().transactions.len(), 1); + assert_eq!(state.read().transactions.len(), 2); // submit another one from the same account. The nonce should be incremented. let results = Signer::::all_accounts() @@ -116,9 +161,9 @@ fn should_submit_signed_twice_from_the_same_account() { }); let len = results.len(); - assert_eq!(len, 1); + assert_eq!(len, 2); assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len); - assert_eq!(state.read().transactions.len(), 2); + assert_eq!(state.read().transactions.len(), 4); // now check that the transaction nonces are not equal let s = state.read(); @@ -128,9 +173,15 @@ fn should_submit_signed_twice_from_the_same_account() { } let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap()); let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap()); + let nonce3 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[2]).unwrap()); + let nonce4 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[3]).unwrap()); assert!( - nonce1 != nonce2, - "Transactions should have different nonces. Got: {:?}", nonce1 + nonce1 != nonce3, + "Transactions should have different nonces. Got: 1st tx nonce: {:?}, 2nd nonce: {:?}", nonce1, nonce3 + ); + assert!( + nonce2 != nonce4, + "Transactions should have different nonces. Got: 1st tx nonce: {:?}, 2nd tx nonce: {:?}", nonce2, nonce4 ); }); } From 583cdc3dae5e7c98d7a6d84b9bcb7222c25936db Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 7 Apr 2020 23:12:05 +0200 Subject: [PATCH 082/108] Move definition of AppCrypto in example-offchain-worker from tests to mod::crypto --- frame/example-offchain-worker/src/lib.rs | 15 ++++++++++++++- frame/example-offchain-worker/src/tests.rs | 8 +------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 9d634c39e36bf..67c7de1664457 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -85,8 +85,21 @@ pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"btc!"); /// the types with this pallet-specific identifier. pub mod crypto { use super::KEY_TYPE; - use sp_runtime::app_crypto::{app_crypto, sr25519}; + use sp_runtime::{ + app_crypto::{app_crypto, sr25519}, + traits::Verify, + }; + use sp_core::{ + sr25519::Signature as Sr25519Signature, + }; app_crypto!(sr25519, KEY_TYPE); + + pub struct TestAuthId; + impl frame_system::offchain::AppCrypto<::Signer, Sr25519Signature> for TestAuthId { + type RuntimeAppPublic = Public; + type GenericSignature = sp_core::sr25519::Signature; + type GenericPublic = sp_core::sr25519::Public; + } } /// This pallet's configuration trait diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index 3941227bf528b..63dec91e1e9c2 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -89,12 +89,6 @@ impl frame_system::offchain::SendTransactionTypes for Test type Extrinsic = Extrinsic; } -pub struct TestAuthId; -impl frame_system::offchain::AppCrypto<::Signer, Signature> for TestAuthId { - type RuntimeAppPublic = crypto::Public; - type GenericSignature = sp_core::sr25519::Signature; - type GenericPublic = sp_core::sr25519::Public; -} impl frame_system::offchain::CreateSignedTransaction for Test where Call: From, @@ -116,7 +110,7 @@ parameter_types! { impl Trait for Test { type Event = (); - type AuthorityId = TestAuthId; + type AuthorityId = crypto::TestAuthId; type Call = Call; type GracePeriod = GracePeriod; type UnsignedInterval = UnsignedInterval; From 42f4802a927afec06a6ddb5695e74b6b1c184717 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 8 Apr 2020 11:29:15 +0200 Subject: [PATCH 083/108] Cleanup stray comment --- frame/system/src/offchain.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 32c72c7287e62..b157a8228cc7d 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -368,7 +368,6 @@ pub trait AppCrypto { /// A wrapper around the types which are used for signing transactions. /// This trait should be implemented on the runtime. pub trait SigningTypes: crate::Trait { - //type AccountId; // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? // Seems that this may cause issues with bounds resolution. type Public: Clone From 536c8c358ca207e5f67866ee1d81f675fff615fb Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 8 Apr 2020 11:37:29 +0200 Subject: [PATCH 084/108] Fix ValidTransaction --- frame/example-offchain-worker/src/lib.rs | 54 +++--------------------- 1 file changed, 7 insertions(+), 47 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index fad54a8528718..3dfeeac358de9 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -596,32 +596,32 @@ impl Module { .map(|price| if &price > new_price { price - new_price } else { new_price - price }) .unwrap_or(0); - Ok(ValidTransaction { + ValidTransaction::with_tag_prefix("ExampleOffchainWorker") // We set base priority to 2**20 and hope it's included before any other // transactions in the pool. Next we tweak the priority depending on how much // it differs from the current average. (the more it differs the more priority it // has). - priority: (1 << 20) + avg_price as u64, + .priority(T::UnsignedPriority::get().saturating_add(avg_price as _)) // This transaction does not require anything else to go before into the pool. // In theory we could require `previous_unsigned_at` transaction to go first, // but it's not necessary in our case. - requires: vec![], + //.and_requires() // We set the `provides` tag to be the same as `next_unsigned_at`. This makes // sure only one transaction produced after `next_unsigned_at` will ever // get to the transaction pool and will end up in the block. // We can still have multiple transactions compete for the same "spot", // and the one with higher priority will replace other one in the pool. - provides: vec![Encode::encode(&(KEY_TYPE.0, next_unsigned_at))], + .and_provides(next_unsigned_at) // The transaction is only valid for next 5 blocks. After that it's // going to be revalidated by the pool. - longevity: 5, + .longevity(5) // It's fine to propagate that transaction to other peers, which means it can be // created even by nodes that don't produce blocks. // Note that sometimes it's better to keep it for yourself (if you are the block // producer), since for instance in some schemes others may copy your solution and // claim a reward. - propagate: true, - }) + .propagate(true) + .build() } } @@ -646,49 +646,9 @@ impl frame_support::unsigned::ValidateUnsigned for Module { if !signature_valid { return InvalidTransaction::BadProof.into(); } -<<<<<<< variant A Self::validate_transaction_parameters(&payload.block_number, &payload.price) } else if let Call::submit_price_unsigned(block_number, new_price) = call { Self::validate_transaction_parameters(block_number, new_price) ->>>>>>> variant B - - // We prioritize transactions that are more far away from current average. - // - // Note this doesn't make much sense when building an actual oracle, but this example - // is here mostly to show off offchain workers capabilities, not about building an - // oracle. - let avg_price = Self::average_price() - .map(|price| if &price > new_price { price - new_price } else { new_price - price }) - .unwrap_or(0); - - ValidTransaction::with_tag_prefix("ExampleOffchainWorker") - // We set base priority to 2**20 to make sure it's included before any other - // transactions in the pool. Next we tweak the priority depending on how much - // it differs from the current average. (the more it differs the more priority it - // has). - .priority(T::UnsignedPriority::get().saturating_add(avg_price as _)) - // This transaction does not require anything else to go before into the pool. - // In theory we could require `previous_unsigned_at` transaction to go first, - // but it's not necessary in our case. - //.and_requires() - - // We set the `provides` tag to be the same as `next_unsigned_at`. This makes - // sure only one transaction produced after `next_unsigned_at` will ever - // get to the transaction pool and will end up in the block. - // We can still have multiple transactions compete for the same "spot", - // and the one with higher priority will replace other one in the pool. - .and_provides(next_unsigned_at) - // The transaction is only valid for next 5 blocks. After that it's - // going to be revalidated by the pool. - .longevity(5) - // It's fine to propagate that transaction to other peers, which means it can be - // created even by nodes that don't produce blocks. - // Note that sometimes it's better to keep it for yourself (if you are the block - // producer), since for instance in some schemes others may copy your solution and - // claim a reward. - .propagate(true) - .build() -======= end } else { InvalidTransaction::Call.into() } From 3253d1f7eda72c80a3e5c7c0382417faceb73bdb Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Thu, 9 Apr 2020 12:10:44 +0200 Subject: [PATCH 085/108] Re-fix CreateSignedTransaction --- bin/node/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index d147606a9b79f..1372ed33577ec 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -59,6 +59,7 @@ pub use pallet_balances::Call as BalancesCall; pub use pallet_contracts::Gas; pub use frame_support::StorageValue; pub use pallet_staking::StakerStatus; +use codec::Encode; /// Implementations of some helper traits passed into runtime modules as associated types. pub mod impls; @@ -528,7 +529,6 @@ impl frame_system::offchain::CreateSignedTransaction for R frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), Default::default(), - Default::default(), ); let raw_payload = SignedPayload::new(call, extra).map_err(|e| { debug::warn!("Unable to create signed payload: {:?}", e); From 27f900ecad6f8b8851367d5382c2213920f01e8a Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Mon, 13 Apr 2020 21:40:28 +0200 Subject: [PATCH 086/108] Address PR feedback --- bin/node/executor/tests/common.rs | 2 +- frame/example-offchain-worker/src/tests.rs | 3 --- frame/im-online/src/tests.rs | 3 ++- frame/system/src/offchain.rs | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index f25f3f86f8132..5a51e4312c5e8 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -28,7 +28,7 @@ use sp_runtime::{ ApplyExtrinsicResult, MultiSigner, MultiSignature, - traits::{Header as HeaderT, BlakeTwo256} + traits::{Header as HeaderT, BlakeTwo256}, }; use sc_executor::{NativeExecutor, WasmExecutionMethod}; use sc_executor::error::Result; diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index 654a4ecf8e5f1..b9ccf90bcd9ad 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -186,9 +186,6 @@ fn should_submit_unsigned_transaction_on_chain() { let (offchain, offchain_state) = testing::TestOffchainExt::new(); let (pool, pool_state) = testing::TestTransactionPoolExt::new(); - // Keystore is required here to be used - // if the signed payload / unsigned transaction - // method is used let keystore = KeyStore::new(); let mut t = sp_io::TestExternalities::default(); diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 5fa931f6c51c9..06125bd0530fc 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -174,7 +174,8 @@ fn late_heartbeat_should_fail() { new_test_ext().execute_with(|| { advance_session(); // given - VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); assert_eq!(Session::validators(), Vec::::new()); + VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); + assert_eq!(Session::validators(), Vec::::new()); // enact the change and buffer another one advance_session(); diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index b157a8228cc7d..97f16bc26aa44 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -44,7 +44,7 @@ where { pub fn submit_transaction( call: >::OverarchingCall, - signature: Option<::SignaturePayload> + signature: Option<::SignaturePayload>, ) -> Result<(), ()> { let xt = T::Extrinsic::new(call.into(), signature).ok_or(())?; sp_io::offchain::submit_transaction(xt.encode()) From 40c2335b847dd23e84cacd8ab211d3991d46a5c8 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 14 Apr 2020 13:45:32 +0200 Subject: [PATCH 087/108] Add can_sign method to signer --- frame/example-offchain-worker/src/lib.rs | 9 ++++++++- frame/system/src/offchain.rs | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 3dfeeac358de9..dfe5f7815cb64 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -383,6 +383,13 @@ impl Module { /// A helper function to fetch the price and send signed transaction. fn fetch_price_and_send_signed() -> Result<(), &'static str> { use frame_system::offchain::SendSignedTransaction; + + let signer = Signer::::all_accounts(); + if !signer.can_sign() { + return Err( + "No local accounts available. Consider adding one via `author_insertKey` RPC." + )? + } // Make an external HTTP request to fetch the current price. // Note this call will block until response is received. let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?; @@ -391,7 +398,7 @@ impl Module { // representing the call, we've just created. // Submit signed will return a vector of results for all accounts that were found in the // local keystore with expected `KEY_TYPE`. - let results = Signer::::all_accounts().send_signed_transaction( + let results = signer.send_signed_transaction( |_account| { // Received price is wrapped into a call to `submit_price` public function of this pallet. // This means that the transaction, when executed, will simply call that function passing diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 97f16bc26aa44..f88409e2715c1 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -96,6 +96,11 @@ impl, X> Signer self.accounts = Some(accounts); self } + + /// Check if there are any keys that could be used for signing. + pub fn can_sign(&self) -> bool { + return self.accounts.is_some() && self.accounts.as_ref().unwrap_or(&vec![]).len() > 0 + } } From 9d72eb899aafcc8a5977e0dc268a973c3a57ea05 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 14 Apr 2020 13:45:49 +0200 Subject: [PATCH 088/108] Propagate error --- frame/example-offchain-worker/src/lib.rs | 29 +++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index dfe5f7815cb64..102be10957de2 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -235,8 +235,7 @@ decl_module! { origin, price_payload: PricePayload, _signature: T::Signature, - ) -> DispatchResult - { + ) -> DispatchResult { // This ensures that the function can only be called via unsigned transaction. ensure_none(origin)?; // Add the price to the on-chain list, but mark it as coming from an empty address. @@ -446,12 +445,12 @@ impl Module { // attack vectors. See validation logic docs for more details. // // Method 1: Unsigned transaction / Unsigned payload - let _result_raw: Result<(), &'static str> = - SubmitTransaction::>::submit_unsigned_transaction(call.into()) - .map_err(|()| "Unable to submit unsigned transaction."); + SubmitTransaction::>::submit_unsigned_transaction(call.into()) + .map_err(|()| "Unable to submit unsigned transaction.")?; // Method 2: Unsigned transction / signed payload - let _result_signed_payload = Signer::::all_accounts().send_unsigned_transaction( + // -- Sign using any account + let (_, result) = Signer::::any_account().send_unsigned_transaction( |account| PricePayload { price, block_number, @@ -460,8 +459,22 @@ impl Module { |payload, signature| { Call::submit_price_unsigned_with_signed_payload(payload, signature) } - ); - + ).ok_or("No local accounts accounts available.")?; + result.map_err(|()| "Unable to submit transaction")?; + // -- Sign using all accounts + Signer::::all_accounts().send_unsigned_transaction( + |account| PricePayload { + price, + block_number, + public: account.public.clone() + }, + |payload, signature| { + Call::submit_price_unsigned_with_signed_payload(payload, signature) + } + ).into_iter().fold(Ok(()), |last_res, (_, res)| { + if res.is_err() { return res; } + else { return last_res } + }).map_err(|()| "Unable to submit transaction")?; Ok(()) } From de39bed513dc6f32c0a68d78d6da68ee03d82c8f Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 14 Apr 2020 15:03:05 +0200 Subject: [PATCH 089/108] Improve documentation --- frame/example-offchain-worker/src/tests.rs | 1 - frame/system/src/offchain.rs | 118 ++++++++++++++++++++- 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index b9ccf90bcd9ad..3b0a8dfbd6fba 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -89,7 +89,6 @@ impl frame_system::offchain::SendTransactionTypes for Test type Extrinsic = Extrinsic; } - impl frame_system::offchain::CreateSignedTransaction for Test where Call: From, { diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index f88409e2715c1..a998f5b3c30c3 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -15,6 +15,76 @@ // along with Substrate. If not, see . //! Module helpers for off-chain calls. +//! +//! ## Overview +//! +//! This module provides transaction related helpers to: +//! - Submit a raw unsigned transaction +//! - Submit an unsigned transaction with a signed payload +//! - Submit a signed transction. +//! +//! ## Usage +//! +//! ### Submit a raw unsigned transaction +//! +//! To submit a raw unsigned transaction, [`SubmitTransaction`](./struct.SubmitTransaction.html) +//! can be used. +//! +//! ```rust +//! SubmitTransaction::>::submit_unsigned_transaction(call) +//! ``` +//! +//! ### Signing transactions +//! +//! To be able to use signing, the following trait should be implemented: +//! +//! - [`AppCrypto`](./trait.AppCrypto.html): where an application-specific key +//! is defined and can be used by this module's helpers for signing. +//! - [`CreateSignedTransaction`](./trait.CreateSignedTransaction.html): where +//! the manner in which the transaction is constructed is defined. +//! +//! #### Submit an unsigned transaction with a signed payload +//! +//! Initially, a payload instance that implements the `SignedPayload` trait should be defined. +//! If we take the [`PricePayload`](../../example-offchain-worker/struct.PricePayload.html) +//! defined in the example-offchain-worker pallet, we see the following: +//! +//! ```rust +//! #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] +//! pub struct PricePayload { +//! block_number: BlockNumber, +//! price: u32, +//! public: Public, +//! } +//! +//! impl SignedPayload for PricePayload { +//! fn public(&self) -> T::Public { +//! self.public.clone() +//! } +//! } +//! ``` +//! +//! An object from the defined payload can then be signed and submitted onchain. +//! +//! ``` +//! Signer::::all_accounts().send_unsigned_transaction( +//! |account| PricePayload { +//! price, +//! block_number, +//! public: account.public.clone() +//! }, +//! |payload, signature| { +//! Call::submit_price_unsigned_with_signed_payload(payload, signature) +//! } +//! ) +//! ``` +//! +//! #### Submit a signed transaction +//! +//! ``` +//! Signer::::all_accounts().send_signed_transaction( +//! |account| Call::submit_price(price) +//! ); use codec::Encode; use sp_std::convert::{TryInto, TryFrom}; @@ -42,6 +112,7 @@ impl SubmitTransaction where T: SendTransactionTypes, { + /// Submit transaction onchain by providing the call and an optional signature pub fn submit_transaction( call: >::OverarchingCall, signature: Option<::SignaturePayload>, @@ -50,6 +121,7 @@ where sp_io::offchain::submit_transaction(xt.encode()) } + /// A convenience method to submit an unsigned transaction onchain. pub fn submit_unsigned_transaction( call: >::OverarchingCall, ) -> Result<(), ()> { @@ -64,7 +136,7 @@ where /// /// - All supported keys in the keystore /// - Any of the supported keys in the keystore -/// - A list of provided keys +/// - An intersection of in-keystore keys and the list of provided keys /// /// The signer is then able to: /// - Submit a unsigned transaction with a signed payload @@ -84,14 +156,21 @@ impl, X> Default for Sign } impl, X> Signer { + /// Use all available keys for signing. pub fn all_accounts() -> Signer { Default::default() } + /// Use any of the available keys for signing. pub fn any_account() -> Signer { Default::default() } + /// Use provided `accounts` for signing. + /// + /// Note that not all keys will be necessarily used. The provided + /// vector of accounts will be intersected with the supported keys + /// in the keystore and the resulting list will be used for signing. pub fn with_filter(mut self, accounts: Vec) -> Self { self.accounts = Some(accounts); self @@ -290,6 +369,7 @@ pub struct Account { } impl Account { + /// Create a new Account instance pub fn new(index: usize, id: T::AccountId, public: T::Public) -> Self { Self { index, id, public } } @@ -340,6 +420,7 @@ pub trait AppCrypto { + TryFrom + Into; + /// Sign payload with the private key to maps to the provided public key. fn sign(payload: &[u8], public: Public) -> Option { let p: Self::GenericPublic = public.try_into().ok()?; let x = Into::::into(p); @@ -351,6 +432,7 @@ pub trait AppCrypto { .map(Into::into) } + /// Verify signature against the provided public key. fn verify(payload: &[u8], public: Public, signature: Signature) -> bool { let p: Self::GenericPublic = match public.try_into() { Ok(a) => a, @@ -414,8 +496,18 @@ pub trait CreateSignedTransaction: SendTransactionTypes + pub trait SignMessage { type Result; + /// Sign message + /// + /// Implementation of this method should return + /// a result containing the signature fn sign_message(&self, message: &[u8]) -> Self::Result; + /// Sign payload + /// + /// This method expects `f` to return a `SignedPayload` + /// object which is then used for signing. + /// + /// Returns a result that contains the signature of the payload. fn sign(&self, f: F) -> Self::Result where F: Fn(&Account) -> TPayload, TPayload: SignedPayload, @@ -430,11 +522,23 @@ pub trait SendSignedTransaction< > { type Result; + /// Send a signed onchain transaction + /// + /// Calls `f` and expects a Call object to be returned. + /// The call object is then signed and submitted onchain. + /// + /// Returns a result of the onchain submission. fn send_signed_transaction( &self, f: impl Fn(&Account) -> LocalCall, ) -> Self::Result; + /// Performs signing and submitting the transaction onchain. + /// + /// This method can be used by implementations of `send_signed_transaction` + /// to actually sign and submit the signed transaction. + /// + /// Returns a result of the onchain submittion. fn submit_signed_transaction( &self, account: &Account, @@ -474,6 +578,11 @@ pub trait SendUnsignedTransaction< > { type Result; + /// Send an unsigned transaction with a signed payload. + /// + /// This method takes `f` and `f2` where: + /// - `f` is called and expected to return a `SignedPayload` object. + /// - `f2` is called with the SignedPayload returned by `f` and expected to return a Call object. fn send_unsigned_transaction( &self, f: F, @@ -483,6 +592,7 @@ pub trait SendUnsignedTransaction< F: Fn(&Account) -> TPayload, TPayload: SignedPayload; + /// Submits an unsigned transaction onchain. fn submit_unsigned_transaction( &self, call: LocalCall @@ -497,10 +607,16 @@ pub trait SendUnsignedTransaction< pub trait SignedPayload: Encode { fn public(&self) -> T::Public; + /// Sign the payload using the implementor's provided public key. + /// + /// Returns `Some(signature)` if public key is supported. fn sign>(&self) -> Option { self.using_encoded(|payload| C::sign(payload, self.public())) } + /// Verify signature against payload. + /// + /// Returns a bool indicating whether the signature is valid or not. fn verify>(&self, signature: T::Signature) -> bool { self.using_encoded(|payload| C::verify(payload, self.public(), signature)) } From 4001eda862d164ab0312c476265364391ba98571 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 14 Apr 2020 15:23:50 +0200 Subject: [PATCH 090/108] Fix vec! macro not available --- frame/system/src/offchain.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index a998f5b3c30c3..7b415d6546921 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -178,7 +178,8 @@ impl, X> Signer /// Check if there are any keys that could be used for signing. pub fn can_sign(&self) -> bool { - return self.accounts.is_some() && self.accounts.as_ref().unwrap_or(&vec![]).len() > 0 + return self.accounts.is_some() && + self.accounts.as_ref().unwrap_or(&Vec::new()).len() > 0 } } From 7ac2b29fc9e7e94384ea1cfaa9f1b086f18ccffc Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Tue, 14 Apr 2020 15:24:02 +0200 Subject: [PATCH 091/108] Document SendTransactiontypes --- frame/system/src/offchain.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 7b415d6546921..72dc86da353d5 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -471,7 +471,9 @@ pub trait SigningTypes: crate::Trait { /// A wrapper around the transaction and call types. pub trait SendTransactionTypes { + /// The extrinsic type that this runtime submits onchain. type Extrinsic: ExtrinsicT + codec::Encode; + /// The runtime's call type. type OverarchingCall: From; } From 19ce6d8a65ef05071166076fec9c5e5bedaed224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 15 Apr 2020 15:02:09 +0200 Subject: [PATCH 092/108] Add some docs. --- frame/system/src/offchain.rs | 166 +++++++++++++++++++++-------------- 1 file changed, 100 insertions(+), 66 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 72dc86da353d5..5dfc18f031240 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -86,6 +86,8 @@ //! |account| Call::submit_price(price) //! ); +#![warn(missing_docs)] + use codec::Encode; use sp_std::convert::{TryInto, TryFrom}; use sp_std::prelude::Vec; @@ -248,13 +250,13 @@ impl> Signer> SignMessage for Signer { - type Result = Vec<(Account, T::Signature)>; + type SignatureData = Vec<(Account, T::Signature)>; - fn sign_message(&self, message: &[u8]) -> Self::Result { + fn sign_message(&self, message: &[u8]) -> Self::SignatureData { self.for_all(|account| C::sign(message, account.public.clone())) } - fn sign(&self, f: F) -> Self::Result where + fn sign(&self, f: F) -> Self::SignatureData where F: Fn(&Account) -> TPayload, TPayload: SignedPayload, { @@ -263,13 +265,13 @@ impl> SignMessage for } impl> SignMessage for Signer { - type Result = Option<(Account, T::Signature)>; + type SignatureData = Option<(Account, T::Signature)>; - fn sign_message(&self, message: &[u8]) -> Self::Result { + fn sign_message(&self, message: &[u8]) -> Self::SignatureData { self.for_any(|account| C::sign(message, account.public.clone())) } - fn sign(&self, f: F) -> Self::Result where + fn sign(&self, f: F) -> Self::SignatureData where F: Fn(&Account) -> TPayload, TPayload: SignedPayload, { @@ -288,9 +290,9 @@ impl< &self, f: impl Fn(&Account) -> LocalCall, ) -> Self::Result { - self.for_any(|account| { + self.for_any(|account| { let call = f(account); - self.submit_signed_transaction(account, call) + self.send_single_signed_transaction(account, call) }) } } @@ -308,7 +310,7 @@ impl< ) -> Self::Result { self.for_all(|account| { let call = f(account); - self.submit_signed_transaction(account, call) + self.send_single_signed_transaction(account, call) }) } } @@ -327,7 +329,7 @@ impl< ) -> Self::Result where F: Fn(&Account) -> TPayload, - TPayload: SignedPayload, + TPayload: SignedPayload, { self.for_any(|account| { let payload = f(account); @@ -362,10 +364,13 @@ impl< } } -/// Account information used for signing payloads +/// Details of an account for which a private key is contained in the keystore. pub struct Account { + /// Index on the provided list of accounts or list of all accounts. pub index: usize, + /// Runtime-specific `AccountId`. pub id: T::AccountId, + /// A runtime-specific `Public` key for that key pair. pub public: T::Public, } @@ -389,32 +394,43 @@ impl Clone for Account where } } -/// App specific crypto trait that provides sign/verify -/// abilities to offchain workers. Implementations of this -/// trait should specify the app-specific public/signature -/// types. +/// App-specific crypto trait that provides sign/verify abilities to offchain workers. +/// +/// Implementations of this trait should specify the app-specific public/signature types. +/// This is merely a wrapper around an existing `RuntimeAppPublic` type, but with +/// extra non-application-specific crypto type that is being wrapped (e.g. `sr25519`, `ed25519`). +/// This is needed to later on convert into runtime-specific `Public` key, which might support +/// multiple different crypto. +/// The point of this trait is to be able to easily convert between `RuntimeAppPublic` and +/// the wrapped crypto types. +/// +/// TODO [#???] Potentially use `IsWrappedBy` types, or find some other way to make it easy to +/// obtain unwrapped crypto (and wrap it back). +/// +/// Example (pseudo-)implementation: +/// ``` +/// // im-online specific crypto +/// type RuntimeAppPublic = ImOnline(sr25519::Public); +/// // wrapped "raw" crypto +/// type GenericPublic = sr25519::Public; +/// type GenericSignature = sr25519::Signature; +/// +/// // runtime-specific public key +/// type Public = MultiSigner: From; +/// type Signature = MulitSignature: From; +/// ``` pub trait AppCrypto { + /// A application-specific crypto. type RuntimeAppPublic: RuntimeAppPublic; - // TODO [ToDr] The conversions are messy, clean them up. - // - // The idea would be to have some implementation for `RuntimeAppPublic` - // to convert to and from generic types. - // Maybe even a method like: - // impl RuntimeAppPublic { - // fn into_public>(&self) -> T; - // } - // so an ability to convert the runtime app public into - // some type that is reachable from the inner (wrapped) generic - // crypto type. - // So example: - // ImOnline(sr25519) = RuntimeAppPublic - // sr25519 = Generic - // MutliSigner = From + + /// A raw crypto public key wrapped by `RuntimeAppPublic`. type GenericPublic: From + Into + TryFrom + Into; + + /// A matching raw crypto `Signature` type. type GenericSignature: From<::Signature> + Into<::Signature> @@ -450,36 +466,50 @@ pub trait AppCrypto { x.verify(&payload, &signature) } - } -/// A wrapper around the types which are used for signing transactions. -/// This trait should be implemented on the runtime. +/// A wrapper around the types which are used for signing. +/// +/// This trait adds extra bounds to `Public` and `Signature` types of the runtime +/// that are necessary to use these types for signing. +/// +/// TODO [#???] Could this be just `T::Signature as traits::Verify>::Signer`? +/// Seems that this may cause issues with bounds resolution. pub trait SigningTypes: crate::Trait { - // TODO [ToDr] Could this be just `T::Signature as traits::Verify>::Signer`? - // Seems that this may cause issues with bounds resolution. + /// A public key that is capable of identifing `AccountId`s. + /// + /// Usually that's either a raw crypto public key (e.g. `sr25519::Public`) or + /// an aggregate type for multiple crypto public keys, like `MulitSigner`. type Public: Clone + PartialEq + IdentifyAccount + core::fmt::Debug + codec::Codec; + + /// A matching `Signature` type. type Signature: Clone + PartialEq + core::fmt::Debug + codec::Codec; } -/// A wrapper around the transaction and call types. +/// A definition of types required to submit transactions from within the runtime. pub trait SendTransactionTypes { - /// The extrinsic type that this runtime submits onchain. + /// The extrinsic type expected by the runtime. type Extrinsic: ExtrinsicT + codec::Encode; /// The runtime's call type. + /// + /// This has additional bound to be able to be created from pallet-local `Call` types. type OverarchingCall: From; } /// Create signed transaction. /// -/// Should be implemented by the runtime to sign transaction data +/// This trait is meant to be implemented by the runtime and is responsible for constructing +/// a payload to be signed and contained within the extrinsic. +/// This will most likely include creation of `SignedExtra` (a set of `SignedExtensions`). +/// Note that the result can be altered by inspecting the `Call` (for instance adjusting +/// fees, or mortality depending on the `pallet` being called). pub trait CreateSignedTransaction: SendTransactionTypes + SigningTypes { /// Attempt to create signed extrinsic data that encodes call from given account. /// @@ -495,57 +525,56 @@ pub trait CreateSignedTransaction: SendTransactionTypes + ) -> Option<(Self::OverarchingCall, ::SignaturePayload)>; } -/// Sign message payload +/// A message signer. pub trait SignMessage { - type Result; + /// A signature data. + /// + /// May contain account used for signing and the `Signature` itself. + type SignatureData; - /// Sign message + /// Sign a message. /// /// Implementation of this method should return - /// a result containing the signature - fn sign_message(&self, message: &[u8]) -> Self::Result; + /// a result containing the signature. + fn sign_message(&self, message: &[u8]) -> Self::SignatureData; - /// Sign payload + /// Construct and sign given payload. /// /// This method expects `f` to return a `SignedPayload` /// object which is then used for signing. - /// - /// Returns a result that contains the signature of the payload. - fn sign(&self, f: F) -> Self::Result where + fn sign(&self, f: F) -> Self::SignatureData where F: Fn(&Account) -> TPayload, TPayload: SignedPayload, ; } -/// Submit a signed transaction onchain +/// Submit a signed transaction to the transaction pool. pub trait SendSignedTransaction< T: SigningTypes + CreateSignedTransaction, C: AppCrypto, LocalCall > { + /// A submission result. + /// + /// This should contain an indication of success and the account that was used for signing. type Result; - /// Send a signed onchain transaction - /// - /// Calls `f` and expects a Call object to be returned. - /// The call object is then signed and submitted onchain. + /// Submit a signed transaction to the local pool. /// - /// Returns a result of the onchain submission. + /// Given `f` closure will be called for every requested account and expects a `Call` object + /// to be returned. + /// The call is then wrapped into a transaction (see `#CreateSignedTransaction`), signed and + /// submitted to the pool. fn send_signed_transaction( &self, f: impl Fn(&Account) -> LocalCall, ) -> Self::Result; - /// Performs signing and submitting the transaction onchain. - /// - /// This method can be used by implementations of `send_signed_transaction` - /// to actually sign and submit the signed transaction. - /// - /// Returns a result of the onchain submittion. - fn submit_signed_transaction( + /// Wraps the call into transaction, signs using given account and submits to the pool. + fn send_single_signed_transaction( &self, account: &Account, - call: LocalCall + call: LocalCall, ) -> Option> { let mut account_data = crate::Account::::get(&account.id); debug::native::debug!( @@ -579,13 +608,17 @@ pub trait SendUnsignedTransaction< T: SigningTypes + SendTransactionTypes, LocalCall, > { + /// A submission result. + /// + /// Should contain the submission result and the account(s) that signed the payload. type Result; /// Send an unsigned transaction with a signed payload. /// /// This method takes `f` and `f2` where: - /// - `f` is called and expected to return a `SignedPayload` object. - /// - `f2` is called with the SignedPayload returned by `f` and expected to return a Call object. + /// - `f` is called for every account and is expected to return a `SignedPayload` object. + /// - `f2` is then called with the `SignedPayload` returned by `f` and the signature and is + /// expected to return a `Call` object to be embedded into transaction. fn send_unsigned_transaction( &self, f: F, @@ -595,7 +628,7 @@ pub trait SendUnsignedTransaction< F: Fn(&Account) -> TPayload, TPayload: SignedPayload; - /// Submits an unsigned transaction onchain. + /// Submits an unsigned call to the transaction pool. fn submit_unsigned_transaction( &self, call: LocalCall @@ -605,9 +638,10 @@ pub trait SendUnsignedTransaction< } } -/// Utility trait to be implemented on payloads -/// that should be signed and submitted onchain. +/// Utility trait to be implemented on payloads that can be signed. pub trait SignedPayload: Encode { + /// Return a public key that is expected to have a matching key in the keystore, + /// which should be used to sign the payload. fn public(&self) -> T::Public; /// Sign the payload using the implementor's provided public key. From cbb909a1aa4dba14d8a99d18534cf7bae1a78763 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 15 Apr 2020 15:17:49 +0200 Subject: [PATCH 093/108] Split signing examples --- frame/example-offchain-worker/src/lib.rs | 84 +++++++++++++++--------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 102be10957de2..45aaee42ccea8 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -90,9 +90,7 @@ pub mod crypto { app_crypto::{app_crypto, sr25519}, traits::Verify, }; - use sp_core::{ - sr25519::Signature as Sr25519Signature, - }; + use sp_core::sr25519::Signature as Sr25519Signature; app_crypto!(sr25519, KEY_TYPE); pub struct TestAuthId; @@ -287,6 +285,7 @@ decl_module! { let res = match should_send { TransactionType::Signed => Self::fetch_price_and_send_signed(), TransactionType::Unsigned => Self::fetch_price_and_send_unsigned(block_number), + TransactionType::Raw => Self::fetch_price_and_send_raw_unsigned(block_number), TransactionType::None => Ok(()), }; if let Err(e) = res { @@ -299,6 +298,7 @@ decl_module! { enum TransactionType { Signed, Unsigned, + Raw, None, } @@ -361,12 +361,10 @@ impl Module { // transactions in a row. If a strict order is desired, it's better to use // the storage entry for that. (for instance store both block number and a flag // indicating the type of next transaction to send). - let send_signed = block_number % 2.into() == Zero::zero(); - if send_signed { - TransactionType::Signed - } else { - TransactionType::Unsigned - } + let transaction_type = block_number % 3.into(); + if transaction_type == Zero::zero() { TransactionType::Signed } + else if transaction_type == T::BlockNumber::from(1) { TransactionType::Unsigned } + else { TransactionType::Raw } }, // We are in the grace period, we should not send a transaction this time. Err(RECENTLY_SENT) => TransactionType::None, @@ -405,7 +403,7 @@ impl Module { Call::submit_price(price) } ); - // let results = T::SubmitSignedTransaction::submit_signed(call); + for (acc, res) in &results { match res { Ok(()) => debug::info!("[{:?}] Submitted price of {} cents", acc.id, price), @@ -416,8 +414,8 @@ impl Module { Ok(()) } - /// A helper function to fetch the price and send unsigned transaction. - fn fetch_price_and_send_unsigned(block_number: T::BlockNumber) -> Result<(), &'static str> { + /// A helper function to fetch the price and send a raw unsigned transaction. + fn fetch_price_and_send_raw_unsigned(block_number: T::BlockNumber) -> Result<(), &'static str> { // Make sure we don't fetch the price if unsigned transaction is going to be rejected // anyway. let next_unsigned_at = >::get(); @@ -435,20 +433,42 @@ impl Module { let call = Call::submit_price_unsigned(block_number, price); // Now let's create a transaction out of this call and submit it to the pool. - // Here we showcase two ways to send a transaction: - // 1. An unsigned transaction / unsigned payload (raw) - // 2. An unsigned transaction with a signed payload + // Here we showcase two ways to send an unsigned transaction / unsigned payload (raw) // // By default unsigned transactions are disallowed, so we need to whitelist this case // by writing `UnsignedValidator`. Note that it's EXTREMELY important to carefuly // implement unsigned validation logic, as any mistakes can lead to opening DoS or spam // attack vectors. See validation logic docs for more details. // - // Method 1: Unsigned transaction / Unsigned payload SubmitTransaction::>::submit_unsigned_transaction(call.into()) .map_err(|()| "Unable to submit unsigned transaction.")?; - // Method 2: Unsigned transction / signed payload + Ok(()) + } + + /// A helper function to fetch the price, sign payload and send an unsigned transaction + fn fetch_price_and_send_unsigned(block_number: T::BlockNumber) -> Result<(), &'static str> { + // Make sure we don't fetch the price if unsigned transaction is going to be rejected + // anyway. + let next_unsigned_at = >::get(); + if next_unsigned_at > block_number { + return Err("Too early to send unsigned transaction") + } + + // Make an external HTTP request to fetch the current price. + // Note this call will block until response is received. + let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?; + + // Received price is wrapped into a call to `submit_price_unsigned` public function of this + // pallet. This means that the transaction, when executed, will simply call that function + // passing `price` as an argument. + let call = Call::submit_price_unsigned(block_number, price); + + // Now let's create a transaction out of this call and submit it to the pool. + // Here we showcase two ways to send an unsigned transaction with a signed payload + SubmitTransaction::>::submit_unsigned_transaction(call.into()) + .map_err(|()| "Unable to submit unsigned transaction.")?; + // -- Sign using any account let (_, result) = Signer::::any_account().send_unsigned_transaction( |account| PricePayload { @@ -461,20 +481,24 @@ impl Module { } ).ok_or("No local accounts accounts available.")?; result.map_err(|()| "Unable to submit transaction")?; + // -- Sign using all accounts - Signer::::all_accounts().send_unsigned_transaction( - |account| PricePayload { - price, - block_number, - public: account.public.clone() - }, - |payload, signature| { - Call::submit_price_unsigned_with_signed_payload(payload, signature) - } - ).into_iter().fold(Ok(()), |last_res, (_, res)| { - if res.is_err() { return res; } - else { return last_res } - }).map_err(|()| "Unable to submit transaction")?; + // let transaction_results = Signer::::all_accounts() + // .send_unsigned_transaction( + // |account| PricePayload { + // price, + // block_number, + // public: account.public.clone() + // }, + // |payload, signature| { + // Call::submit_price_unsigned_with_signed_payload(payload, signature) + // } + // ); + // for (_account_id, result) in transaction_results.into_iter() { + // if result.is_err() { + // return Err("Unable to submit transaction"); + // } + // } Ok(()) } From 79f54ab9fc328ef7c740142a50907fd19f96a4fa Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 15 Apr 2020 15:18:25 +0200 Subject: [PATCH 094/108] Add tests for signing examples --- frame/example-offchain-worker/src/tests.rs | 50 +++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index 3b0a8dfbd6fba..90259fc1d25e0 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -182,23 +182,71 @@ fn should_submit_signed_transaction_on_chain() { #[test] fn should_submit_unsigned_transaction_on_chain() { + const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put clutch what kitten"; let (offchain, offchain_state) = testing::TestOffchainExt::new(); let (pool, pool_state) = testing::TestTransactionPoolExt::new(); let keystore = KeyStore::new(); + keystore.write().sr25519_generate_new( + crate::crypto::Public::ID, + Some(&format!("{}/hunter1", PHRASE)) + ).unwrap(); + let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainExt::new(offchain)); t.register_extension(TransactionPoolExt::new(pool)); - t.register_extension(KeystoreExt(keystore)); + t.register_extension(KeystoreExt(keystore.clone())); price_oracle_response(&mut offchain_state.write()); + let public_key = keystore.read() + .sr25519_public_keys(crate::crypto::Public::ID) + .get(0) + .unwrap() + .clone(); + + let price_payload = PricePayload { + block_number: 1, + price: 15523, + public: ::Public::from(public_key), + }; + + // let signature = price_payload.sign::().unwrap(); t.execute_with(|| { + let signature = ::Public, + ::BlockNumber + > as SignedPayload>::sign::(&price_payload).unwrap(); // when Example::fetch_price_and_send_unsigned(1).unwrap(); // then let tx = pool_state.write().transactions.pop().unwrap(); + let tx = Extrinsic::decode(&mut &*tx).unwrap(); + assert_eq!(tx.signature, None); + assert_eq!(tx.call, Call::submit_price_unsigned_with_signed_payload(price_payload, signature)); + }); +} + +#[test] +fn should_submit_raw_unsigned_transaction_on_chain() { + let (offchain, offchain_state) = testing::TestOffchainExt::new(); + let (pool, pool_state) = testing::TestTransactionPoolExt::new(); + + let keystore = KeyStore::new(); + + let mut t = sp_io::TestExternalities::default(); + t.register_extension(OffchainExt::new(offchain)); + t.register_extension(TransactionPoolExt::new(pool)); + t.register_extension(KeystoreExt(keystore)); + + price_oracle_response(&mut offchain_state.write()); + + t.execute_with(|| { + // when + Example::fetch_price_and_send_raw_unsigned(1).unwrap(); + // then + let tx = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx = Extrinsic::decode(&mut &*tx).unwrap(); assert_eq!(tx.signature, None); From 8491fbcf0cc81e3ff6e5e206746dd7275caab4ab Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 15 Apr 2020 15:18:41 +0200 Subject: [PATCH 095/108] WIP can_sign - PR feedback --- frame/system/src/offchain.rs | 100 ++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 72dc86da353d5..e08d2f9b999c3 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -178,8 +178,46 @@ impl, X> Signer /// Check if there are any keys that could be used for signing. pub fn can_sign(&self) -> bool { - return self.accounts.is_some() && - self.accounts.as_ref().unwrap_or(&Vec::new()).len() > 0 + self.accounts_from_keys().len() > 0 + } + + /// Return a vector of the intersection between + /// all available accounts and the provided accounts + /// in `with_filter`. If no accounts are provided, + /// use all accounts by default. + fn accounts_from_keys(&self) -> Vec> { + let keystore_accounts = self.keystore_accounts(); + + if self.accounts.as_ref().unwrap_or(&Vec::new()).len() == 0 { + return keystore_accounts; + } + + let mut intersecting_accounts = vec![]; + if let Some(ref keys) = self.accounts { + for (index, key) in keys.iter().enumerate() { + let account_id = key.clone().into_account(); + let account = Account::new(index, account_id, key.clone()); + intersecting_accounts.push(account); + } + } + intersecting_accounts + } + + fn keystore_accounts(&self) -> Vec> { + let mut accounts = vec![]; + let runtime_keys = C::RuntimeAppPublic::all() + .into_iter() + .enumerate(); + + for (index, key) in runtime_keys { + let generic_public = C::GenericPublic::from(key); + let public = generic_public.into(); + let account_id = public.clone().into_account(); + let account = Account::new(index, account_id, public.clone()); + accounts.push(account); + } + + accounts } } @@ -188,29 +226,13 @@ impl> Signer(&self, f: F) -> Vec<(Account, R)> where F: Fn(&Account) -> Option, { - if let Some(ref accounts) = self.accounts { - accounts - .iter() - .enumerate() - .filter_map(|(index, key)| { - let account_id = key.clone().into_account(); - let account = Account::new(index, account_id, key.clone()); - f(&account).map(|res| (account, res)) - }) - .collect() - } else { - C::RuntimeAppPublic::all() - .into_iter() - .enumerate() - .filter_map(|(index, key)| { - let generic_public = C::GenericPublic::from(key); - let public = generic_public.into(); - let account_id = public.clone().into_account(); - let account = Account::new(index, account_id, public.clone()); - f(&account).map(|res| (account, res)) - }) - .collect() - } + let accounts = self.accounts_from_keys(); + accounts + .into_iter() + .filter_map(|account| { + f(&account).map(|res| (account, res)) + }) + .collect() } } @@ -218,29 +240,11 @@ impl> Signer(&self, f: F) -> Option<(Account, R)> where F: Fn(&Account) -> Option, { - if let Some(ref accounts) = self.accounts { - for (index, key) in accounts.iter().enumerate() { - let account_id = key.clone().into_account(); - let account = Account::new(index, account_id, key.clone()); - let res = f(&account); - if let Some(res) = res { - return Some((account, res)); - } - } - } else { - let runtime_keys = C::RuntimeAppPublic::all() - .into_iter() - .enumerate(); - - for (index, key) in runtime_keys { - let generic_public = C::GenericPublic::from(key); - let public = generic_public.into(); - let account_id = public.clone().into_account(); - let account = Account::new(index, account_id, public.clone()); - let res = f(&account); - if let Some(res) = res { - return Some((account, res)); - } + let accounts = self.accounts_from_keys(); + for account in accounts.into_iter() { + let res = f(&account); + if let Some(res) = res { + return Some((account, res)); } } None From 9d125a1cdee711845a1dcf6c634e1fc981e7f2fb Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Wed, 15 Apr 2020 16:18:15 +0200 Subject: [PATCH 096/108] WIP --- frame/system/src/offchain.rs | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 1ff05f6081c67..235f4965c0b9b 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -180,46 +180,46 @@ impl, X> Signer /// Check if there are any keys that could be used for signing. pub fn can_sign(&self) -> bool { - self.accounts_from_keys().len() > 0 + self.accounts_from_keys().count() > 0 } /// Return a vector of the intersection between /// all available accounts and the provided accounts /// in `with_filter`. If no accounts are provided, /// use all accounts by default. - fn accounts_from_keys(&self) -> Vec> { + fn accounts_from_keys(&self) -> impl Iterator> { let keystore_accounts = self.keystore_accounts(); if self.accounts.as_ref().unwrap_or(&Vec::new()).len() == 0 { return keystore_accounts; } - let mut intersecting_accounts = vec![]; + let keystore_accounts: Vec<::Public> = keystore_accounts.map(|account| { + account.public + }).collect(); + if let Some(ref keys) = self.accounts { - for (index, key) in keys.iter().enumerate() { - let account_id = key.clone().into_account(); - let account = Account::new(index, account_id, key.clone()); - intersecting_accounts.push(account); - } + return keys.into_iter() + .enumerate() + .map(|(index, key)| { + let account_id = key.clone().into_account(); + Account::new(index, account_id, key.clone()) + }) + .filter(|account| keystore_accounts.contains(account.public)); } - intersecting_accounts + std::iter::empty::>() } - fn keystore_accounts(&self) -> Vec> { - let mut accounts = vec![]; - let runtime_keys = C::RuntimeAppPublic::all() + fn keystore_accounts(&self) -> impl Iterator> { + C::RuntimeAppPublic::all() .into_iter() - .enumerate(); - - for (index, key) in runtime_keys { - let generic_public = C::GenericPublic::from(key); - let public = generic_public.into(); - let account_id = public.clone().into_account(); - let account = Account::new(index, account_id, public.clone()); - accounts.push(account); - } - - accounts + .enumerate() + .map(|(index, key)| { + let generic_public = C::GenericPublic::from(key); + let public = generic_public.into(); + let account_id = public.clone().into_account(); + Account::new(index, account_id, public.clone()) + }) } } From 507ef7110cc382673c2e601552372172099dbe7a Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Thu, 16 Apr 2020 12:33:08 +0200 Subject: [PATCH 097/108] Split for_any / for_all into different calls --- frame/example-offchain-worker/src/lib.rs | 69 +++++++++++++++++------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 45aaee42ccea8..e1756ebe7f3e4 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -284,7 +284,8 @@ decl_module! { let should_send = Self::choose_transaction_type(block_number); let res = match should_send { TransactionType::Signed => Self::fetch_price_and_send_signed(), - TransactionType::Unsigned => Self::fetch_price_and_send_unsigned(block_number), + TransactionType::UnsignedForAny => Self::fetch_price_and_send_unsigned_for_any_account(block_number), + TransactionType::UnsignedForAll => Self::fetch_price_and_send_unsigned_for_all_accounts(block_number), TransactionType::Raw => Self::fetch_price_and_send_raw_unsigned(block_number), TransactionType::None => Ok(()), }; @@ -297,7 +298,8 @@ decl_module! { enum TransactionType { Signed, - Unsigned, + UnsignedForAny, + UnsignedForAll, Raw, None, } @@ -363,7 +365,8 @@ impl Module { // indicating the type of next transaction to send). let transaction_type = block_number % 3.into(); if transaction_type == Zero::zero() { TransactionType::Signed } - else if transaction_type == T::BlockNumber::from(1) { TransactionType::Unsigned } + else if transaction_type == T::BlockNumber::from(1) { TransactionType::UnsignedForAny } + else if transaction_type == T::BlockNumber::from(2) { TransactionType::UnsignedForAll } else { TransactionType::Raw } }, // We are in the grace period, we should not send a transaction this time. @@ -447,7 +450,7 @@ impl Module { } /// A helper function to fetch the price, sign payload and send an unsigned transaction - fn fetch_price_and_send_unsigned(block_number: T::BlockNumber) -> Result<(), &'static str> { + fn fetch_price_and_send_unsigned_for_any_account(block_number: T::BlockNumber) -> Result<(), &'static str> { // Make sure we don't fetch the price if unsigned transaction is going to be rejected // anyway. let next_unsigned_at = >::get(); @@ -482,23 +485,49 @@ impl Module { ).ok_or("No local accounts accounts available.")?; result.map_err(|()| "Unable to submit transaction")?; + Ok(()) + } + + /// A helper function to fetch the price, sign payload and send an unsigned transaction + fn fetch_price_and_send_unsigned_for_all_accounts(block_number: T::BlockNumber) -> Result<(), &'static str> { + // Make sure we don't fetch the price if unsigned transaction is going to be rejected + // anyway. + let next_unsigned_at = >::get(); + if next_unsigned_at > block_number { + return Err("Too early to send unsigned transaction") + } + + // Make an external HTTP request to fetch the current price. + // Note this call will block until response is received. + let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?; + + // Received price is wrapped into a call to `submit_price_unsigned` public function of this + // pallet. This means that the transaction, when executed, will simply call that function + // passing `price` as an argument. + let call = Call::submit_price_unsigned(block_number, price); + + // Now let's create a transaction out of this call and submit it to the pool. + // Here we showcase two ways to send an unsigned transaction with a signed payload + SubmitTransaction::>::submit_unsigned_transaction(call.into()) + .map_err(|()| "Unable to submit unsigned transaction.")?; + // -- Sign using all accounts - // let transaction_results = Signer::::all_accounts() - // .send_unsigned_transaction( - // |account| PricePayload { - // price, - // block_number, - // public: account.public.clone() - // }, - // |payload, signature| { - // Call::submit_price_unsigned_with_signed_payload(payload, signature) - // } - // ); - // for (_account_id, result) in transaction_results.into_iter() { - // if result.is_err() { - // return Err("Unable to submit transaction"); - // } - // } + let transaction_results = Signer::::all_accounts() + .send_unsigned_transaction( + |account| PricePayload { + price, + block_number, + public: account.public.clone() + }, + |payload, signature| { + Call::submit_price_unsigned_with_signed_payload(payload, signature) + } + ); + for (_account_id, result) in transaction_results.into_iter() { + if result.is_err() { + return Err("Unable to submit transaction"); + } + } Ok(()) } From 5732ff15142f86f9fc64bca0d76988c38b1a3bba Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Thu, 16 Apr 2020 12:33:33 +0200 Subject: [PATCH 098/108] Verify payload and signature in test --- frame/example-offchain-worker/src/tests.rs | 72 +++++++++++++++++++--- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index 90259fc1d25e0..0259f43324a92 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -181,7 +181,7 @@ fn should_submit_signed_transaction_on_chain() { } #[test] -fn should_submit_unsigned_transaction_on_chain() { +fn should_submit_unsigned_transaction_on_chain_for_any_account() { const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put clutch what kitten"; let (offchain, offchain_state) = testing::TestOffchainExt::new(); let (pool, pool_state) = testing::TestTransactionPoolExt::new(); @@ -214,17 +214,75 @@ fn should_submit_unsigned_transaction_on_chain() { // let signature = price_payload.sign::().unwrap(); t.execute_with(|| { - let signature = ::Public, - ::BlockNumber - > as SignedPayload>::sign::(&price_payload).unwrap(); // when - Example::fetch_price_and_send_unsigned(1).unwrap(); + Example::fetch_price_and_send_unsigned_for_any_account(1).unwrap(); // then let tx = pool_state.write().transactions.pop().unwrap(); let tx = Extrinsic::decode(&mut &*tx).unwrap(); assert_eq!(tx.signature, None); - assert_eq!(tx.call, Call::submit_price_unsigned_with_signed_payload(price_payload, signature)); + if let Call::submit_price_unsigned_with_signed_payload(body, signature) = tx.call { + assert_eq!(body, price_payload); + + let signature_valid = ::Public, + ::BlockNumber + > as SignedPayload>::verify::(&price_payload, signature); + + assert!(signature_valid); + } + }); +} + +#[test] +fn should_submit_unsigned_transaction_on_chain_for_all_accounts() { + const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put clutch what kitten"; + let (offchain, offchain_state) = testing::TestOffchainExt::new(); + let (pool, pool_state) = testing::TestTransactionPoolExt::new(); + + let keystore = KeyStore::new(); + + keystore.write().sr25519_generate_new( + crate::crypto::Public::ID, + Some(&format!("{}/hunter1", PHRASE)) + ).unwrap(); + + let mut t = sp_io::TestExternalities::default(); + t.register_extension(OffchainExt::new(offchain)); + t.register_extension(TransactionPoolExt::new(pool)); + t.register_extension(KeystoreExt(keystore.clone())); + + price_oracle_response(&mut offchain_state.write()); + + let public_key = keystore.read() + .sr25519_public_keys(crate::crypto::Public::ID) + .get(0) + .unwrap() + .clone(); + + let price_payload = PricePayload { + block_number: 1, + price: 15523, + public: ::Public::from(public_key), + }; + + // let signature = price_payload.sign::().unwrap(); + t.execute_with(|| { + // when + Example::fetch_price_and_send_unsigned_for_all_accounts(1).unwrap(); + // then + let tx = pool_state.write().transactions.pop().unwrap(); + let tx = Extrinsic::decode(&mut &*tx).unwrap(); + assert_eq!(tx.signature, None); + if let Call::submit_price_unsigned_with_signed_payload(body, signature) = tx.call { + assert_eq!(body, price_payload); + + let signature_valid = ::Public, + ::BlockNumber + > as SignedPayload>::verify::(&price_payload, signature); + + assert!(signature_valid); + } }); } From 2dde0c0f3c8b2c0d3c3fca7f917bb52e82566d48 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Thu, 16 Apr 2020 12:37:18 +0200 Subject: [PATCH 099/108] Fix can_sign implementation --- frame/system/src/offchain.rs | 39 +++++++++++++++++------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 235f4965c0b9b..1d4d49feeed32 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -89,12 +89,12 @@ #![warn(missing_docs)] use codec::Encode; +use sp_std::collections::btree_set::BTreeSet; use sp_std::convert::{TryInto, TryFrom}; use sp_std::prelude::Vec; use sp_runtime::app_crypto::RuntimeAppPublic; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; use frame_support::{debug, storage::StorageMap}; - /// Marker struct used to flag using all supported keys to sign a payload. pub struct ForAll {} /// Marker struct used to flag using any of the supported keys to sign a payload. @@ -187,27 +187,23 @@ impl, X> Signer /// all available accounts and the provided accounts /// in `with_filter`. If no accounts are provided, /// use all accounts by default. - fn accounts_from_keys(&self) -> impl Iterator> { + fn accounts_from_keys<'a>(&'a self) -> Box> + 'a> { let keystore_accounts = self.keystore_accounts(); - - if self.accounts.as_ref().unwrap_or(&Vec::new()).len() == 0 { - return keystore_accounts; - } - - let keystore_accounts: Vec<::Public> = keystore_accounts.map(|account| { - account.public - }).collect(); - - if let Some(ref keys) = self.accounts { - return keys.into_iter() - .enumerate() - .map(|(index, key)| { - let account_id = key.clone().into_account(); - Account::new(index, account_id, key.clone()) - }) - .filter(|account| keystore_accounts.contains(account.public)); + match self.accounts { + None => Box::new(keystore_accounts), + Some(ref keys) => { + let keystore_lookup: BTreeSet<::Public> = keystore_accounts + .map(|account| account.public).collect(); + + Box::new(keys.into_iter() + .enumerate() + .map(|(index, key)| { + let account_id = key.clone().into_account(); + Account::new(index, account_id, key.clone()) + }) + .filter(move |account| keystore_lookup.contains(&account.public))) + } } - std::iter::empty::>() } fn keystore_accounts(&self) -> impl Iterator> { @@ -488,7 +484,8 @@ pub trait SigningTypes: crate::Trait { + PartialEq + IdentifyAccount + core::fmt::Debug - + codec::Codec; + + codec::Codec + + Ord; /// A matching `Signature` type. type Signature: Clone From c378199c0f17984905811cf568600cedd9653281 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Thu, 16 Apr 2020 12:41:29 +0200 Subject: [PATCH 100/108] Fix impl_version --- bin/node/runtime/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 18c4cd3f5d63e..8f35d986a0caa 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -85,7 +85,6 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // is and increment impl_version. spec_version: 242, impl_version: 1, - impl_version: 0, apis: RUNTIME_API_VERSIONS, }; From 4a6d2686e7871137f744f31781ab22e8f65f61d1 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Thu, 16 Apr 2020 13:30:16 +0200 Subject: [PATCH 101/108] Import Box from sp_std --- frame/system/src/offchain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 1d4d49feeed32..bb99f7652a879 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -91,7 +91,7 @@ use codec::Encode; use sp_std::collections::btree_set::BTreeSet; use sp_std::convert::{TryInto, TryFrom}; -use sp_std::prelude::Vec; +use sp_std::prelude::{Box, Vec}; use sp_runtime::app_crypto::RuntimeAppPublic; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; use frame_support::{debug, storage::StorageMap}; From a94fecc435bc180e682cf2c6a4f82fe187b3164e Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Thu, 16 Apr 2020 13:50:33 +0200 Subject: [PATCH 102/108] Create issues for TODOs --- frame/system/src/offchain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index bb99f7652a879..85be4b146d080 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -404,7 +404,7 @@ impl Clone for Account where /// The point of this trait is to be able to easily convert between `RuntimeAppPublic` and /// the wrapped crypto types. /// -/// TODO [#???] Potentially use `IsWrappedBy` types, or find some other way to make it easy to +/// TODO [#5662] Potentially use `IsWrappedBy` types, or find some other way to make it easy to /// obtain unwrapped crypto (and wrap it back). /// /// Example (pseudo-)implementation: @@ -473,7 +473,7 @@ pub trait AppCrypto { /// This trait adds extra bounds to `Public` and `Signature` types of the runtime /// that are necessary to use these types for signing. /// -/// TODO [#???] Could this be just `T::Signature as traits::Verify>::Signer`? +/// TODO [#5663] Could this be just `T::Signature as traits::Verify>::Signer`? /// Seems that this may cause issues with bounds resolution. pub trait SigningTypes: crate::Trait { /// A public key that is capable of identifing `AccountId`s. From 4a6133434b80053bc29d465556162db2bf24104d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 16 Apr 2020 16:55:19 +0200 Subject: [PATCH 103/108] Ignore doctest. --- frame/system/src/offchain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 85be4b146d080..a508d06ab1e25 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -408,7 +408,7 @@ impl Clone for Account where /// obtain unwrapped crypto (and wrap it back). /// /// Example (pseudo-)implementation: -/// ``` +/// ```ignore /// // im-online specific crypto /// type RuntimeAppPublic = ImOnline(sr25519::Public); /// // wrapped "raw" crypto From 57221630ed3efd182bce19468d211c0d98454d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 16 Apr 2020 18:58:20 +0200 Subject: [PATCH 104/108] Add test directly to system. Adjust UintTypes. --- frame/system/src/lib.rs | 11 ++- frame/system/src/offchain.rs | 92 ++++++++++++++++++- .../src/generic/unchecked_extrinsic.rs | 23 +---- primitives/runtime/src/testing.rs | 48 ++++++---- 4 files changed, 126 insertions(+), 48 deletions(-) diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 31b862f3b23c3..936c1aa936718 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1591,7 +1591,7 @@ impl Lookup for ChainContext { } #[cfg(test)] -mod tests { +pub(crate) mod tests { use super::*; use sp_std::cell::RefCell; use sp_core::H256; @@ -1602,7 +1602,7 @@ mod tests { pub enum Origin for Test where system = super {} } - #[derive(Clone, Eq, PartialEq)] + #[derive(Clone, Eq, PartialEq, Debug)] pub struct Test; parameter_types! { @@ -1629,8 +1629,9 @@ mod tests { fn on_killed_account(who: &u64) { KILLED.with(|r| r.borrow_mut().push(*who)) } } - #[derive(Debug)] - pub struct Call {} + #[derive(Debug, codec::Encode, codec::Decode)] + pub struct Call; + impl Dispatchable for Call { type Origin = (); type Trait = (); @@ -1678,7 +1679,7 @@ mod tests { type System = Module; - const CALL: &::Call = &Call {}; + const CALL: &::Call = &Call; fn new_test_ext() -> sp_io::TestExternalities { GenesisConfig::default().build_storage::().unwrap().into() diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index a508d06ab1e25..8192ec100f92e 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -94,7 +94,8 @@ use sp_std::convert::{TryInto, TryFrom}; use sp_std::prelude::{Box, Vec}; use sp_runtime::app_crypto::RuntimeAppPublic; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; -use frame_support::{debug, storage::StorageMap}; +use frame_support::{debug, storage::StorageMap, RuntimeDebug}; + /// Marker struct used to flag using all supported keys to sign a payload. pub struct ForAll {} /// Marker struct used to flag using any of the supported keys to sign a payload. @@ -143,6 +144,7 @@ where /// The signer is then able to: /// - Submit a unsigned transaction with a signed payload /// - Submit a signed transaction +#[derive(RuntimeDebug)] pub struct Signer, X = ForAny> { accounts: Option>, _phantom: sp_std::marker::PhantomData<(X, C)>, @@ -365,6 +367,7 @@ impl< } /// Details of an account for which a private key is contained in the keystore. +#[derive(RuntimeDebug, PartialEq)] pub struct Account { /// Index on the provided list of accounts or list of all accounts. pub index: usize, @@ -394,15 +397,15 @@ impl Clone for Account where } } -/// App-specific crypto trait that provides sign/verify abilities to offchain workers. +/// A type binding runtime-level `Public/Signature` pair with crypto wrapped by `RuntimeAppPublic`. /// /// Implementations of this trait should specify the app-specific public/signature types. /// This is merely a wrapper around an existing `RuntimeAppPublic` type, but with /// extra non-application-specific crypto type that is being wrapped (e.g. `sr25519`, `ed25519`). /// This is needed to later on convert into runtime-specific `Public` key, which might support /// multiple different crypto. -/// The point of this trait is to be able to easily convert between `RuntimeAppPublic` and -/// the wrapped crypto types. +/// The point of this trait is to be able to easily convert between `RuntimeAppPublic`, the wrapped +/// (generic = non application-specific) crypto types and the `Public` type required by the runtime. /// /// TODO [#5662] Potentially use `IsWrappedBy` types, or find some other way to make it easy to /// obtain unwrapped crypto (and wrap it back). @@ -659,3 +662,84 @@ pub trait SignedPayload: Encode { self.using_encoded(|payload| C::verify(payload, self.public(), signature)) } } + + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::{Test as TestRuntime, Call}; + use sp_runtime::testing::{UintAuthorityId, TestSignature, TestXt}; + + impl SigningTypes for TestRuntime { + type Public = UintAuthorityId; + type Signature = TestSignature; + } + + impl SendTransactionTypes for TestRuntime { + type Extrinsic = TestXt; + type OverarchingCall = Call; + } + + #[derive(codec::Encode, codec::Decode)] + struct SimplePayload { + pub public: UintAuthorityId, + pub data: Vec, + } + + impl SignedPayload for SimplePayload { + fn public(&self) -> UintAuthorityId { + self.public.clone() + } + } + + struct DummyAppCrypto; + // Bind together the `SigningTypes` with app-crypto and the wrapper types. + // here the implementation is pretty dummy, because we use the same type for + // both application-specific crypto and the runtime crypto, but in real-life + // runtimes it's going to use different types everywhere. + impl AppCrypto for DummyAppCrypto { + type RuntimeAppPublic = UintAuthorityId; + type GenericPublic = UintAuthorityId; + type GenericSignature = TestSignature; + } + + #[test] + fn should_send_unsigned_with_signed_payload_with_all_accounts() { + // given + UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]); + + // when + let result = Signer:: + ::all_accounts() + .send_unsigned_transaction( + |account| SimplePayload { + data: vec![1, 2, 3], + public: account.public.clone() + }, + |_payload, _signature| { + Call + } + ); + + // then + let mut res = result.into_iter(); + assert_eq!(res.next(), Some((Account { + index: 0, + id: 0xf0, + public: 0xf0.into(), + }, Ok(())))); + assert_eq!(res.next(), Some((Account { + index: 1, + id: 0xf1, + public: 0xf1.into(), + }, Ok(())))); + assert_eq!(res.next(), Some((Account { + index: 2, + id: 0xf2, + public: 0xf2.into(), + }, Ok(())))); + assert_eq!(res.next(), None); + + // TODO check txpool content + } +} diff --git a/primitives/runtime/src/generic/unchecked_extrinsic.rs b/primitives/runtime/src/generic/unchecked_extrinsic.rs index 3e9e52ba8beda..4a52d7390de3c 100644 --- a/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -321,29 +321,10 @@ mod tests { use super::*; use sp_io::hashing::blake2_256; use crate::codec::{Encode, Decode}; - use crate::traits::{SignedExtension, IdentifyAccount, IdentityLookup}; - use serde::{Serialize, Deserialize}; + use crate::traits::{SignedExtension, IdentityLookup}; + use crate::testing::TestSignature as TestSig; type TestContext = IdentityLookup; - - #[derive(Eq, PartialEq, Clone, Copy, Debug, Serialize, Deserialize, Encode, Decode)] - pub struct TestSigner(pub u64); - impl From for TestSigner { fn from(x: u64) -> Self { Self(x) } } - impl From for u64 { fn from(x: TestSigner) -> Self { x.0 } } - impl IdentifyAccount for TestSigner { - type AccountId = u64; - fn into_account(self) -> u64 { self.into() } - } - - #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Encode, Decode)] - struct TestSig(u64, Vec); - impl traits::Verify for TestSig { - type Signer = TestSigner; - fn verify>(&self, mut msg: L, signer: &u64) -> bool { - signer == &self.0 && msg.get() == &self.1[..] - } - } - type TestAccountId = u64; type TestCall = Vec; diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index 0e18f2c527aad..40b4e23e3fa0a 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -29,7 +29,12 @@ pub use sp_core::{H256, sr25519}; use sp_core::{crypto::{CryptoType, Dummy, key_types, Public}, U256}; use crate::transaction_validity::{TransactionValidity, TransactionValidityError, TransactionSource}; -/// Authority Id +/// A dummy type which can be used instead of regular cryptographic primitives. +/// +/// 1. Wraps a `u64` `AccountId` and is able to `IdentifyAccount`. +/// 2. Can be converted to any `Public` key. +/// 3. Implements `RuntimeAppPublic` so it can be used instead of regular application-specific +/// crypto. #[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug, Hash, Serialize, Deserialize, PartialOrd, Ord)] pub struct UintAuthorityId(pub u64); @@ -82,7 +87,7 @@ impl UintAuthorityId { impl sp_application_crypto::RuntimeAppPublic for UintAuthorityId { const ID: KeyTypeId = key_types::DUMMY; - type Signature = u64; + type Signature = TestSignature; fn all() -> Vec { ALL_KEYS.with(|l| l.borrow().clone()) @@ -94,25 +99,11 @@ impl sp_application_crypto::RuntimeAppPublic for UintAuthorityId { } fn sign>(&self, msg: &M) -> Option { - let mut signature = [0u8; 8]; - msg.as_ref().iter() - .chain(std::iter::repeat(&42u8)) - .take(8) - .enumerate() - .for_each(|(i, v)| { signature[i] = *v; }); - - Some(u64::from_le_bytes(signature)) + Some(TestSignature(self.0, msg.as_ref().to_vec())) } fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { - let mut msg_signature = [0u8; 8]; - msg.as_ref().iter() - .chain(std::iter::repeat(&42)) - .take(8) - .enumerate() - .for_each(|(i, v)| { msg_signature[i] = *v; }); - - u64::from_le_bytes(msg_signature) == *signature + traits::Verify::verify(signature, msg.as_ref(), &self.0) } fn to_raw_vec(&self) -> Vec { @@ -140,6 +131,26 @@ impl crate::BoundToRuntimeAppPublic for UintAuthorityId { type Public = Self; } +impl traits::IdentifyAccount for UintAuthorityId { + type AccountId = u64; + + fn into_account(self) -> Self::AccountId { + self.0 + } +} + +/// A dummy signature type, to match `UintAuthorityId`. +#[derive(Eq, PartialEq, Clone, Debug, Hash, Serialize, Deserialize, Encode, Decode)] +pub struct TestSignature(pub u64, pub Vec); + +impl traits::Verify for TestSignature { + type Signer = UintAuthorityId; + + fn verify>(&self, mut msg: L, signer: &u64) -> bool { + signer == &self.0 && msg.get() == &self.1[..] + } +} + /// Digest item pub type DigestItem = generic::DigestItem; @@ -332,6 +343,7 @@ impl Checkable for TestXt Result { Ok(self) } } + impl traits::Extrinsic for TestXt { type Call = Call; type SignaturePayload = (u64, Extra); From 19494c37e4e3b85aa505c7cb43bbfff291d0b829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 17 Apr 2020 16:53:36 +0200 Subject: [PATCH 105/108] Add some tests to account filtering. --- frame/system/src/offchain.rs | 199 +++++++++++++++++++++++++++++------ 1 file changed, 166 insertions(+), 33 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 8192ec100f92e..f96525085fa20 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -667,7 +667,9 @@ pub trait SignedPayload: Encode { #[cfg(test)] mod tests { use super::*; + use codec::Decode; use crate::tests::{Test as TestRuntime, Call}; + use sp_core::offchain::{testing, TransactionPoolExt}; use sp_runtime::testing::{UintAuthorityId, TestSignature, TestXt}; impl SigningTypes for TestRuntime { @@ -675,8 +677,10 @@ mod tests { type Signature = TestSignature; } + type Extrinsic = TestXt; + impl SendTransactionTypes for TestRuntime { - type Extrinsic = TestXt; + type Extrinsic = Extrinsic; type OverarchingCall = Call; } @@ -703,43 +707,172 @@ mod tests { type GenericSignature = TestSignature; } + fn assert_account( + next: Option<(Account, Result<(), ()>)>, + index: usize, + id: u64, + ) { + assert_eq!(next, Some((Account { + index, + id, + public: id.into(), + }, Ok(())))); + } + #[test] fn should_send_unsigned_with_signed_payload_with_all_accounts() { + let (pool, pool_state) = testing::TestTransactionPoolExt::new(); + + let mut t = sp_io::TestExternalities::default(); + t.register_extension(TransactionPoolExt::new(pool)); + // given UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]); - // when - let result = Signer:: - ::all_accounts() - .send_unsigned_transaction( - |account| SimplePayload { - data: vec![1, 2, 3], - public: account.public.clone() - }, - |_payload, _signature| { - Call - } - ); - - // then - let mut res = result.into_iter(); - assert_eq!(res.next(), Some((Account { - index: 0, - id: 0xf0, - public: 0xf0.into(), - }, Ok(())))); - assert_eq!(res.next(), Some((Account { - index: 1, - id: 0xf1, - public: 0xf1.into(), - }, Ok(())))); - assert_eq!(res.next(), Some((Account { - index: 2, - id: 0xf2, - public: 0xf2.into(), - }, Ok(())))); - assert_eq!(res.next(), None); + t.execute_with(|| { + // when + let result = Signer:: + ::all_accounts() + .send_unsigned_transaction( + |account| SimplePayload { + data: vec![1, 2, 3], + public: account.public.clone() + }, + |_payload, _signature| { + Call + } + ); + + // then + let mut res = result.into_iter(); + assert_account(res.next(), 0, 0xf0); + assert_account(res.next(), 1, 0xf1); + assert_account(res.next(), 2, 0xf2); + assert_eq!(res.next(), None); + + // check the transaction pool content: + let tx1 = pool_state.write().transactions.pop().unwrap(); + let _tx2 = pool_state.write().transactions.pop().unwrap(); + let _tx3 = pool_state.write().transactions.pop().unwrap(); + assert!(pool_state.read().transactions.is_empty()); + let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); + assert_eq!(tx1.signature, None); + }); + } + + #[test] + fn should_send_unsigned_with_signed_payload_with_any_account() { + let (pool, pool_state) = testing::TestTransactionPoolExt::new(); + + let mut t = sp_io::TestExternalities::default(); + t.register_extension(TransactionPoolExt::new(pool)); - // TODO check txpool content + // given + UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]); + + t.execute_with(|| { + // when + let result = Signer:: + ::any_account() + .send_unsigned_transaction( + |account| SimplePayload { + data: vec![1, 2, 3], + public: account.public.clone() + }, + |_payload, _signature| { + Call + } + ); + + // then + let mut res = result.into_iter(); + assert_account(res.next(), 0, 0xf0); + assert_eq!(res.next(), None); + + // check the transaction pool content: + let tx1 = pool_state.write().transactions.pop().unwrap(); + assert!(pool_state.read().transactions.is_empty()); + let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); + assert_eq!(tx1.signature, None); + }); } + + #[test] + fn should_send_unsigned_with_signed_payload_with_all_account_and_filter() { + let (pool, pool_state) = testing::TestTransactionPoolExt::new(); + + let mut t = sp_io::TestExternalities::default(); + t.register_extension(TransactionPoolExt::new(pool)); + + // given + UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]); + + t.execute_with(|| { + // when + let result = Signer:: + ::all_accounts() + .with_filter(vec![0xf2.into(), 0xf1.into()]) + .send_unsigned_transaction( + |account| SimplePayload { + data: vec![1, 2, 3], + public: account.public.clone() + }, + |_payload, _signature| { + Call + } + ); + + // then + let mut res = result.into_iter(); + assert_account(res.next(), 0, 0xf2); + assert_account(res.next(), 1, 0xf1); + assert_eq!(res.next(), None); + + // check the transaction pool content: + let tx1 = pool_state.write().transactions.pop().unwrap(); + let _tx2 = pool_state.write().transactions.pop().unwrap(); + assert!(pool_state.read().transactions.is_empty()); + let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); + assert_eq!(tx1.signature, None); + }); + } + + #[test] + fn should_send_unsigned_with_signed_payload_with_any_account_and_filter() { + let (pool, pool_state) = testing::TestTransactionPoolExt::new(); + + let mut t = sp_io::TestExternalities::default(); + t.register_extension(TransactionPoolExt::new(pool)); + + // given + UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]); + + t.execute_with(|| { + // when + let result = Signer:: + ::any_account() + .with_filter(vec![0xf2.into(), 0xf1.into()]) + .send_unsigned_transaction( + |account| SimplePayload { + data: vec![1, 2, 3], + public: account.public.clone() + }, + |_payload, _signature| { + Call + } + ); + + // then + let mut res = result.into_iter(); + assert_account(res.next(), 0, 0xf2); + assert_eq!(res.next(), None); + + // check the transaction pool content: + let tx1 = pool_state.write().transactions.pop().unwrap(); + assert!(pool_state.read().transactions.is_empty()); + let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); + assert_eq!(tx1.signature, None); + }); + } + } From 9bdbbc91d148696871df5b43aa9688a24e7e78b6 Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Fri, 17 Apr 2020 17:35:00 +0200 Subject: [PATCH 106/108] Remove code samples and point to example offchain worker --- frame/system/src/offchain.rs | 46 ++++++------------------------------ 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 8192ec100f92e..ae504ffbb64f4 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -25,15 +25,14 @@ //! //! ## Usage //! +//! Please refer to [`example-offchain-worker`](../../example-offchain-worker/index.html) for +//! a concrete example usage of this crate. +//! //! ### Submit a raw unsigned transaction //! //! To submit a raw unsigned transaction, [`SubmitTransaction`](./struct.SubmitTransaction.html) //! can be used. //! -//! ```rust -//! SubmitTransaction::>::submit_unsigned_transaction(call) -//! ``` -//! //! ### Signing transactions //! //! To be able to use signing, the following trait should be implemented: @@ -46,45 +45,14 @@ //! #### Submit an unsigned transaction with a signed payload //! //! Initially, a payload instance that implements the `SignedPayload` trait should be defined. -//! If we take the [`PricePayload`](../../example-offchain-worker/struct.PricePayload.html) -//! defined in the example-offchain-worker pallet, we see the following: -//! -//! ```rust -//! #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] -//! pub struct PricePayload { -//! block_number: BlockNumber, -//! price: u32, -//! public: Public, -//! } +//! See [`PricePayload`](../../example-offchain-worker/struct.PricePayload.html) //! -//! impl SignedPayload for PricePayload { -//! fn public(&self) -> T::Public { -//! self.public.clone() -//! } -//! } -//! ``` -//! -//! An object from the defined payload can then be signed and submitted onchain. -//! -//! ``` -//! Signer::::all_accounts().send_unsigned_transaction( -//! |account| PricePayload { -//! price, -//! block_number, -//! public: account.public.clone() -//! }, -//! |payload, signature| { -//! Call::submit_price_unsigned_with_signed_payload(payload, signature) -//! } -//! ) -//! ``` +//! The payload type that is defined defined can then be signed and submitted onchain. //! //! #### Submit a signed transaction //! -//! ``` -//! Signer::::all_accounts().send_signed_transaction( -//! |account| Call::submit_price(price) -//! ); +//! [`Signer`](./struct.Signer.html) can be used to sign/verify payloads +//! #![warn(missing_docs)] From 2436ca71e8cc93e4131d8c11f9fa877c658d2d6c Mon Sep 17 00:00:00 2001 From: Rakan Alhneiti Date: Fri, 17 Apr 2020 17:48:44 +0200 Subject: [PATCH 107/108] Fix doc links --- frame/example-offchain-worker/src/lib.rs | 8 ++++---- frame/system/src/offchain.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index 085257ba4518a..d2ebd1159e228 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -22,12 +22,12 @@ //! Run `cargo doc --package pallet-example-offchain-worker --open` to view this module's //! documentation. //! -//! - \[`pallet_example_offchain_worker::Trait`](./trait.Trait.html) -//! - \[`Call`](./enum.Call.html) -//! - \[`Module`](./struct.Module.html) +//! - [`pallet_example_offchain_worker::Trait`](./trait.Trait.html) +//! - [`Call`](./enum.Call.html) +//! - [`Module`](./struct.Module.html) //! //! -//! \## Overview +//! ## Overview //! //! In this example we are going to build a very simplistic, naive and definitely NOT //! production-ready oracle for BTC/USD price. diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index a332195fa7910..04cd3001e43a3 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -25,7 +25,7 @@ //! //! ## Usage //! -//! Please refer to [`example-offchain-worker`](../../example-offchain-worker/index.html) for +//! Please refer to [`example-offchain-worker`](../../pallet_example_offchain_worker/index.html) for //! a concrete example usage of this crate. //! //! ### Submit a raw unsigned transaction @@ -45,7 +45,7 @@ //! #### Submit an unsigned transaction with a signed payload //! //! Initially, a payload instance that implements the `SignedPayload` trait should be defined. -//! See [`PricePayload`](../../example-offchain-worker/struct.PricePayload.html) +//! See [`PricePayload`](../../pallet_example_offchain_worker/struct.PricePayload.html) //! //! The payload type that is defined defined can then be signed and submitted onchain. //! From dec24909ac9ce1e39c100bfb68f8c0ab305bbaaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 20 Apr 2020 16:39:50 +0200 Subject: [PATCH 108/108] Fix im-online tests using signatures. --- frame/im-online/src/lib.rs | 5 +++++ frame/im-online/src/mock.rs | 2 ++ frame/im-online/src/tests.rs | 10 +++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 1c123b956184f..813b11bbc9360 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -574,6 +574,11 @@ impl Module { Keys::::put(keys); } } + + #[cfg(test)] + fn set_keys(keys: Vec) { + Keys::::put(&keys) + } } impl sp_runtime::BoundToRuntimeAppPublic for Module { diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 9454db1aa231f..e9b5ef95ef4a0 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -190,5 +190,7 @@ pub fn advance_session() { let now = System::block_number().max(1); System::set_block_number(now + 1); Session::rotate_session(); + let keys = Session::validators().into_iter().map(UintAuthorityId).collect(); + ImOnline::set_keys(keys); assert_eq!(Session::current_index(), (now / Period::get()) as u32); } diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 06125bd0530fc..e49f28f4896e0 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -61,15 +61,15 @@ fn should_report_offline_validators() { let block = 1; System::set_block_number(block); // buffer new validators - Session::rotate_session(); + advance_session(); // enact the change and buffer another one let validators = vec![1, 2, 3, 4, 5, 6]; VALIDATORS.with(|l| *l.borrow_mut() = Some(validators.clone())); - Session::rotate_session(); + advance_session(); // when // we end current session and start the next one - Session::rotate_session(); + advance_session(); // then let offences = OFFENCES.with(|l| l.replace(vec![])); @@ -89,7 +89,7 @@ fn should_report_offline_validators() { for (idx, v) in validators.into_iter().take(4).enumerate() { let _ = heartbeat(block, 3, idx as u32, v.into()).unwrap(); } - Session::rotate_session(); + advance_session(); // then let offences = OFFENCES.with(|l| l.replace(vec![])); @@ -315,7 +315,7 @@ fn should_not_send_a_report_if_already_online() { ImOnline::note_uncle(3, 0); // when - UintAuthorityId::set_all_keys(vec![0]); // all authorities use pallet_session key 0 + UintAuthorityId::set_all_keys(vec![1, 2, 3]); // we expect error, since the authority is already online. let mut res = ImOnline::send_heartbeats(4).unwrap(); assert_eq!(res.next().unwrap().unwrap(), ());