1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// 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 <http://www.gnu.org/licenses/>.

//! Module helpers for offchain calls.

use codec::Encode;
use sp_runtime::app_crypto::{self, RuntimeAppPublic};
use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount};

/// A trait responsible for signing a payload using given account.
pub trait Signer<Public, Signature> {
	/// 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<Payload: Encode>(public: Public, payload: &Payload) -> Option<Signature>;
}

/// A `Signer` implementation for any `AppPublic` type.
///
/// This implementation additionaly supports conversion to/from multi-signature/multi-signer
/// wrappers.
/// If the wrapped crypto doesn't match `AppPublic`s crypto `None` is returned.
impl<Public, Signature, AppPublic> Signer<Public, Signature> for AppPublic where
	AppPublic: RuntimeAppPublic
		+ app_crypto::AppPublic
		+ From<<AppPublic as app_crypto::AppPublic>::Generic>,
	<AppPublic as RuntimeAppPublic>::Signature: app_crypto::AppSignature,
	Signature: From<
		<<AppPublic as RuntimeAppPublic>::Signature as app_crypto::AppSignature>::Generic
	>,
	Public: sp_std::convert::TryInto<<AppPublic as app_crypto::AppPublic>::Generic>
{
	fn sign<Payload: Encode>(public: Public, raw_payload: &Payload) -> Option<Signature> {
		raw_payload.using_encoded(|payload| {
			let public = public.try_into().ok()?;
			AppPublic::from(public).sign(&payload)
				.map(
					<<AppPublic as RuntimeAppPublic>::Signature as app_crypto::AppSignature>
					 ::Generic::from
				)
				.map(Signature::from)
		})
	}
}

/// Creates a runtime-specific signed transaction.
pub trait CreateTransaction<T: crate::Trait, Extrinsic: ExtrinsicT> {
	/// A `Public` key representing a particular `AccountId`.
	type Public: IdentifyAccount<AccountId=T::AccountId> + Clone;
	/// A `Signature` generated by the `Signer`.
	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<F: Signer<Self::Public, Self::Signature>>(
		call: Extrinsic::Call,
		public: Self::Public,
		account: T::AccountId,
		nonce: T::Index,
	) -> Option<(Extrinsic::Call, Extrinsic::SignaturePayload)>;
}

type PublicOf<T, Call, X> = <
	<X as SubmitSignedTransaction<T, Call>>::CreateTransaction as CreateTransaction<
		T,
		<X as SubmitSignedTransaction<T, Call>>::Extrinsic,
	>
>::Public;

/// A trait to sign and submit transactions in offchain calls.
pub trait SubmitSignedTransaction<T: crate::Trait, Call> {
	/// Unchecked extrinsic type.
	type Extrinsic: ExtrinsicT<Call=Call> + codec::Encode;

	/// A runtime-specific type to produce signed data for the extrinsic.
	type CreateTransaction: CreateTransaction<T, Self::Extrinsic>;

	/// A type used to sign transactions created using `CreateTransaction`.
	type Signer: Signer<
		PublicOf<T, Call, Self>,
		<Self::CreateTransaction as CreateTransaction<T, Self::Extrinsic>>::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<Call>, public: PublicOf<T, Call, Self>) -> Result<(), ()> {
		let call = call.into();
		let id = public.clone().into_account();
		let expected = <crate::Module<T>>::account_nonce(&id);
		let (call, signature_data) = Self::CreateTransaction
			::create_transaction::<Self::Signer>(call, public, id, expected)
			.ok_or(())?;
		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.
pub trait SubmitUnsignedTransaction<T: crate::Trait, Call> {
	/// Unchecked extrinsic type.
	type Extrinsic: ExtrinsicT<Call=Call> + 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<Call>) -> Result<(), ()> {
		let xt = Self::Extrinsic::new(call.into(), None).ok_or(())?;
		sp_io::offchain::submit_transaction(xt.encode())
	}
}

/// A default type used to submit transactions to the pool.
pub struct TransactionSubmitter<S, C, E> {
	_signer: sp_std::marker::PhantomData<(S, C, E)>,
}

impl<S, C, E> Default for TransactionSubmitter<S, C, E> {
	fn default() -> Self {
		Self {
			_signer: Default::default(),
		}
	}
}

/// A blanket implementation to simplify creation of transaction signer & submitter in the runtime.
impl<T, E, S, C, Call> SubmitSignedTransaction<T, Call> for TransactionSubmitter<S, C, E> where
	T: crate::Trait,
	C: CreateTransaction<T, E>,
	S: Signer<<C as CreateTransaction<T, E>>::Public, <C as CreateTransaction<T, E>>::Signature>,
	E: ExtrinsicT<Call=Call> + 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<T, E, S, C, Call> SubmitUnsignedTransaction<T, Call> for TransactionSubmitter<S, C, E> where
	T: crate::Trait,
	E: ExtrinsicT<Call=Call> + codec::Encode,
{
	type Extrinsic = E;
}