From 14e7f4238dba9f1112b59e1c07626de203747417 Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Tue, 9 Jun 2020 17:14:07 +1200 Subject: [PATCH 01/10] add system_dryRun --- Cargo.lock | 1 + utils/frame/rpc/system/Cargo.toml | 1 + utils/frame/rpc/system/src/lib.rs | 75 ++++++++++++++++++++++++++----- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e0f788707dfc..464fc21e5fb7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8035,6 +8035,7 @@ dependencies = [ "sc-transaction-pool", "serde", "sp-api", + "sp-block-builder", "sp-blockchain", "sp-core", "sp-runtime", diff --git a/utils/frame/rpc/system/Cargo.toml b/utils/frame/rpc/system/Cargo.toml index 11afd3b841ed3..9ce223e72153b 100644 --- a/utils/frame/rpc/system/Cargo.toml +++ b/utils/frame/rpc/system/Cargo.toml @@ -26,6 +26,7 @@ frame-system-rpc-runtime-api = { version = "2.0.0-rc3", path = "../../../../fram sp-core = { version = "2.0.0-rc3", path = "../../../../primitives/core" } sp-blockchain = { version = "2.0.0-rc3", path = "../../../../primitives/blockchain" } sp-transaction-pool = { version = "2.0.0-rc3", path = "../../../../primitives/transaction-pool" } +sp-block-builder = { version = "2.0.0-rc3", path = "../../../../primitives/block-builder" } [dev-dependencies] substrate-test-runtime-client = { version = "2.0.0-rc3", path = "../../../../test-utils/runtime/client" } diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index a3ce1466f6fe9..8a11fc5aebff1 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -22,7 +22,7 @@ use std::sync::Arc; use codec::{self, Codec, Decode, Encode}; use sc_client_api::light::{future_header, RemoteBlockchain, Fetcher, RemoteCallRequest}; use jsonrpc_core::{ - Error, ErrorCode, + Error as RpcError, ErrorCode, futures::future::{result, Future}, }; use jsonrpc_derive::rpc; @@ -35,18 +35,19 @@ use sp_runtime::{ generic::BlockId, traits, }; -use sp_core::hexdisplay::HexDisplay; +use sp_core::{hexdisplay::HexDisplay, Bytes}; use sp_transaction_pool::{TransactionPool, InPoolTransaction}; +use sp_block_builder::BlockBuilder; pub use frame_system_rpc_runtime_api::AccountNonceApi; pub use self::gen_client::Client as SystemClient; /// Future that resolves to account nonce. -pub type FutureResult = Box + Send>; +pub type FutureResult = Box + Send>; /// System RPC methods. #[rpc] -pub trait SystemApi { +pub trait SystemApi { /// Returns the next valid index (aka nonce) for given account. /// /// This method takes into consideration all pending transactions @@ -54,9 +55,28 @@ pub trait SystemApi { /// it fallbacks to query the index from the runtime (aka. state nonce). #[rpc(name = "system_accountNextIndex", alias("account_nextIndex"))] fn nonce(&self, account: AccountId) -> FutureResult; + + /// Dry run an extrinsic at a given block. Return all encoded events emitted during execution. + #[rpc(name = "system_dryRun", alias("system_dryRunAt"))] + fn dry_run(&self, extrinsic: Bytes, at: Option) -> FutureResult; +} + +/// Error type of this RPC api. +pub enum Error { + /// The transaction was not decodable. + DecodeError, + /// The call to runtime failed. + RuntimeError, } -const RUNTIME_ERROR: i64 = 1; +impl From for i64 { + fn from(e: Error) -> i64 { + match e { + Error::RuntimeError => 1, + Error::DecodeError => 2, + } + } +} /// An implementation of System-specific RPC methods on full client. pub struct FullSystem { @@ -76,12 +96,14 @@ impl FullSystem { } } -impl SystemApi for FullSystem +impl SystemApi<::Hash, AccountId, Index> for FullSystem where C: sp_api::ProvideRuntimeApi, C: HeaderBackend, C: Send + Sync + 'static, C::Api: AccountNonceApi, + C::Api: BlockBuilder, + ::Error: Encode, P: TransactionPool + 'static, Block: traits::Block, AccountId: Clone + std::fmt::Display + Codec, @@ -93,8 +115,8 @@ where let best = self.client.info().best_hash; let at = BlockId::hash(best); - let nonce = api.account_nonce(&at, account.clone()).map_err(|e| Error { - code: ErrorCode::ServerError(RUNTIME_ERROR), + let nonce = api.account_nonce(&at, account.clone()).map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::RuntimeError.into()), message: "Unable to query nonce.".into(), data: Some(format!("{:?}", e).into()), })?; @@ -104,6 +126,29 @@ where Box::new(result(get_nonce())) } + + fn dry_run(&self, extrinsic: Bytes, at: Option<::Hash>) -> FutureResult { + let dry_run = || { + let api = self.client.runtime_api(); + let at = BlockId::::hash(at.unwrap_or_else(|| + // If the block hash is not supplied assume the best block. + self.client.info().best_hash + )); + + let uxt: ::Extrinsic = Decode::decode(&mut &*extrinsic).map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::DecodeError.into()), + message: "Unable to dry run extrinsic.".into(), + data: Some(format!("{:?}", e).into()), + })?; + + let result = api.apply_extrinsic(&at, uxt).map(|outcome| outcome.map(|_| true)); // TODO read system events + + Ok(Encode::encode(&result).into()) + }; + + + Box::new(result(dry_run())) + } } /// An implementation of System-specific RPC methods on light client. @@ -131,7 +176,7 @@ impl LightSystem { } } -impl SystemApi for LightSystem +impl SystemApi<::Hash, AccountId, Index> for LightSystem where P: TransactionPool + 'static, C: HeaderBackend, @@ -165,8 +210,8 @@ where ).compat(); let future_nonce = future_nonce.and_then(|nonce| Decode::decode(&mut &nonce[..]) .map_err(|e| ClientError::CallResultDecode("Cannot decode account nonce", e))); - let future_nonce = future_nonce.map_err(|e| Error { - code: ErrorCode::ServerError(RUNTIME_ERROR), + let future_nonce = future_nonce.map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::RuntimeError.into()), message: "Unable to query nonce.".into(), data: Some(format!("{:?}", e).into()), }); @@ -176,6 +221,14 @@ where Box::new(future_nonce) } + + fn dry_run(&self, _extrinsic: Bytes, _at: Option<::Hash>) -> FutureResult { + Box::new(result(Err(RpcError { + code: ErrorCode::MethodNotFound, + message: "Unable to dry run extrinsic.".into(), + data: None, + }))) + } } /// Adjust account nonce from state, so that tx with the nonce will be From 5dddc7ba4e3d0d151045326c0b1c8398f4b0aec2 Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Wed, 10 Jun 2020 19:16:46 +1200 Subject: [PATCH 02/10] fix build error --- Cargo.lock | 1 + bin/node/rpc/Cargo.toml | 1 + bin/node/rpc/src/lib.rs | 4 +++- utils/frame/rpc/system/src/lib.rs | 10 ++++++++-- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 464fc21e5fb7c..c1efb8f939c91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3440,6 +3440,7 @@ dependencies = [ "sc-keystore", "sc-rpc-api", "sp-api", + "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-babe", diff --git a/bin/node/rpc/Cargo.toml b/bin/node/rpc/Cargo.toml index 0c6c913b137ad..2bac8b67409d2 100644 --- a/bin/node/rpc/Cargo.toml +++ b/bin/node/rpc/Cargo.toml @@ -31,3 +31,4 @@ sp-blockchain = { version = "2.0.0-rc3", path = "../../../primitives/blockchain" sc-finality-grandpa = { version = "0.8.0-rc3", path = "../../../client/finality-grandpa" } sc-finality-grandpa-rpc = { version = "0.8.0-rc3", path = "../../../client/finality-grandpa/rpc" } sc-rpc-api = { version = "0.8.0-rc3", path = "../../../client/rpc-api" } +sp-block-builder = { version = "2.0.0-rc3", path = "../../../primitives/block-builder" } diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index 259a792441d40..803cf8253164b 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -46,6 +46,7 @@ use sc_consensus_babe_rpc::BabeRpcHandler; use sc_finality_grandpa::{SharedVoterState, SharedAuthoritySet}; use sc_finality_grandpa_rpc::GrandpaRpcHandler; use sc_rpc_api::DenyUnsafe; +use sp_block_builder::BlockBuilder; /// Light client extra dependencies. pub struct LightDeps { @@ -104,6 +105,7 @@ pub fn create_full( C::Api: pallet_contracts_rpc::ContractsRuntimeApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, C::Api: BabeApi, + C::Api: BlockBuilder, ::Error: fmt::Debug, P: TransactionPool + 'static, M: jsonrpc_core::Metadata + Default, @@ -185,7 +187,7 @@ pub fn create_light( } = deps; let mut io = jsonrpc_core::IoHandler::default(); io.extend_with( - SystemApi::::to_delegate(LightSystem::new(client, remote_blockchain, fetcher, pool)) + SystemApi::::to_delegate(LightSystem::new(client, remote_blockchain, fetcher, pool)) ); io diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index 8a11fc5aebff1..30a835de1ee81 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -103,7 +103,7 @@ where C: Send + Sync + 'static, C::Api: AccountNonceApi, C::Api: BlockBuilder, - ::Error: Encode, + ::Error: std::fmt::Debug, P: TransactionPool + 'static, Block: traits::Block, AccountId: Clone + std::fmt::Display + Codec, @@ -141,7 +141,13 @@ where data: Some(format!("{:?}", e).into()), })?; - let result = api.apply_extrinsic(&at, uxt).map(|outcome| outcome.map(|_| true)); // TODO read system events + let result = api.apply_extrinsic(&at, uxt) + .map(|outcome| outcome.map(|_| true)) // TODO read system events + .map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::RuntimeError.into()), + message: "Unable to dry run extrinsic.".into(), + data: Some(format!("{:?}", e).into()), + })?; Ok(Encode::encode(&result).into()) }; From 1044965d057993641400013075aa86e5f93cf92e Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Wed, 10 Jun 2020 19:18:38 +1200 Subject: [PATCH 03/10] delete unneeded code --- bin/node/rpc/src/lib.rs | 3 +-- client/consensus/babe/rpc/src/lib.rs | 3 +-- utils/frame/rpc/system/src/lib.rs | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index 803cf8253164b..f1bc4fa3abd66 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -30,7 +30,7 @@ #![warn(missing_docs)] -use std::{sync::Arc, fmt}; +use std::sync::Arc; use node_primitives::{Block, BlockNumber, AccountId, Index, Balance, Hash}; use node_runtime::UncheckedExtrinsic; @@ -106,7 +106,6 @@ pub fn create_full( C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, C::Api: BabeApi, C::Api: BlockBuilder, - ::Error: fmt::Debug, P: TransactionPool + 'static, M: jsonrpc_core::Metadata + Default, SC: SelectChain +'static, diff --git a/client/consensus/babe/rpc/src/lib.rs b/client/consensus/babe/rpc/src/lib.rs index 8e1282a8d79a7..35000770d49c1 100644 --- a/client/consensus/babe/rpc/src/lib.rs +++ b/client/consensus/babe/rpc/src/lib.rs @@ -38,7 +38,7 @@ use sp_api::{ProvideRuntimeApi, BlockId}; use sp_runtime::traits::{Block as BlockT, Header as _}; use sp_consensus::{SelectChain, Error as ConsensusError}; use sp_blockchain::{HeaderBackend, HeaderMetadata, Error as BlockChainError}; -use std::{collections::HashMap, fmt, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; type FutureResult = Box + Send>; @@ -93,7 +93,6 @@ impl BabeApi for BabeRpcHandler B: BlockT, C: ProvideRuntimeApi + HeaderBackend + HeaderMetadata + 'static, C::Api: BabeRuntimeApi, - ::Error: fmt::Debug, SC: SelectChain + Clone + 'static, { fn epoch_authorship(&self) -> FutureResult> { diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index 30a835de1ee81..d49b94c7b7e35 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -103,7 +103,6 @@ where C: Send + Sync + 'static, C::Api: AccountNonceApi, C::Api: BlockBuilder, - ::Error: std::fmt::Debug, P: TransactionPool + 'static, Block: traits::Block, AccountId: Clone + std::fmt::Display + Codec, From 8facb2b7674677e5e70170835ce3d78b2490390c Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Thu, 11 Jun 2020 15:36:37 +1200 Subject: [PATCH 04/10] return ApplyExtrinsicResult directly --- utils/frame/rpc/system/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index d49b94c7b7e35..807512aac0290 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -141,7 +141,6 @@ where })?; let result = api.apply_extrinsic(&at, uxt) - .map(|outcome| outcome.map(|_| true)) // TODO read system events .map_err(|e| RpcError { code: ErrorCode::ServerError(Error::RuntimeError.into()), message: "Unable to dry run extrinsic.".into(), From c5a4c34a15ef9fe9875ea998558731e99203b1b1 Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Thu, 11 Jun 2020 15:56:34 +1200 Subject: [PATCH 05/10] line width --- utils/frame/rpc/system/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index 807512aac0290..5706220eff17c 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -96,7 +96,8 @@ impl FullSystem { } } -impl SystemApi<::Hash, AccountId, Index> for FullSystem +impl SystemApi<::Hash, AccountId, Index> + for FullSystem where C: sp_api::ProvideRuntimeApi, C: HeaderBackend, From 4af71f60df41fc5803f43395f4ef5b89f6eca995 Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Fri, 12 Jun 2020 01:09:00 +1200 Subject: [PATCH 06/10] mark dry run unsafe --- Cargo.lock | 1 + bin/node/rpc/src/lib.rs | 2 +- utils/frame/rpc/system/Cargo.toml | 1 + utils/frame/rpc/system/src/lib.rs | 11 +++++++++-- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58da50ee9eab8..21002425d104b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8058,6 +8058,7 @@ dependencies = [ "log", "parity-scale-codec", "sc-client-api", + "sc-rpc-api", "sc-transaction-pool", "serde", "sp-api", diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index f1bc4fa3abd66..9b6b5991748f9 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -134,7 +134,7 @@ pub fn create_full( } = grandpa; io.extend_with( - SystemApi::to_delegate(FullSystem::new(client.clone(), pool)) + SystemApi::to_delegate(FullSystem::new(client.clone(), pool, deny_unsafe)) ); // Making synchronous calls in light client freezes the browser currently, // more context: https://github.com/paritytech/substrate/pull/3480 diff --git a/utils/frame/rpc/system/Cargo.toml b/utils/frame/rpc/system/Cargo.toml index 9ce223e72153b..21cd00ebd4bc2 100644 --- a/utils/frame/rpc/system/Cargo.toml +++ b/utils/frame/rpc/system/Cargo.toml @@ -27,6 +27,7 @@ sp-core = { version = "2.0.0-rc3", path = "../../../../primitives/core" } sp-blockchain = { version = "2.0.0-rc3", path = "../../../../primitives/blockchain" } sp-transaction-pool = { version = "2.0.0-rc3", path = "../../../../primitives/transaction-pool" } sp-block-builder = { version = "2.0.0-rc3", path = "../../../../primitives/block-builder" } +sc-rpc-api = { version = "0.8.0-rc3", path = "../../../../client/rpc-api" } [dev-dependencies] substrate-test-runtime-client = { version = "2.0.0-rc3", path = "../../../../test-utils/runtime/client" } diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index 5706220eff17c..fcea34c774a6f 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -23,7 +23,7 @@ use codec::{self, Codec, Decode, Encode}; use sc_client_api::light::{future_header, RemoteBlockchain, Fetcher, RemoteCallRequest}; use jsonrpc_core::{ Error as RpcError, ErrorCode, - futures::future::{result, Future}, + futures::future::{self as rpc_future,result, Future}, }; use jsonrpc_derive::rpc; use futures::future::{ready, TryFutureExt}; @@ -38,6 +38,7 @@ use sp_runtime::{ use sp_core::{hexdisplay::HexDisplay, Bytes}; use sp_transaction_pool::{TransactionPool, InPoolTransaction}; use sp_block_builder::BlockBuilder; +use sc_rpc_api::DenyUnsafe; pub use frame_system_rpc_runtime_api::AccountNonceApi; pub use self::gen_client::Client as SystemClient; @@ -82,15 +83,17 @@ impl From for i64 { pub struct FullSystem { client: Arc, pool: Arc

, + deny_unsafe: DenyUnsafe, _marker: std::marker::PhantomData, } impl FullSystem { /// Create new `FullSystem` given client and transaction pool. - pub fn new(client: Arc, pool: Arc

) -> Self { + pub fn new(client: Arc, pool: Arc

, deny_unsafe: DenyUnsafe,) -> Self { FullSystem { client, pool, + deny_unsafe, _marker: Default::default(), } } @@ -128,6 +131,10 @@ where } fn dry_run(&self, extrinsic: Bytes, at: Option<::Hash>) -> FutureResult { + if let Err(err) = self.deny_unsafe.check_if_safe() { + return Box::new(rpc_future::err(err.into())); + } + let dry_run = || { let api = self.client.runtime_api(); let at = BlockId::::hash(at.unwrap_or_else(|| From d6ff8f1e3035f488683ce1130e5ae82e26eb9424 Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Fri, 12 Jun 2020 01:11:50 +1200 Subject: [PATCH 07/10] line width --- utils/frame/rpc/system/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index fcea34c774a6f..da34ace60d97a 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -188,7 +188,8 @@ impl LightSystem { } } -impl SystemApi<::Hash, AccountId, Index> for LightSystem +impl SystemApi<::Hash, AccountId, Index> + for LightSystem where P: TransactionPool + 'static, C: HeaderBackend, From 374376b77742f4de8474bad58e76e797f83e8168 Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Sat, 13 Jun 2020 16:06:48 +1200 Subject: [PATCH 08/10] fix test --- utils/frame/rpc/system/src/lib.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index da34ace60d97a..8f945d151d3cc 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -321,7 +321,7 @@ mod tests { let ext1 = new_transaction(1); block_on(pool.submit_one(&BlockId::number(0), source, ext1)).unwrap(); - let accounts = FullSystem::new(client, pool); + let accounts = FullSystem::new(client, pool, DenyUnsafe::Yes); // when let nonce = accounts.nonce(AccountKeyring::Alice.into()); @@ -329,4 +329,27 @@ mod tests { // then assert_eq!(nonce.wait().unwrap(), 2); } + + #[test] + fn dry_run_should_deny_unsafe() { + let _ = env_logger::try_init(); + + // given + let client = Arc::new(substrate_test_runtime_client::new()); + let pool = Arc::new( + BasicPool::new( + Default::default(), + Arc::new(FullChainApi::new(client.clone())), + None, + ).0 + ); + + let accounts = FullSystem::new(client, pool, DenyUnsafe::Yes); + + // when + let res = accounts.dry_run(vec![].into(), None); + + // then + assert_eq!(res.wait(), Err(RpcError::method_not_found())); + } } From 39bc8ef10157c3967d18c7ab2c6b151dd348b404 Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Sat, 13 Jun 2020 16:27:59 +1200 Subject: [PATCH 09/10] add test --- utils/frame/rpc/system/src/lib.rs | 65 +++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index 8f945d151d3cc..65b2fc2d4ef30 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -290,6 +290,7 @@ mod tests { use futures::executor::block_on; use substrate_test_runtime_client::{runtime::Transfer, AccountKeyring}; use sc_transaction_pool::{BasicPool, FullChainApi}; + use sp_runtime::{ApplyExtrinsicResult, transaction_validity::{TransactionValidityError, InvalidTransaction}}; #[test] fn should_return_next_nonce_for_some_account() { @@ -352,4 +353,68 @@ mod tests { // then assert_eq!(res.wait(), Err(RpcError::method_not_found())); } + + #[test] + fn dry_run_should_work() { + let _ = env_logger::try_init(); + + // given + let client = Arc::new(substrate_test_runtime_client::new()); + let pool = Arc::new( + BasicPool::new( + Default::default(), + Arc::new(FullChainApi::new(client.clone())), + None, + ).0 + ); + + let accounts = FullSystem::new(client, pool, DenyUnsafe::No); + + let tx = Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Bob.into(), + amount: 5, + nonce: 0, + }.into_signed_tx(); + + // when + let res = accounts.dry_run(tx.encode().into(), None); + + // then + let bytes = res.wait().unwrap().0; + let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_slice()).unwrap(); + assert_eq!(apply_res, Ok(Ok(()))); + } + + #[test] + fn dry_run_should_indicate_error() { + let _ = env_logger::try_init(); + + // given + let client = Arc::new(substrate_test_runtime_client::new()); + let pool = Arc::new( + BasicPool::new( + Default::default(), + Arc::new(FullChainApi::new(client.clone())), + None, + ).0 + ); + + let accounts = FullSystem::new(client, pool, DenyUnsafe::No); + + let tx = Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Bob.into(), + amount: 5, + nonce: 100, + }.into_signed_tx(); + + // when + let res = accounts.dry_run(tx.encode().into(), None); + + // then + let bytes = res.wait().unwrap().0; + let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_slice()).unwrap(); + assert_eq!(apply_res, Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))); + } } From 41a7ab46236288a4b6ad1744a36d59b0e7619335 Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Mon, 15 Jun 2020 09:55:02 +1200 Subject: [PATCH 10/10] update comment --- utils/frame/rpc/system/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index 65b2fc2d4ef30..6927f05b4f05b 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -57,7 +57,7 @@ pub trait SystemApi { #[rpc(name = "system_accountNextIndex", alias("account_nextIndex"))] fn nonce(&self, account: AccountId) -> FutureResult; - /// Dry run an extrinsic at a given block. Return all encoded events emitted during execution. + /// Dry run an extrinsic at a given block. Return SCALE encoded ApplyExtrinsicResult. #[rpc(name = "system_dryRun", alias("system_dryRunAt"))] fn dry_run(&self, extrinsic: Bytes, at: Option) -> FutureResult; }