Refactor: extract functions and types for creating extrinsics (#138)

* Extract extrinsic

* Extract extrinsic types to own module

* Fix compiler errors

* Fix warnings

* Fmt

* Pin wabt-sys to 0.7.1, it fails (on my machine) with 0.7.2

* Fix up tests

* Fmt

* Reexport RuntimeVersion
This commit is contained in:
Andrew Jones
2020-07-20 09:17:19 +01:00
committed by GitHub
parent 015a819759
commit cd6b8f43f1
8 changed files with 130 additions and 132 deletions
+311
View File
@@ -0,0 +1,311 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of substrate-subxt.
//
// subxt 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.
//
// subxt 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-subxt. If not, see <http://www.gnu.org/licenses/>.
use codec::{
Decode,
Encode,
};
use core::{
fmt::Debug,
marker::PhantomData,
};
use sp_runtime::{
generic::Era,
traits::SignedExtension,
transaction_validity::TransactionValidityError,
};
use crate::{
frame::{
balances::Balances,
system::System,
},
runtimes::Runtime,
};
/// Extra type.
pub type Extra<T> = <<T as Runtime>::Extra as SignedExtra<T>>::Extra;
/// SignedExtra checks copied from substrate, in order to remove requirement to implement
/// substrate's `frame_system::Trait`
/// Ensure the runtime version registered in the transaction is the same as at present.
///
/// # Note
///
/// This is modified from the substrate version to allow passing in of the version, which is
/// returned via `additional_signed()`.
/// Ensure the runtime version registered in the transaction is the same as at present.
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct CheckSpecVersion<T: System>(
pub PhantomData<T>,
/// Local version to be used for `AdditionalSigned`
#[codec(skip)]
pub u32,
);
impl<T> SignedExtension for CheckSpecVersion<T>
where
T: System + Clone + Debug + Eq + Send + Sync,
{
const IDENTIFIER: &'static str = "CheckSpecVersion";
type AccountId = u64;
type Call = ();
type AdditionalSigned = u32;
type Pre = ();
fn additional_signed(
&self,
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(self.1)
}
}
/// Ensure the transaction version registered in the transaction is the same as at present.
///
/// # Note
///
/// This is modified from the substrate version to allow passing in of the version, which is
/// returned via `additional_signed()`.
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct CheckTxVersion<T: System>(
pub PhantomData<T>,
/// Local version to be used for `AdditionalSigned`
#[codec(skip)]
pub u32,
);
impl<T> SignedExtension for CheckTxVersion<T>
where
T: System + Clone + Debug + Eq + Send + Sync,
{
const IDENTIFIER: &'static str = "CheckTxVersion";
type AccountId = u64;
type Call = ();
type AdditionalSigned = u32;
type Pre = ();
fn additional_signed(
&self,
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(self.1)
}
}
/// Check genesis hash
///
/// # Note
///
/// This is modified from the substrate version to allow passing in of the genesis hash, which is
/// returned via `additional_signed()`.
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct CheckGenesis<T: System>(
pub PhantomData<T>,
/// Local genesis hash to be used for `AdditionalSigned`
#[codec(skip)]
pub T::Hash,
);
impl<T> SignedExtension for CheckGenesis<T>
where
T: System + Clone + Debug + Eq + Send + Sync,
{
const IDENTIFIER: &'static str = "CheckGenesis";
type AccountId = u64;
type Call = ();
type AdditionalSigned = T::Hash;
type Pre = ();
fn additional_signed(
&self,
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(self.1)
}
}
/// Check for transaction mortality.
///
/// # Note
///
/// This is modified from the substrate version to allow passing in of the genesis hash, which is
/// returned via `additional_signed()`. It assumes therefore `Era::Immortal` (The transaction is
/// valid forever)
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct CheckEra<T: System>(
/// The default structure for the Extra encoding
pub (Era, PhantomData<T>),
/// Local genesis hash to be used for `AdditionalSigned`
#[codec(skip)]
pub T::Hash,
);
impl<T> SignedExtension for CheckEra<T>
where
T: System + Clone + Debug + Eq + Send + Sync,
{
const IDENTIFIER: &'static str = "CheckEra";
type AccountId = u64;
type Call = ();
type AdditionalSigned = T::Hash;
type Pre = ();
fn additional_signed(
&self,
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(self.1)
}
}
/// Nonce check and increment to give replay protection for transactions.
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct CheckNonce<T: System>(#[codec(compact)] pub T::Index);
impl<T> SignedExtension for CheckNonce<T>
where
T: System + Clone + Debug + Eq + Send + Sync,
{
const IDENTIFIER: &'static str = "CheckNonce";
type AccountId = u64;
type Call = ();
type AdditionalSigned = ();
type Pre = ();
fn additional_signed(
&self,
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(())
}
}
/// Resource limit check.
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct CheckWeight<T: System>(pub PhantomData<T>);
impl<T> SignedExtension for CheckWeight<T>
where
T: System + Clone + Debug + Eq + Send + Sync,
{
const IDENTIFIER: &'static str = "CheckWeight";
type AccountId = u64;
type Call = ();
type AdditionalSigned = ();
type Pre = ();
fn additional_signed(
&self,
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(())
}
}
/// Require the transactor pay for themselves and maybe include a tip to gain additional priority
/// in the queue.
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct ChargeTransactionPayment<T: Balances>(#[codec(compact)] pub T::Balance);
impl<T> SignedExtension for ChargeTransactionPayment<T>
where
T: Balances + Clone + Debug + Eq + Send + Sync,
{
const IDENTIFIER: &'static str = "ChargeTransactionPayment";
type AccountId = u64;
type Call = ();
type AdditionalSigned = ();
type Pre = ();
fn additional_signed(
&self,
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(())
}
}
/// Trait for implementing transaction extras for a runtime.
pub trait SignedExtra<T: System>: SignedExtension {
/// The type the extras.
type Extra: SignedExtension + Send + Sync;
/// Creates a new `SignedExtra`.
fn new(
spec_version: u32,
tx_version: u32,
nonce: T::Index,
genesis_hash: T::Hash,
) -> Self;
/// Returns the transaction extra.
fn extra(&self) -> Self::Extra;
}
/// Default `SignedExtra` for substrate runtimes.
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct DefaultExtra<T: System> {
spec_version: u32,
tx_version: u32,
nonce: T::Index,
genesis_hash: T::Hash,
}
impl<T: System + Balances + Clone + Debug + Eq + Send + Sync> SignedExtra<T>
for DefaultExtra<T>
{
type Extra = (
CheckSpecVersion<T>,
CheckTxVersion<T>,
CheckGenesis<T>,
CheckEra<T>,
CheckNonce<T>,
CheckWeight<T>,
ChargeTransactionPayment<T>,
);
fn new(
spec_version: u32,
tx_version: u32,
nonce: T::Index,
genesis_hash: T::Hash,
) -> Self {
DefaultExtra {
spec_version,
tx_version,
nonce,
genesis_hash,
}
}
fn extra(&self) -> Self::Extra {
(
CheckSpecVersion(PhantomData, self.spec_version),
CheckTxVersion(PhantomData, self.tx_version),
CheckGenesis(PhantomData, self.genesis_hash),
CheckEra((Era::Immortal, PhantomData), self.genesis_hash),
CheckNonce(self.nonce),
CheckWeight(PhantomData),
ChargeTransactionPayment(<T as Balances>::Balance::default()),
)
}
}
impl<T: System + Balances + Clone + Debug + Eq + Send + Sync> SignedExtension
for DefaultExtra<T>
{
const IDENTIFIER: &'static str = "DefaultExtra";
type AccountId = T::AccountId;
type Call = ();
type AdditionalSigned =
<<Self as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned;
type Pre = ();
fn additional_signed(
&self,
) -> Result<Self::AdditionalSigned, TransactionValidityError> {
self.extra().additional_signed()
}
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of substrate-subxt.
//
// subxt 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.
//
// subxt 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-subxt. If not, see <http://www.gnu.org/licenses/>.
//! Create signed or unsigned extrinsics.
mod extra;
mod signer;
pub use self::{
extra::{
DefaultExtra,
Extra,
SignedExtra,
},
signer::{
PairSigner,
Signer,
},
};
use sp_runtime::traits::SignedExtension;
use sp_version::RuntimeVersion;
use crate::{
frame::system::System,
runtimes::Runtime,
Encoded,
Error,
};
/// UncheckedExtrinsic type.
pub type UncheckedExtrinsic<T> = sp_runtime::generic::UncheckedExtrinsic<
<T as System>::Address,
Encoded,
<T as Runtime>::Signature,
Extra<T>,
>;
/// SignedPayload type.
pub type SignedPayload<T> = sp_runtime::generic::SignedPayload<Encoded, Extra<T>>;
/// Creates a signed extrinsic
pub async fn create_signed<T>(
runtime_version: &RuntimeVersion,
genesis_hash: T::Hash,
nonce: T::Index,
call: Encoded,
signer: &(dyn Signer<T> + Send + Sync),
) -> Result<UncheckedExtrinsic<T>, Error>
where
T: Runtime,
<<T::Extra as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync,
{
let spec_version = runtime_version.spec_version;
let tx_version = runtime_version.transaction_version;
let extra: T::Extra = T::Extra::new(spec_version, tx_version, nonce, genesis_hash);
let payload = SignedPayload::<T>::new(call, extra.extra())?;
let signed = signer.sign(payload).await?;
Ok(signed)
}
/// Creates an unsigned extrinsic
pub fn create_unsigned<T>(call: Encoded) -> UncheckedExtrinsic<T>
where
T: Runtime,
{
UncheckedExtrinsic::<T>::new_unsigned(call)
}
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of substrate-subxt.
//
// subxt 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.
//
// subxt 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-subxt. If not, see <http://www.gnu.org/licenses/>.
//! A library to **sub**mit e**xt**rinsics to a
//! [substrate](https://github.com/paritytech/substrate) node via RPC.
use super::{
SignedExtra,
SignedPayload,
UncheckedExtrinsic,
};
use crate::runtimes::Runtime;
use codec::Encode;
use sp_core::Pair;
use sp_runtime::traits::{
IdentifyAccount,
SignedExtension,
Verify,
};
use std::{
future::Future,
pin::Pin,
};
/// Extrinsic signer.
pub trait Signer<T: Runtime> {
/// Returns the account id.
fn account_id(&self) -> &T::AccountId;
/// Optionally returns a nonce.
fn nonce(&self) -> Option<T::Index>;
/// Takes an unsigned extrinsic and returns a signed extrinsic.
///
/// Some signers may fail, for instance because the hardware on which the keys are located has
/// refused the operation.
fn sign(
&self,
extrinsic: SignedPayload<T>,
) -> Pin<Box<dyn Future<Output = Result<UncheckedExtrinsic<T>, String>> + Send + Sync>>;
}
/// Extrinsic signer using a private key.
pub struct PairSigner<T: Runtime, P: Pair> {
account_id: T::AccountId,
nonce: Option<T::Index>,
signer: P,
}
impl<T, P> PairSigner<T, P>
where
T: Runtime,
T::Signature: From<P::Signature>,
<T::Signature as Verify>::Signer:
From<P::Public> + IdentifyAccount<AccountId = T::AccountId>,
P: Pair,
{
/// Creates a new `Signer` from a `Pair`.
pub fn new(signer: P) -> Self {
let account_id =
<T::Signature as Verify>::Signer::from(signer.public()).into_account();
Self {
account_id,
nonce: None,
signer,
}
}
/// Sets the nonce to a new value.
pub fn set_nonce(&mut self, nonce: T::Index) {
self.nonce = Some(nonce);
}
/// Increment the nonce.
pub fn increment_nonce(&mut self) {
self.nonce = self.nonce.map(|nonce| nonce + 1.into());
}
/// Returns the signer.
pub fn signer(&self) -> &P {
&self.signer
}
}
impl<T, P> Signer<T> for PairSigner<T, P>
where
T: Runtime,
T::AccountId: Into<T::Address> + 'static,
<<T::Extra as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync,
P: Pair + 'static,
P::Signature: Into<T::Signature> + 'static,
{
fn account_id(&self) -> &T::AccountId {
&self.account_id
}
fn nonce(&self) -> Option<T::Index> {
self.nonce
}
fn sign(
&self,
extrinsic: SignedPayload<T>,
) -> Pin<Box<dyn Future<Output = Result<UncheckedExtrinsic<T>, String>> + Send + Sync>>
{
let signature = extrinsic.using_encoded(|payload| self.signer.sign(payload));
let (call, extra, _) = extrinsic.deconstruct();
let extrinsic = UncheckedExtrinsic::<T>::new_signed(
call,
self.account_id.clone().into(),
signature.into(),
extra,
);
Box::pin(async move { Ok(extrinsic) })
}
}