diff --git a/core/sr-primitives/src/generic/mod.rs b/core/sr-primitives/src/generic/mod.rs
index 1511753d2c524..0138a15aee21a 100644
--- a/core/sr-primitives/src/generic/mod.rs
+++ b/core/sr-primitives/src/generic/mod.rs
@@ -27,7 +27,7 @@ mod digest;
#[cfg(test)]
mod tests;
-pub use self::unchecked_extrinsic::UncheckedExtrinsic;
+pub use self::unchecked_extrinsic::{UncheckedExtrinsic, SignedPayload};
pub use self::era::{Era, Phase};
pub use self::checked_extrinsic::CheckedExtrinsic;
pub use self::header::Header;
diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs
index cb9330cfaaff0..aebf80edc63ec 100644
--- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs
+++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs
@@ -72,12 +72,22 @@ impl
Extrinsic
{
type Call = Call;
+ type SignaturePayload = (
+ Address,
+ Signature,
+ Extra,
+ );
+
fn is_signed(&self) -> Option {
Some(self.signature.is_some())
}
- fn new_unsigned(function: Call) -> Option {
- Some(UncheckedExtrinsic::new_unsigned(function))
+ fn new(function: Call, signed_data: Option) -> Option {
+ Some(if let Some((address, signature, extra)) = signed_data {
+ UncheckedExtrinsic::new_signed(function, address, signature, extra)
+ } else {
+ UncheckedExtrinsic::new_unsigned(function)
+ })
}
}
@@ -98,21 +108,18 @@ where
fn check(self, lookup: &Lookup) -> Result {
Ok(match self.signature {
Some((signed, signature, extra)) => {
- let additional_signed = extra.additional_signed()?;
- let raw_payload = (self.function, extra, additional_signed);
let signed = lookup.lookup(signed)?;
+ let raw_payload = SignedPayload::new(self.function, extra)?;
if !raw_payload.using_encoded(|payload| {
- if payload.len() > 256 {
- signature.verify(&blake2_256(payload)[..], &signed)
- } else {
- signature.verify(payload, &signed)
- }
+ signature.verify(payload, &signed)
}) {
return Err(crate::BAD_SIGNATURE)
}
+
+ let (function, extra, _) = raw_payload.deconstruct();
CheckedExtrinsic {
- signed: Some((signed, raw_payload.1)),
- function: raw_payload.0,
+ signed: Some((signed, extra)),
+ function,
}
}
None => CheckedExtrinsic {
@@ -123,6 +130,59 @@ where
}
}
+/// A payload that has been signed for an unchecked extrinsics.
+///
+/// Note that the payload that we sign to produce unchecked extrinsic signature
+/// is going to be different than the `SignaturePayload` - so the thing the extrinsic
+/// actually contains.
+pub struct SignedPayload((
+ Call,
+ Extra,
+ Extra::AdditionalSigned,
+));
+
+impl SignedPayload where
+ Call: Encode,
+ Extra: SignedExtension,
+{
+ /// Create new `SignedPayload`.
+ ///
+ /// This function may fail if `additional_signed` of `Extra` is not available.
+ pub fn new(call: Call, extra: Extra) -> Result {
+ let additional_signed = extra.additional_signed()?;
+ let raw_payload = (call, extra, additional_signed);
+ Ok(Self(raw_payload))
+ }
+
+ /// Create new `SignedPayload` from raw components.
+ pub fn from_raw(call: Call, extra: Extra, additional_signed: Extra::AdditionalSigned) -> Self {
+ Self((call, extra, additional_signed))
+ }
+
+ /// Deconstruct the payload into it's components.
+ pub fn deconstruct(self) -> (Call, Extra, Extra::AdditionalSigned) {
+ self.0
+ }
+}
+
+impl Encode for SignedPayload where
+ Call: Encode,
+ Extra: SignedExtension,
+{
+ /// Get an encoded version of this payload.
+ ///
+ /// Payloads longer than 256 bytes are going to be `blake2_256`-hashed.
+ fn using_encoded R>(&self, f: F) -> R {
+ self.0.using_encoded(|payload| {
+ if payload.len() > 256 {
+ f(&blake2_256(payload)[..])
+ } else {
+ f(payload)
+ }
+ })
+ }
+}
+
impl Decode
for UncheckedExtrinsic
where
diff --git a/core/sr-primitives/src/lib.rs b/core/sr-primitives/src/lib.rs
index 41dac41f79b35..454f1cdfb8106 100644
--- a/core/sr-primitives/src/lib.rs
+++ b/core/sr-primitives/src/lib.rs
@@ -847,12 +847,7 @@ impl<'a> ::serde::Deserialize<'a> for OpaqueExtrinsic {
impl traits::Extrinsic for OpaqueExtrinsic {
type Call = ();
-
- fn is_signed(&self) -> Option {
- None
- }
-
- fn new_unsigned(_call: Self::Call) -> Option { None }
+ type SignaturePayload = ();
}
#[cfg(test)]
diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs
index c91c8366c1123..ed5ac7fa45675 100644
--- a/core/sr-primitives/src/testing.rs
+++ b/core/sr-primitives/src/testing.rs
@@ -187,6 +187,7 @@ pub struct ExtrinsicWrapper(Xt);
impl traits::Extrinsic for ExtrinsicWrapper {
type Call = ();
+ type SignaturePayload = ();
fn is_signed(&self) -> Option {
None
@@ -274,13 +275,14 @@ impl Checkable for TestXt traits::Extrinsic for TestXt {
type Call = Call;
+ type SignaturePayload = (u64, Extra);
fn is_signed(&self) -> Option {
Some(self.0.is_some())
}
- fn new_unsigned(_c: Call) -> Option {
- None
+ fn new(c: Call, sig: Option) -> Option {
+ Some(TestXt(sig, c))
}
}
diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs
index 3dc1649e495d4..fe3d67268f117 100644
--- a/core/sr-primitives/src/traits.rs
+++ b/core/sr-primitives/src/traits.rs
@@ -743,13 +743,24 @@ pub trait Extrinsic: Sized {
/// The function call.
type Call;
+ /// The payload we carry for signed extrinsics.
+ ///
+ /// Usually it will contain a `Signature` and
+ /// may include some additional data that are specific to signed
+ /// extrinsics.
+ type SignaturePayload;
+
/// Is this `Extrinsic` signed?
/// If no information are available about signed/unsigned, `None` should be returned.
fn is_signed(&self) -> Option { None }
- /// New instance of an unsigned extrinsic aka "inherent". `None` if this is an opaque
- /// extrinsic type.
- fn new_unsigned(_call: Self::Call) -> Option { None }
+ /// Create new instance of the extrinsic.
+ ///
+ /// Extrinsics can be split into:
+ /// 1. Inherents (no signature; created by validators during block production)
+ /// 2. Unsigned Transactions (no signature; represent "system calls" or other special kinds of calls)
+ /// 3. Signed Transactions (with signature; a regular transactions with known origin)
+ fn new(_call: Self::Call, _signed_data: Option) -> Option { None }
}
/// Extract the hashing type for a block.
diff --git a/core/test-runtime/src/lib.rs b/core/test-runtime/src/lib.rs
index 55b6ad9b5fe14..f023fd89e137d 100644
--- a/core/test-runtime/src/lib.rs
+++ b/core/test-runtime/src/lib.rs
@@ -141,6 +141,7 @@ impl BlindCheckable for Extrinsic {
impl ExtrinsicT for Extrinsic {
type Call = Extrinsic;
+ type SignaturePayload = ();
fn is_signed(&self) -> Option {
if let Extrinsic::IncludeData(_) = *self {
@@ -150,7 +151,7 @@ impl ExtrinsicT for Extrinsic {
}
}
- fn new_unsigned(call: Self::Call) -> Option {
+ fn new(call: Self::Call, _signature_payload: Option) -> Option {
Some(call)
}
}
diff --git a/node/cli/src/service.rs b/node/cli/src/service.rs
index 01cad95c4678f..68b8d63fe0b18 100644
--- a/node/cli/src/service.rs
+++ b/node/cli/src/service.rs
@@ -289,7 +289,7 @@ mod tests {
crypto::Pair as CryptoPair, blake2_256,
sr25519::Public as AddressPublic, H256,
};
- use sr_primitives::{generic::{BlockId, Era, Digest}, traits::Block, OpaqueExtrinsic};
+ use sr_primitives::{generic::{BlockId, Era, Digest, SignedPayload}, traits::Block, OpaqueExtrinsic};
use timestamp;
use finality_tracker;
use keyring::AccountKeyring;
@@ -462,15 +462,17 @@ mod tests {
let check_weight = system::CheckWeight::new();
let take_fees = balances::TakeFees::from(0);
let extra = (check_version, check_genesis, check_era, check_nonce, check_weight, take_fees);
-
- let raw_payload = (function, extra.clone(), version, genesis_hash, genesis_hash);
- let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 {
- signer.sign(&blake2_256(payload)[..])
- } else {
+ let raw_payload = SignedPayload::from_raw(
+ function,
+ extra,
+ (version, genesis_hash, genesis_hash, (), (), ())
+ );
+ let signature = raw_payload.using_encoded(|payload| {
signer.sign(payload)
});
+ let (function, extra, _) = raw_payload.deconstruct();
let xt = UncheckedExtrinsic::new_signed(
- raw_payload.0,
+ function,
from.into(),
signature.into(),
extra,
diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs
index 6fec9abe7afdd..5dc8d02eccde7 100644
--- a/node/runtime/src/lib.rs
+++ b/node/runtime/src/lib.rs
@@ -39,7 +39,7 @@ use sr_primitives::{ApplyResult, impl_opaque_keys, generic, create_runtime_str,
use sr_primitives::transaction_validity::TransactionValidity;
use sr_primitives::weights::Weight;
use sr_primitives::traits::{
- BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup,
+ self, BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, SaturatedConversion,
};
use version::RuntimeVersion;
use elections::VoteIndex;
@@ -48,6 +48,7 @@ use version::NativeVersion;
use primitives::OpaqueMetadata;
use grandpa::{AuthorityId as GrandpaId, AuthorityWeight as GrandpaWeight};
use im_online::sr25519::{AuthorityId as ImOnlineId};
+use system::offchain::TransactionSubmitter;
#[cfg(any(feature = "std", test))]
pub use sr_primitives::BuildStorage;
@@ -80,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 154,
- impl_version: 157,
+ impl_version: 158,
apis: RUNTIME_API_VERSIONS,
};
@@ -392,11 +393,13 @@ impl sudo::Trait for Runtime {
type Proposal = Call;
}
+type SubmitTransaction = TransactionSubmitter;
+
impl im_online::Trait for Runtime {
type AuthorityId = ImOnlineId;
type Call = Call;
type Event = Event;
- type UncheckedExtrinsic = UncheckedExtrinsic;
+ type SubmitTransaction = SubmitTransaction;
type ReportUnresponsiveness = Offences;
type CurrentElectedSet = staking::CurrentElectedStashAccounts;
}
@@ -424,6 +427,33 @@ impl finality_tracker::Trait for Runtime {
type ReportLatency = ReportLatency;
}
+impl system::offchain::CreateTransaction for Runtime {
+ type Signature = Signature;
+
+ fn create_transaction>(
+ call: Call,
+ account: AccountId,
+ index: Index,
+ ) -> Option<(Call, ::SignaturePayload)> {
+ let period = 1 << 8;
+ let current_block = System::block_number().saturated_into::();
+ let tip = 0;
+ let extra: SignedExtra = (
+ system::CheckVersion::::new(),
+ system::CheckGenesis::::new(),
+ system::CheckEra::::from(generic::Era::mortal(period, current_block)),
+ system::CheckNonce::::from(index),
+ system::CheckWeight::::new(),
+ balances::TakeFees::::from(tip),
+ );
+ let raw_payload = SignedPayload::new(call, extra).ok()?;
+ let signature = F::sign(account.clone(), &raw_payload)?;
+ let address = Indices::unlookup(account);
+ let (call, extra, _) = raw_payload.deconstruct();
+ Some((call, (address, signature, extra)))
+ }
+}
+
construct_runtime!(
pub enum Runtime where
Block = Block,
@@ -475,6 +505,8 @@ pub type SignedExtra = (
);
/// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic;
+/// The payload being signed in transactions.
+pub type SignedPayload = generic::SignedPayload;
/// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic;
/// Executive: handles dispatch to the various modules.
@@ -609,3 +641,28 @@ impl_runtime_apis! {
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use sr_primitives::app_crypto::RuntimeAppPublic;
+ use system::offchain::SubmitSignedTransaction;
+
+ fn is_submit_signed_transaction(_arg: T) where
+ T: SubmitSignedTransaction<
+ Runtime,
+ Call,
+ Extrinsic=UncheckedExtrinsic,
+ CreateTransaction=Runtime,
+ Signer=Signer,
+ >,
+ Signer: RuntimeAppPublic + From,
+ Signer::Signature: Into,
+ {}
+
+ #[test]
+ fn validate_bounds() {
+ let x = SubmitTransaction::default();
+ is_submit_signed_transaction(x);
+ }
+}
diff --git a/srml/authority-discovery/src/lib.rs b/srml/authority-discovery/src/lib.rs
index ffcb6672c8662..74b1c6172f869 100644
--- a/srml/authority-discovery/src/lib.rs
+++ b/srml/authority-discovery/src/lib.rs
@@ -194,7 +194,11 @@ mod tests {
type AuthorityId = AuthorityId;
type Call = im_online::Call;
type Event = ();
- type UncheckedExtrinsic = UncheckedExtrinsic<(), im_online::Call, (), ()>;
+ type SubmitTransaction = system::offchain::TransactionSubmitter<
+ (),
+ im_online::Call,
+ UncheckedExtrinsic<(), im_online::Call, (), ()>,
+ >;
type ReportUnresponsiveness = ();
type CurrentElectedSet = DummyCurrentElectedSet;
}
diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs
index e37ce5c4de1c6..289dfbf93f2d7 100644
--- a/srml/balances/src/lib.rs
+++ b/srml/balances/src/lib.rs
@@ -1187,7 +1187,6 @@ pub struct TakeFees, I: Instance = DefaultInstance>(#[codec(compact)
impl, I: Instance> TakeFees {
/// utility constructor. Used only in client/factory code.
- #[cfg(feature = "std")]
pub fn from(fee: T::Balance) -> Self {
Self(fee)
}
diff --git a/srml/im-online/src/lib.rs b/srml/im-online/src/lib.rs
index fe604ca788fa4..c8494ef633ac8 100644
--- a/srml/im-online/src/lib.rs
+++ b/srml/im-online/src/lib.rs
@@ -75,7 +75,7 @@ use session::historical::IdentificationTuple;
use sr_io::Printable;
use sr_primitives::{
Perbill, ApplyError,
- traits::{Convert, Extrinsic as ExtrinsicT, Member},
+ traits::{Convert, Member},
transaction_validity::{TransactionValidity, TransactionLongevity, ValidTransaction},
};
use sr_staking_primitives::{
@@ -87,11 +87,18 @@ use srml_support::{
Parameter, StorageValue, StorageDoubleMap,
};
use system::ensure_none;
+use system::offchain::SubmitUnsignedTransaction;
pub mod sr25519 {
mod app_sr25519 {
use app_crypto::{app_crypto, key_types::IM_ONLINE, sr25519};
app_crypto!(sr25519, IM_ONLINE);
+
+ impl From for sr_primitives::AnySignature {
+ fn from(sig: Signature) -> Self {
+ sr25519::Signature::from(sig).into()
+ }
+ }
}
/// An i'm online keypair using sr25519 as its crypto.
@@ -109,6 +116,12 @@ pub mod ed25519 {
mod app_ed25519 {
use app_crypto::{app_crypto, key_types::IM_ONLINE, ed25519};
app_crypto!(ed25519, IM_ONLINE);
+
+ impl From for sr_primitives::AnySignature {
+ fn from(sig: Signature) -> Self {
+ ed25519::Signature::from(sig).into()
+ }
+ }
}
/// An i'm online keypair using ed25519 as its crypto.
@@ -141,7 +154,6 @@ struct WorkerStatus {
// Error which may occur while executing the off-chain code.
enum OffchainErr {
DecodeWorkerStatus,
- ExtrinsicCreation,
FailedSigning,
NetworkState,
SubmitTransaction,
@@ -151,7 +163,6 @@ impl Printable for OffchainErr {
fn print(self) {
match self {
OffchainErr::DecodeWorkerStatus => print("Offchain error: decoding WorkerStatus failed!"),
- OffchainErr::ExtrinsicCreation => print("Offchain error: extrinsic creation failed!"),
OffchainErr::FailedSigning => print("Offchain error: signing failed!"),
OffchainErr::NetworkState => print("Offchain error: fetching network state failed!"),
OffchainErr::SubmitTransaction => print("Offchain error: submitting transaction failed!"),
@@ -183,9 +194,8 @@ pub trait Trait: system::Trait + session::historical::Trait {
/// A dispatchable call type.
type Call: From>;
- /// A extrinsic right from the external world. This is unchecked and so
- /// can contain a signature.
- type UncheckedExtrinsic: ExtrinsicT::Call> + Encode + Decode;
+ /// A transaction submitter.
+ type SubmitTransaction: SubmitUnsignedTransaction::Call>;
/// A type that gives us the ability to submit unresponsiveness offence reports.
type ReportUnresponsiveness:
@@ -340,9 +350,8 @@ impl Module {
let signature = key.sign(&heartbeat_data.encode()).ok_or(OffchainErr::FailedSigning)?;
let call = Call::heartbeat(heartbeat_data, signature);
- let ex = T::UncheckedExtrinsic::new_unsigned(call.into())
- .ok_or(OffchainErr::ExtrinsicCreation)?;
- sr_io::submit_transaction(&ex).map_err(|_| OffchainErr::SubmitTransaction)?;
+ T::SubmitTransaction::submit_unsigned(call)
+ .map_err(|_| OffchainErr::SubmitTransaction)?;
// once finished we set the worker status without comparing
// if the existing value changed in the meantime. this is
diff --git a/srml/support/src/inherent.rs b/srml/support/src/inherent.rs
index 1b6d8fbdd7ed9..935d3b4e74e4f 100644
--- a/srml/support/src/inherent.rs
+++ b/srml/support/src/inherent.rs
@@ -55,14 +55,16 @@ macro_rules! impl_outer_inherent {
fn create_extrinsics(&self) ->
$crate::inherent::Vec<<$block as $crate::inherent::BlockT>::Extrinsic> {
use $crate::inherent::ProvideInherent;
+ use $crate::inherent::Extrinsic;
let mut inherents = Vec::new();
$(
if let Some(inherent) = $module::create_inherent(self) {
- inherents.push($uncheckedextrinsic::new_unsigned(
- Call::$call(inherent))
- );
+ inherents.push($uncheckedextrinsic::new(
+ Call::$call(inherent),
+ None,
+ ).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return `Some`; qed"));
}
)*
diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs
index 91959f43e4e86..133731c8bd6cf 100644
--- a/srml/system/src/lib.rs
+++ b/srml/system/src/lib.rs
@@ -121,6 +121,8 @@ use runtime_io::{twox_128, TestExternalities, Blake2Hasher};
#[cfg(any(feature = "std", test))]
use primitives::ChangesTrieConfiguration;
+pub mod offchain;
+
/// Handler for when a new account has been created.
pub trait OnNewAccount {
/// A new account `who` has been registered.
@@ -443,7 +445,7 @@ decl_storage! {
}
}
-pub struct EnsureRoot(::rstd::marker::PhantomData);
+pub struct EnsureRoot(rstd::marker::PhantomData);
impl<
O: Into, O>> + From>,
AccountId,
@@ -457,7 +459,7 @@ impl<
}
}
-pub struct EnsureSigned(::rstd::marker::PhantomData);
+pub struct EnsureSigned(rstd::marker::PhantomData);
impl<
O: Into, O>> + From>,
AccountId,
@@ -471,7 +473,7 @@ impl<
}
}
-pub struct EnsureSignedBy(::rstd::marker::PhantomData<(Who, AccountId)>);
+pub struct EnsureSignedBy(rstd::marker::PhantomData<(Who, AccountId)>);
impl<
O: Into, O>> + From>,
Who: Contains,
@@ -486,7 +488,7 @@ impl<
}
}
-pub struct EnsureNone(::rstd::marker::PhantomData);
+pub struct EnsureNone(rstd::marker::PhantomData);
impl<
O: Into, O>> + From>,
AccountId,
@@ -500,7 +502,7 @@ impl<
}
}
-pub struct EnsureNever(::rstd::marker::PhantomData);
+pub struct EnsureNever(rstd::marker::PhantomData);
impl EnsureOrigin for EnsureNever {
type Success = T;
fn try_origin(o: O) -> Result {
@@ -894,8 +896,7 @@ impl CheckWeight {
}
}
- /// Utility constructor for tests and client code.
- #[cfg(feature = "std")]
+ /// Creates new `SignedExtension` to check weight of the extrinsic.
pub fn new() -> Self {
Self(PhantomData)
}
@@ -950,7 +951,6 @@ impl rstd::fmt::Debug for CheckWeight {
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
pub struct CheckNonce(#[codec(compact)] T::Index);
-#[cfg(feature = "std")]
impl CheckNonce {
/// utility constructor. Used only in client/factory code.
pub fn from(nonce: T::Index) -> Self {
@@ -1024,7 +1024,6 @@ impl SignedExtension for CheckNonce {
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
pub struct CheckEra((Era, rstd::marker::PhantomData));
-#[cfg(feature = "std")]
impl CheckEra {
/// utility constructor. Used only in client/factory code.
pub fn from(era: Era) -> Self {
@@ -1079,10 +1078,10 @@ impl rstd::fmt::Debug for CheckGenesis {
}
}
-#[cfg(feature = "std")]
impl CheckGenesis {
+ /// Creates new `SignedExtension` to check genesis hash.
pub fn new() -> Self {
- Self(std::marker::PhantomData)
+ Self(rstd::marker::PhantomData)
}
}
@@ -1108,10 +1107,10 @@ impl rstd::fmt::Debug for CheckVersion {
}
}
-#[cfg(feature = "std")]
impl CheckVersion {
+ /// Create new `SignedExtension` to check runtime version.
pub fn new() -> Self {
- Self(std::marker::PhantomData)
+ Self(rstd::marker::PhantomData)
}
}
@@ -1126,10 +1125,10 @@ impl SignedExtension for CheckVersion {
}
}
-pub struct ChainContext(::rstd::marker::PhantomData);
+pub struct ChainContext(rstd::marker::PhantomData);
impl Default for ChainContext {
fn default() -> Self {
- ChainContext(::rstd::marker::PhantomData)
+ ChainContext(rstd::marker::PhantomData)
}
}
diff --git a/srml/system/src/offchain.rs b/srml/system/src/offchain.rs
new file mode 100644
index 0000000000000..d67598f64fe02
--- /dev/null
+++ b/srml/system/src/offchain.rs
@@ -0,0 +1,135 @@
+// Copyright 2019 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 .
+
+//! Module helpers for offchain calls.
+
+use codec::Encode;
+use sr_primitives::app_crypto::RuntimeAppPublic;
+use sr_primitives::traits::Extrinsic as ExtrinsicT;
+
+/// A trait responsible for signing a payload using given account.
+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.
+ fn sign(account: Account, payload: &Payload) -> Option;
+}
+
+impl Signer for AppPublic where
+ AppPublic: RuntimeAppPublic + From,
+ AppPublic::Signature: Into,
+{
+ fn sign(account: Account, raw_payload: &Payload) -> Option {
+ raw_payload.using_encoded(|payload| {
+ AppPublic::from(account).sign(&payload).map(Into::into)
+ })
+ }
+}
+
+/// Creates runtime-specific signed transaction.
+pub trait CreateTransaction {
+ type Signature;
+
+ /// 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,
+ account: T::AccountId,
+ nonce: T::Index,
+ ) -> Option<(Extrinsic::Call, Extrinsic::SignaturePayload)>;
+}
+
+/// A trait to sign and submit transactions in offchain calls.
+pub trait SubmitSignedTransaction {
+ /// 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<
+ T::AccountId,
+ >::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, id: T::AccountId) -> Result<(), ()> {
+ let call = call.into();
+ let expected = >::account_nonce(&id);
+ let (call, signature_data) = Self::CreateTransaction
+ ::create_transaction::(call, id, expected)
+ .ok_or(())?;
+ let xt = Self::Extrinsic::new(call, Some(signature_data)).ok_or(())?;
+ runtime_io::submit_transaction(&xt)
+ }
+}
+
+/// A trait to submit unsigned transactions in offchain calls.
+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(())?;
+ runtime_io::submit_transaction(&xt)
+ }
+}
+
+/// A default type used to submit transactions to the pool.
+pub struct TransactionSubmitter {
+ _signer: rstd::marker::PhantomData<(S, C, E)>,
+}
+
+impl Default for TransactionSubmitter {
+ fn default() -> Self {
+ Self {
+ _signer: Default::default(),
+ }
+ }
+}
+
+/// A blanket implementation to simplify creation of transaction signer & submitter in the runtime.
+impl SubmitSignedTransaction for TransactionSubmitter where
+ T: crate::Trait,
+ C: CreateTransaction,
+ S: Signer>::Signature>,
+ E: ExtrinsicT + codec::Encode,
+{
+ type Extrinsic = E;
+ type CreateTransaction = C;
+ type Signer = S;
+}
+
+/// A blanket impl to use the same submitter for usigned transactions as well.
+impl SubmitUnsignedTransaction for TransactionSubmitter where
+ T: crate::Trait,
+ E: ExtrinsicT + codec::Encode,
+{
+ type Extrinsic = E;
+}
diff --git a/subkey/src/main.rs b/subkey/src/main.rs
index ba9d1a6cce059..e0ac2cb8b6b0f 100644
--- a/subkey/src/main.rs
+++ b/subkey/src/main.rs
@@ -29,7 +29,7 @@ use primitives::{
use codec::{Encode, Decode};
use sr_primitives::generic::Era;
use node_primitives::{Balance, Index, Hash};
-use node_runtime::{Call, UncheckedExtrinsic, BalancesCall, Runtime};
+use node_runtime::{Call, UncheckedExtrinsic, SignedPayload, BalancesCall, Runtime, VERSION};
mod vanity;
@@ -182,22 +182,21 @@ fn execute(matches: clap::ArgMatches) where
println!("Using a genesis hash of {}", HexDisplay::from(&genesis_hash.as_ref()));
- let raw_payload = (
+ let raw_payload = SignedPayload::from_raw(
function,
extra(index, 0),
- (&genesis_hash, &genesis_hash),
+ (VERSION.spec_version as u32, genesis_hash, genesis_hash, (), (), ()),
);
- let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 {
- signer.sign(&blake2_256(payload)[..])
- } else {
+ let signature = raw_payload.using_encoded(|payload| {
println!("Signing {}", HexDisplay::from(&payload));
signer.sign(payload)
});
+ let (function, extra, _) = raw_payload.deconstruct();
let extrinsic = UncheckedExtrinsic::new_signed(
- raw_payload.0,
+ function,
signer.public().into(),
signature.into(),
- extra(index, 0),
+ extra,
);
println!("0x{}", hex::encode(&extrinsic.encode()));
}