Remove runtime dependency (#21)

* Remove System::Event type

* WIP: removing requirement for concrete Runtime impl for extras

* Use built in substrate types for constructing/signing the extrinsic

* Implement SignedExtension for copied types

* Fix remaining compilation errors

* Allow plugging in of predefined runtime types with blanket impls

* rustfmt

* Add CheckBlockGasLimit

* Construct DefaultExtrinsic in XtBuilder

* Derive Debug for extras

* Replace SignedExtension macro with explicit implementations

* Replace Lookup associated type with Address

* Move balances and contracts to main deps
This commit is contained in:
Andrew Jones
2019-10-03 18:17:28 +01:00
committed by GitHub
parent ee6db12917
commit 90cb9fc4cc
7 changed files with 328 additions and 103 deletions
+2 -2
View File
@@ -25,6 +25,8 @@ runtime_primitives = { git = "https://github.com/paritytech/substrate/", package
serde = { version = "1.0", features = ["derive"] }
sr-version = { git = "https://github.com/paritytech/substrate/", package = "sr-version" }
srml-system = { git = "https://github.com/paritytech/substrate/", package = "srml-system" }
srml-balances = { git = "https://github.com/paritytech/substrate/", package = "srml-balances" }
srml-contracts = { git = "https://github.com/paritytech/substrate/", package = "srml-contracts" }
substrate-rpc-api = { git = "https://github.com/paritytech/substrate/", package = "substrate-rpc-api" }
substrate-rpc-primitives = { git = "https://github.com/paritytech/substrate/", package = "substrate-rpc-primitives" }
substrate-primitives = { git = "https://github.com/paritytech/substrate/", package = "substrate-primitives" }
@@ -34,8 +36,6 @@ url = "1.7"
[dev-dependencies]
env_logger = "0.6"
node-runtime = { git = "https://github.com/paritytech/substrate/", package = "node-runtime" }
srml-balances = { git = "https://github.com/paritytech/substrate/", package = "srml-balances" }
srml-contracts = { git = "https://github.com/paritytech/substrate/", package = "srml-contracts" }
substrate-keyring = { git = "https://github.com/paritytech/substrate/", package = "substrate-keyring" }
tokio = "0.1"
wabt = "0.9.0"
+4
View File
@@ -20,6 +20,7 @@ use crate::{
};
use jsonrpc_core_client::RpcError;
use parity_scale_codec::Error as CodecError;
use runtime_primitives::transaction_validity::TransactionValidityError;
use std::io::Error as IoError;
use substrate_primitives::crypto::SecretStringError;
@@ -39,6 +40,9 @@ pub enum Error {
SecretString(SecretStringError),
/// Metadata error.
Metadata(MetadataError),
/// Extrinsic validity error
#[display(fmt = "Transaction Validity Error: {:?}", _0)]
Invalid(TransactionValidityError),
/// Other error.
Other(String),
}
+263
View File
@@ -0,0 +1,263 @@
// Copyright 2019 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 crate::srml::{
balances::Balances,
system::System,
};
use parity_scale_codec::{
Codec,
Decode,
Encode,
};
use runtime_primitives::{
generic::{
Era,
SignedPayload,
UncheckedExtrinsic,
},
traits::SignedExtension,
transaction_validity::TransactionValidityError,
};
use std::marker::PhantomData;
use substrate_primitives::Pair;
/// SignedExtra checks copied from substrate, in order to remove requirement to implement
/// substrate's `srml_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()`.
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct CheckVersion<T: System + Send + Sync>(
PhantomData<T>,
/// Local version to be used for `AdditionalSigned`
#[codec(skip)]
u32,
);
impl<T> SignedExtension for CheckVersion<T> where T: System + Send + Sync {
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 + Send + Sync>(
PhantomData<T>,
/// Local genesis hash to be used for `AdditionalSigned`
#[codec(skip)]
T::Hash,
);
impl<T> SignedExtension for CheckGenesis<T> where T: System + Send + Sync {
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 + Send + Sync>(
/// The default structure for the Extra encoding
(Era, PhantomData<T>),
/// Local genesis hash to be used for `AdditionalSigned`
#[codec(skip)]
T::Hash,
);
impl<T> SignedExtension for CheckEra<T> where T: System + Send + Sync {
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 + Send + Sync>(#[codec(compact)] T::Index);
impl<T> SignedExtension for CheckNonce<T> where T: System + Send + Sync {
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 + Send + Sync>(PhantomData<T>);
impl<T> SignedExtension for CheckWeight<T> where T: System + Send + Sync {
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 TakeFees<T: Balances>(#[codec(compact)] T::Balance);
impl<T> SignedExtension for TakeFees<T> where T: Balances + Send + Sync {
type AccountId = u64;
type Call = ();
type AdditionalSigned = ();
type Pre = ();
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(())
}
}
/// Checks if a transaction would exhausts the block gas limit.
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct CheckBlockGasLimit<T: System + Send + Sync>(PhantomData<T>);
impl<T> SignedExtension for CheckBlockGasLimit<T> where T: System + Send + Sync {
type AccountId = u64;
type Call = ();
type AdditionalSigned = ();
type Pre = ();
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(())
}
}
pub trait SignedExtra<T> {
type Extra: SignedExtension;
fn extra(&self) -> Self::Extra;
}
#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
pub struct DefaultExtra<T: System> {
version: u32,
nonce: T::Index,
genesis_hash: T::Hash,
}
impl<T: System + Balances + Send + Sync> DefaultExtra<T> {
pub fn new(version: u32, nonce: T::Index, genesis_hash: T::Hash) -> Self {
DefaultExtra {
version,
nonce,
genesis_hash,
}
}
}
impl<T: System + Balances + Send + Sync> SignedExtra<T> for DefaultExtra<T> {
type Extra = (
CheckVersion<T>,
CheckGenesis<T>,
CheckEra<T>,
CheckNonce<T>,
CheckWeight<T>,
TakeFees<T>,
CheckBlockGasLimit<T>,
);
fn extra(&self) -> Self::Extra {
(
CheckVersion(PhantomData, self.version),
CheckGenesis(PhantomData, self.genesis_hash),
CheckEra((Era::Immortal, PhantomData), self.genesis_hash),
CheckNonce(self.nonce),
CheckWeight(PhantomData),
TakeFees(<T as Balances>::Balance::default()),
CheckBlockGasLimit(PhantomData),
)
}
}
impl<T: System + Balances + Send + Sync> SignedExtension for DefaultExtra<T> {
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()
}
}
pub fn create_and_sign<T: System + Send + Sync, C, P, E>(
signer: P,
call: C,
extra: E,
) -> Result<
UncheckedExtrinsic<
T::Address,
C,
P::Signature,
<E as SignedExtra<T>>::Extra,
>,
TransactionValidityError,
>
where
P: Pair,
P::Public: Into<T::Address>,
P::Signature: Codec,
C: Encode,
E: SignedExtra<T> + SignedExtension,
{
let raw_payload = SignedPayload::new(call, extra.extra())?;
let signature = raw_payload.using_encoded(|payload| signer.sign(payload));
let (call, extra, _) = raw_payload.deconstruct();
Ok(UncheckedExtrinsic::new_signed(
call,
signer.public().into(),
signature.into(),
extra,
))
}
+19 -78
View File
@@ -31,19 +31,14 @@ use metadata::Metadata;
use parity_scale_codec::{
Codec,
Decode,
Encode,
};
use runtime_primitives::{
generic::UncheckedExtrinsic,
traits::StaticLookup,
};
use runtime_primitives::generic::UncheckedExtrinsic;
use sr_version::RuntimeVersion;
use std::{
convert::TryFrom,
marker::PhantomData,
};
use substrate_primitives::{
blake2_256,
storage::{
StorageChangeSet,
StorageKey,
@@ -55,6 +50,10 @@ use url::Url;
use crate::{
codec::Encoded,
events::EventsDecoder,
extrinsic::{
DefaultExtra,
SignedExtra,
},
metadata::MetadataError,
rpc::{
BlockNumber,
@@ -76,6 +75,7 @@ use crate::{
mod codec;
mod error;
mod events;
mod extrinsic;
mod metadata;
mod rpc;
mod srml;
@@ -235,7 +235,7 @@ impl<T: System + Balances + 'static> Client<T> {
) -> impl Future<Item = XtBuilder<T, P>, Error = Error>
where
P: Pair,
P::Public: Into<T::AccountId> + Into<<T::Lookup as StaticLookup>::Source>,
P::Public: Into<T::AccountId> + Into<T::Address>,
P::Signature: Codec,
{
let client = self.clone();
@@ -324,10 +324,10 @@ where
}
}
impl<T: System + Balances + 'static, P> XtBuilder<T, P, Valid>
impl<T: System + Balances + Send + Sync + 'static, P> XtBuilder<T, P, Valid>
where
P: Pair,
P::Public: Into<<T::Lookup as StaticLookup>::Source>,
P::Public: Into<T::Address>,
P::Signature: Codec,
{
/// Creates and signs an Extrinsic for the supplied `Call`
@@ -335,16 +335,16 @@ where
&self,
) -> Result<
UncheckedExtrinsic<
<T::Lookup as StaticLookup>::Source,
T::Address,
Encoded,
P::Signature,
T::SignedExtra,
<DefaultExtra<T> as SignedExtra<T>>::Extra,
>,
MetadataError,
Error,
>
where
P: Pair,
P::Public: Into<<T::Lookup as StaticLookup>::Source>,
P::Public: Into<T::Address>,
P::Signature: Codec,
{
let signer = self.signer.clone();
@@ -362,27 +362,9 @@ where
account_nonce
);
let extra = T::extra(account_nonce);
let raw_payload = (
call.clone(),
extra.clone(),
version,
(&genesis_hash, &genesis_hash),
);
let signature = raw_payload.using_encoded(|payload| {
if payload.len() > 256 {
signer.sign(&blake2_256(payload)[..])
} else {
signer.sign(payload)
}
});
Ok(UncheckedExtrinsic::new_signed(
raw_payload.0,
signer.public().into(),
signature.into(),
extra,
))
let extra = extrinsic::DefaultExtra::new(version, account_nonce, genesis_hash);
let xt = extrinsic::create_and_sign(signer, call, extra)?;
Ok(xt)
}
/// Submits a transaction to the chain.
@@ -427,14 +409,11 @@ mod tests {
BalancesStore,
BalancesXt,
},
contracts::{
Contracts,
ContractsXt,
},
contracts::ContractsXt,
};
use futures::stream::Stream;
use node_runtime::Runtime;
use parity_scale_codec::Encode;
use runtime_primitives::generic::Era;
use runtime_support::StorageMap;
use substrate_keyring::AccountKeyring;
use substrate_primitives::{
@@ -442,47 +421,9 @@ mod tests {
Pair,
};
struct Runtime;
impl System for Runtime {
type Index = <node_runtime::Runtime as srml_system::Trait>::Index;
type BlockNumber = <node_runtime::Runtime as srml_system::Trait>::BlockNumber;
type Hash = <node_runtime::Runtime as srml_system::Trait>::Hash;
type Hashing = <node_runtime::Runtime as srml_system::Trait>::Hashing;
type AccountId = <node_runtime::Runtime as srml_system::Trait>::AccountId;
type Lookup = <node_runtime::Runtime as srml_system::Trait>::Lookup;
type Header = <node_runtime::Runtime as srml_system::Trait>::Header;
type Event = <node_runtime::Runtime as srml_system::Trait>::Event;
type SignedExtra = (
srml_system::CheckVersion<node_runtime::Runtime>,
srml_system::CheckGenesis<node_runtime::Runtime>,
srml_system::CheckEra<node_runtime::Runtime>,
srml_system::CheckNonce<node_runtime::Runtime>,
srml_system::CheckWeight<node_runtime::Runtime>,
srml_balances::TakeFees<node_runtime::Runtime>,
);
fn extra(nonce: Self::Index) -> Self::SignedExtra {
(
srml_system::CheckVersion::<node_runtime::Runtime>::new(),
srml_system::CheckGenesis::<node_runtime::Runtime>::new(),
srml_system::CheckEra::<node_runtime::Runtime>::from(Era::Immortal),
srml_system::CheckNonce::<node_runtime::Runtime>::from(nonce),
srml_system::CheckWeight::<node_runtime::Runtime>::new(),
srml_balances::TakeFees::<node_runtime::Runtime>::from(0),
)
}
}
impl Balances for Runtime {
type Balance = <node_runtime::Runtime as srml_balances::Trait>::Balance;
}
impl Contracts for Runtime {}
type Index = <Runtime as System>::Index;
type AccountId = <Runtime as System>::AccountId;
type Address = <<Runtime as System>::Lookup as StaticLookup>::Source;
type Address = <Runtime as System>::Address;
type Balance = <Runtime as Balances>::Balance;
fn test_setup() -> (tokio::runtime::Runtime, Client<Runtime>) {
+9 -2
View File
@@ -23,7 +23,6 @@ use runtime_primitives::traits::{
MaybeSerializeDebug,
Member,
SimpleArithmetic,
StaticLookup,
};
use runtime_support::Parameter;
use substrate_primitives::Pair;
@@ -41,6 +40,14 @@ pub trait Balances: System {
+ From<<Self as System>::BlockNumber>;
}
/// Blanket impl for using existing runtime types
impl<T: srml_system::Trait + srml_balances::Trait + std::fmt::Debug> Balances for T
where
<T as srml_system::Trait>::Header: serde::de::DeserializeOwned,
{
type Balance = T::Balance;
}
/// The Balances extension trait for the Client.
pub trait BalancesStore {
/// Balances type.
@@ -134,7 +141,7 @@ where
/// of the transfer, the account will be reaped.
pub fn transfer(
self,
to: <<T as System>::Lookup as StaticLookup>::Source,
to: <T as System>::Address,
amount: <T as Balances>::Balance,
) -> Result<Encoded, MetadataError> {
self.module.call("transfer", (to, compact(amount)))
+13 -2
View File
@@ -13,7 +13,6 @@ use crate::{
Valid,
XtBuilder,
};
use runtime_primitives::traits::StaticLookup;
use substrate_primitives::Pair;
/// Gas units are chosen to be represented by u64 so that gas metering
@@ -23,6 +22,18 @@ pub type Gas = u64;
/// The subset of the `srml_contracts::Trait` that a client must implement.
pub trait Contracts: System + Balances {}
/// Blanket impl for using existing runtime types
impl<
T: srml_contracts::Trait
+ srml_system::Trait
+ srml_balances::Trait
+ std::fmt::Debug,
> Contracts for T
where
<T as srml_system::Trait>::Header: serde::de::DeserializeOwned,
{
}
/// The Contracts extension trait for the XtBuilder.
pub trait ContractsXt {
/// Contracts type.
@@ -106,7 +117,7 @@ where
/// will be transferred.
pub fn call(
&self,
dest: <<T as System>::Lookup as StaticLookup>::Source,
dest: <T as System>::Address,
value: <T as Balances>::Balance,
gas_limit: Gas,
data: Vec<u8>,
+18 -19
View File
@@ -15,27 +15,27 @@ use futures::future::{
self,
Future,
};
use parity_scale_codec::Codec;
use runtime_primitives::traits::{
Bounded,
CheckEqual,
Hash,
Header,
MaybeDebug,
MaybeDisplay,
MaybeSerializeDebug,
MaybeSerializeDebugButNotDeserialize,
Member,
SignedExtension,
SimpleArithmetic,
SimpleBitOps,
StaticLookup,
};
use runtime_support::Parameter;
use serde::de::DeserializeOwned;
use srml_system::Event;
use substrate_primitives::Pair;
/// The subset of the `srml_system::Trait` that a client must implement.
pub trait System {
pub trait System: 'static + Eq + Clone + std::fmt::Debug {
/// Account index (aka nonce) type. This stores the number of previous
/// transactions associated with a sender account.
type Index: Parameter
@@ -81,28 +81,27 @@ pub trait System {
+ Ord
+ Default;
/// Converting trait to take a source type and convert to `AccountId`.
///
/// Used to define the type and conversion mechanism for referencing
/// accounts in transactions. It's perfectly reasonable for this to be an
/// identity conversion (with the source type being `AccountId`), but other
/// modules (e.g. Indices module) may provide more functional/efficient
/// alternatives.
type Lookup: StaticLookup<Target = Self::AccountId>;
/// The address type. This instead of `<srml_system::Trait::Lookup as StaticLookup>::Source`.
type Address: Codec + Clone + PartialEq + MaybeDebug;
/// The block header.
type Header: Parameter
+ Header<Number = Self::BlockNumber, Hash = Self::Hash>
+ DeserializeOwned;
}
/// The aggregated event type of the runtime.
type Event: Parameter + Member + From<Event>;
/// The `SignedExtension` to the basic transaction logic.
type SignedExtra: SignedExtension;
/// Creates the `SignedExtra` from the account nonce.
fn extra(nonce: Self::Index) -> Self::SignedExtra;
/// Blanket impl for using existing runtime types
impl<T: srml_system::Trait + std::fmt::Debug> System for T
where
<T as srml_system::Trait>::Header: serde::de::DeserializeOwned,
{
type Index = T::Index;
type BlockNumber = T::BlockNumber;
type Hash = T::Hash;
type Hashing = T::Hashing;
type AccountId = T::AccountId;
type Address = <T::Lookup as StaticLookup>::Source;
type Header = T::Header;
}
/// The System extension trait for the Client.