From bc28c60593c30c4f53a76df92f0c4546a29bc901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 25 Nov 2019 17:01:05 +0100 Subject: [PATCH 01/30] Add documentation to signed transactions and actually make them work. --- frame/system/src/offchain.rs | 223 ++++++++++++++++++++++++++++------- 1 file changed, 183 insertions(+), 40 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 9f0a3ab1b2442..f4ff79e6aecd4 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -17,14 +17,52 @@ //! Module helpers for offchain calls. use codec::Encode; -use sr_primitives::app_crypto::{self, RuntimeAppPublic}; +use rstd::convert::TryInto; +use rstd::prelude::Vec; +use sr_primitives::app_crypto::{self, RuntimeAppPublic, AppPublic, AppSignature}; use sr_primitives::traits::{Extrinsic as ExtrinsicT, IdentifyAccount}; +use support::debug; + +/// Creates runtime-specific signed transaction. +/// +/// This trait should be implemented by your `Runtime` to be able +/// to submit `SignedTransaction`s` to the pool from offchain code. +pub trait CreateTransaction { + /// A `Public` key representing a particular `AccountId`. + type Public: Clone + IdentifyAccount; + /// 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>( + call: Extrinsic::Call, + public: Self::Public, + account: T::AccountId, + nonce: T::Index, + ) -> Option<(Extrinsic::Call, Extrinsic::SignaturePayload)>; +} /// A trait responsible for signing a payload using given account. +/// +/// This trait is usually going to represent a local public key +/// that has ability to sign arbitrary `Payloads`. +/// +/// NOTE: Most likely you don't need to implement this trait manually. +/// It has a blanket implementation for all `RuntimeAppPublic` types, +/// so it's enough to pass an application-specific crypto type. +/// +/// To easily create `SignedTransaction`s have a look at the +/// `TransactionSubmitter` type. 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. + /// Returns `Some` if signing succeeded and `None` in case the `account` couldn't + /// be used (for instance we couldn't convert it to required application specific crypto). fn sign(public: Public, payload: &Payload) -> Option; } @@ -33,22 +71,22 @@ pub trait Signer { /// 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 Signer for AppPublic where - AppPublic: RuntimeAppPublic - + app_crypto::AppPublic - + From<::Generic>, - ::Signature: app_crypto::AppSignature, +impl Signer for TAnyAppPublic where + TAnyAppPublic: RuntimeAppPublic + + AppPublic + + From<::Generic>, + ::Signature: AppSignature, Signature: From< - <::Signature as app_crypto::AppSignature>::Generic + <::Signature as AppSignature>::Generic >, - Public: rstd::convert::TryInto<::Generic> + Public: TryInto<::Generic> { fn sign(public: Public, raw_payload: &Payload) -> Option { raw_payload.using_encoded(|payload| { let public = public.try_into().ok()?; - AppPublic::from(public).sign(&payload) + TAnyAppPublic::from(public).sign(&payload) .map( - <::Signature as app_crypto::AppSignature> + <::Signature as AppSignature> ::Generic::from ) .map(Signature::from) @@ -56,35 +94,19 @@ impl Signer for AppPublic where } } -/// Creates runtime-specific signed transaction. -pub trait CreateTransaction { - /// A `Public` key representing a particular `AccountId`. - type Public: IdentifyAccount + 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>( - call: Extrinsic::Call, - public: Self::Public, - account: T::AccountId, - nonce: T::Index, - ) -> Option<(Extrinsic::Call, Extrinsic::SignaturePayload)>; -} - -type PublicOf = < - >::CreateTransaction as CreateTransaction< - T, - >::Extrinsic, - > +/// Retrieves a public key type for given `SubmitSignedTransaction`. +pub type PublicOf = +< + >::CreateTransaction + as + CreateTransaction>::Extrinsic> >::Public; /// A trait to sign and submit transactions in offchain calls. +/// +/// NOTE: Most likely you should not implement this trait yourself. +/// There is an implementation for `TransactionSubmitter` type, which +/// you should use. pub trait SubmitSignedTransaction { /// Unchecked extrinsic type. type Extrinsic: ExtrinsicT + codec::Encode; @@ -107,6 +129,12 @@ pub trait SubmitSignedTransaction { let call = call.into(); let id = public.clone().into_account(); let expected = >::account_nonce(&id); + debug::native::debug!( + target: "offchain", + "Creating signed transaction from account: {:?} (nonce: {:?})", + id, + expected, + ); let (call, signature_data) = Self::CreateTransaction ::create_transaction::(call, public, id, expected) .ok_or(())?; @@ -116,6 +144,10 @@ pub trait SubmitSignedTransaction { } /// A trait to submit unsigned transactions in offchain calls. +/// +/// NOTE: Most likely you should not implement this trait yourself. +/// There is an implementation for `TransactionSubmitter` type, which +/// you should use. pub trait SubmitUnsignedTransaction { /// Unchecked extrinsic type. type Extrinsic: ExtrinsicT + codec::Encode; @@ -130,9 +162,70 @@ pub trait SubmitUnsignedTransaction { } } +/// A utility trait to easily create signed transactions +/// from accounts in node's local keystore. +/// +/// NOTE: Most likely you should not implement this trait yourself. +/// There is an implementation for `TransactionSubmitter` type, which +/// you should use. +pub trait SigningAccountFinder { + /// A `SubmitSignedTransaction` implementation. + type SubmitTransaction: SubmitSignedTransaction; + + /// Find local keys that match given list of accounts. + /// + /// Technically it finds an intersection between given list of `AccountId`s + /// and accounts that are represented by public keys in local keystore. + /// + /// Returns both public keys and `AccountId`s of accounts that are available. + /// Such accounts can later be used to sign a payload or send signed transactions. + fn find_local_keys(accounts: impl IntoIterator) -> Vec<( + T::AccountId, + PublicOf, + )>; + + /// Create and submit signed transactions from supported accounts. + /// + /// This method should intersect given list of accounts with the ones + /// supported locally and submit signed transaction containing given `Call` + /// with every of them. + /// + /// Returns a vector of results and account ids that were supported. + fn submit_signed( + accounts: impl IntoIterator, + call: impl Into + Clone, + ) -> Vec<(T::AccountId, Result<(), ()>)> { + let keys = Self::find_local_keys(accounts); + + keys.into_iter().map(|(account, pub_key)| { + let call = call.clone().into(); + ( + account, + Self::SubmitTransaction::sign_and_submit(call, pub_key) + ) + }).collect() + } +} + + /// A default type used to submit transactions to the pool. -pub struct TransactionSubmitter { - _signer: rstd::marker::PhantomData<(S, C, E)>, +/// +/// This is passed into each runtime as an opaque associated type that can have either of: +/// - [`SubmitSignedTransaction`] +/// - [`SubmitUnsignedTransaction`] +/// - [`SigningAccountFinder`] +/// and used accordingly. +/// +/// This struct should be constructed by providing the following generic parameters: +/// * `Signer` - Usually the application specific key type (see `app_crypto`). +/// * `CreateTransaction` - A type that is able to produce signed transactions, +/// usually it's going to be the entire `Runtime` object. +/// * `Extrinsic` - A runtime-specific type for in-block extrinsics. +/// +/// If you only need the ability to submit unsigned transactions, +/// you may substitute both `Signer` and `CreateTransaction` with any type. +pub struct TransactionSubmitter { + _signer: rstd::marker::PhantomData<(Signer, CreateTransaction, Extrinsic)>, } impl Default for TransactionSubmitter { @@ -155,10 +248,60 @@ impl SubmitSignedTransaction for TransactionSubmitter type Signer = S; } -/// A blanket impl to use the same submitter for usigned transactions as well. +/// A blanket implementation to use the same submitter for unsigned transactions as well. impl SubmitUnsignedTransaction for TransactionSubmitter where T: crate::Trait, E: ExtrinsicT + codec::Encode, { type Extrinsic = E; } + +/// A blanket implementation to support local keystore of application-crypto types. +impl SigningAccountFinder for TransactionSubmitter where + T: crate::Trait, + C: CreateTransaction, + E: ExtrinsicT + codec::Encode, + S: Signer<>::Public, >::Signature>, + S: RuntimeAppPublic + + AppPublic + // TODO remove AppPublic? + + Into<::Generic> + + From<::Generic>, + S::Generic: Into>, + PublicOf: TryInto<::Generic>, + ::Signature: AppSignature, + // >::Signature: From< + // <::Signature as app_crypto::AppSignature>::Generic + // >, + < as SubmitSignedTransaction>::CreateTransaction as CreateTransaction as SubmitSignedTransaction>::Extrinsic>>::Signature: From<<::Signature as app_crypto::AppSignature>::Generic>, + Self: SubmitSignedTransaction, +{ + type SubmitTransaction = Self; + + fn find_local_keys(accounts: impl IntoIterator) -> Vec<( + T::AccountId, + PublicOf, + )> { + // Convert app-specific keys into generic ones. + let local_keys = S::all().into_iter().map(|app_key| { + app_key.into() + }).collect::::Generic>>(); + + // lookup accountids for the pub keys. + let mut local_accounts = local_keys.clone().into_iter().map(|pub_key| { + pub_key.into().into_account() + }).collect::>(); + // sort to allow bin-search. + local_accounts.sort(); + + // get all the matching accounts + accounts.into_iter().filter_map(|acc| { + let idx = local_accounts.binary_search(&acc).ok()?; + Some(( + local_accounts.get(idx)?.clone(), + local_keys.get(idx)?.clone().into(), + )) + }).collect() + } +} + From a645b90d6b785b36df52524d4c935a598da33402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 25 Nov 2019 17:32:10 +0100 Subject: [PATCH 02/30] Fix naming and bounds. --- bin/node/runtime/src/lib.rs | 31 ++++++++++++++--------- frame/system/src/offchain.rs | 49 ++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index c4752bae06d46..195128c698c66 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -715,20 +715,27 @@ mod tests { use super::*; use system::offchain::SubmitSignedTransaction; - fn is_submit_signed_transaction(_arg: T) where - T: SubmitSignedTransaction< - Runtime, - Call, - Extrinsic=UncheckedExtrinsic, - CreateTransaction=Runtime, - Signer=ImOnlineId, - >, - {} - #[test] fn validate_bounds() { - let x = SubmitTransaction::default(); - is_submit_signed_transaction(x); + fn is_submit_signed_transaction() where + T: SubmitSignedTransaction< + Runtime, + Call, + >, + {} + + fn is_sign_and_submit_transaction(_arg: T) where + T: SignAndSubmitTransaction< + Runtime, + Call, + Extrinsic=UncheckedExtrinsic, + CreateTransaction=Runtime, + Signer=ImOnlineId, + >, + {} + + is_submit_signed_transaction::(); + is_sign_and_submit_transaction::(); } #[test] diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index f4ff79e6aecd4..bfbbcee134d7a 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -94,12 +94,12 @@ impl Signer for TAnyAppPubl } } -/// Retrieves a public key type for given `SubmitSignedTransaction`. +/// Retrieves a public key type for given `SignAndSubmitTransaction`. pub type PublicOf = < - >::CreateTransaction + >::CreateTransaction as - CreateTransaction>::Extrinsic> + CreateTransaction>::Extrinsic> >::Public; /// A trait to sign and submit transactions in offchain calls. @@ -107,7 +107,7 @@ pub type PublicOf = /// NOTE: Most likely you should not implement this trait yourself. /// There is an implementation for `TransactionSubmitter` type, which /// you should use. -pub trait SubmitSignedTransaction { +pub trait SignAndSubmitTransaction { /// Unchecked extrinsic type. type Extrinsic: ExtrinsicT + codec::Encode; @@ -168,9 +168,9 @@ pub trait SubmitUnsignedTransaction { /// NOTE: Most likely you should not implement this trait yourself. /// There is an implementation for `TransactionSubmitter` type, which /// you should use. -pub trait SigningAccountFinder { - /// A `SubmitSignedTransaction` implementation. - type SubmitTransaction: SubmitSignedTransaction; +pub trait SubmitSignedTransaction { + /// A `SignAndSubmitTransaction` implementation. + type SignAndSubmit: SignAndSubmitTransaction; /// Find local keys that match given list of accounts. /// @@ -181,7 +181,7 @@ pub trait SigningAccountFinder { /// Such accounts can later be used to sign a payload or send signed transactions. fn find_local_keys(accounts: impl IntoIterator) -> Vec<( T::AccountId, - PublicOf, + PublicOf, )>; /// Create and submit signed transactions from supported accounts. @@ -201,7 +201,7 @@ pub trait SigningAccountFinder { let call = call.clone().into(); ( account, - Self::SubmitTransaction::sign_and_submit(call, pub_key) + Self::SignAndSubmit::sign_and_submit(call, pub_key) ) }).collect() } @@ -211,9 +211,9 @@ pub trait SigningAccountFinder { /// A default type used to submit transactions to the pool. /// /// This is passed into each runtime as an opaque associated type that can have either of: -/// - [`SubmitSignedTransaction`] +/// - [`SignAndSubmitTransaction`] /// - [`SubmitUnsignedTransaction`] -/// - [`SigningAccountFinder`] +/// - [`SubmitSignedTransaction`] /// and used accordingly. /// /// This struct should be constructed by providing the following generic parameters: @@ -237,7 +237,7 @@ impl Default for TransactionSubmitter { } /// A blanket implementation to simplify creation of transaction signer & submitter in the runtime. -impl SubmitSignedTransaction for TransactionSubmitter where +impl SignAndSubmitTransaction for TransactionSubmitter where T: crate::Trait, C: CreateTransaction, S: Signer<>::Public, >::Signature>, @@ -257,35 +257,28 @@ impl SubmitUnsignedTransaction for TransactionSubmitt } /// A blanket implementation to support local keystore of application-crypto types. -impl SigningAccountFinder for TransactionSubmitter where +impl SubmitSignedTransaction for TransactionSubmitter where T: crate::Trait, C: CreateTransaction, E: ExtrinsicT + codec::Encode, S: Signer<>::Public, >::Signature>, - S: RuntimeAppPublic - + AppPublic - // TODO remove AppPublic? - + Into<::Generic> - + From<::Generic>, + // Make sure we can unwrap the app crypto key. + S: RuntimeAppPublic + AppPublic + Into<::Generic>, + // Make sure we can convert from wrapped crypto to public key (e.g. `MultiSigner`) S::Generic: Into>, - PublicOf: TryInto<::Generic>, - ::Signature: AppSignature, - // >::Signature: From< - // <::Signature as app_crypto::AppSignature>::Generic - // >, - < as SubmitSignedTransaction>::CreateTransaction as CreateTransaction as SubmitSignedTransaction>::Extrinsic>>::Signature: From<<::Signature as app_crypto::AppSignature>::Generic>, - Self: SubmitSignedTransaction, + // For simplicity we require the same trait to implement `SignAndSubmitTransaction` too. + Self: SignAndSubmitTransaction, { - type SubmitTransaction = Self; + type SignAndSubmit = Self; fn find_local_keys(accounts: impl IntoIterator) -> Vec<( T::AccountId, - PublicOf, + PublicOf, )> { // Convert app-specific keys into generic ones. let local_keys = S::all().into_iter().map(|app_key| { app_key.into() - }).collect::::Generic>>(); + }).collect::::Generic>>(); // lookup accountids for the pub keys. let mut local_accounts = local_keys.clone().into_iter().map(|pub_key| { From f8c9540383e6e7481576476d341fea8e4937d1c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 25 Nov 2019 17:34:52 +0100 Subject: [PATCH 03/30] Forgotten import. --- bin/node/runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 195128c698c66..023252415f06c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -713,7 +713,7 @@ impl_runtime_apis! { #[cfg(test)] mod tests { use super::*; - use system::offchain::SubmitSignedTransaction; + use system::offchain::{SignAndSubmitTransaction, SubmitSignedTransaction}; #[test] fn validate_bounds() { @@ -724,7 +724,7 @@ mod tests { >, {} - fn is_sign_and_submit_transaction(_arg: T) where + fn is_sign_and_submit_transaction() where T: SignAndSubmitTransaction< Runtime, Call, From d715f645ab03dd63dc61901956b3802d6d39db5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 3 Dec 2019 10:06:20 +0100 Subject: [PATCH 04/30] Remove warning. --- frame/system/src/offchain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index bc6ad2a7c24f5..215ce9a173fa0 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -19,7 +19,7 @@ use codec::Encode; use rstd::convert::TryInto; use rstd::prelude::Vec; -use sp_runtime::app_crypto::{self, RuntimeAppPublic, AppPublic, AppSignature}; +use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature}; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount}; use support::debug; From 0c4c0378ff3857603f38aad3ba6ca9ea85eb8da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 3 Dec 2019 16:19:55 +0100 Subject: [PATCH 05/30] Make accounts optional, fix logic. --- frame/system/src/offchain.rs | 49 +++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 215ce9a173fa0..bc8ef3d517504 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -176,10 +176,11 @@ pub trait SubmitSignedTransaction { /// /// Technically it finds an intersection between given list of `AccountId`s /// and accounts that are represented by public keys in local keystore. + /// If `None` is passed it returns all accounts in the keystore. /// /// Returns both public keys and `AccountId`s of accounts that are available. /// Such accounts can later be used to sign a payload or send signed transactions. - fn find_local_keys(accounts: impl IntoIterator) -> Vec<( + fn find_local_keys(accounts: Option>) -> Vec<( T::AccountId, PublicOf, )>; @@ -191,9 +192,10 @@ pub trait SubmitSignedTransaction { /// with every of them. /// /// Returns a vector of results and account ids that were supported. + #[must_use] fn submit_signed( - accounts: impl IntoIterator, call: impl Into + Clone, + accounts: Option>, ) -> Vec<(T::AccountId, Result<(), ()>)> { let keys = Self::find_local_keys(accounts); @@ -271,30 +273,37 @@ impl SubmitSignedTransaction for TransactionSubmitter { type SignAndSubmit = Self; - fn find_local_keys(accounts: impl IntoIterator) -> Vec<( + fn find_local_keys(accounts: Option>) -> Vec<( T::AccountId, PublicOf, )> { // Convert app-specific keys into generic ones. - let local_keys = S::all().into_iter().map(|app_key| { - app_key.into() - }).collect::::Generic>>(); + let local_accounts_and_keys = S::all() + .into_iter() + .map(|app_key| { + // unwrap app-crypto + let generic_pub_key: ::Generic = app_key.into(); + // convert to expected public key type (might be MultiSigner) + let signer_pub_key: PublicOf = generic_pub_key.into(); + // lookup accountid for that pubkey + let account = signer_pub_key.clone().into_account(); + (account, signer_pub_key) + }).collect::>(); - // lookup accountids for the pub keys. - let mut local_accounts = local_keys.clone().into_iter().map(|pub_key| { - pub_key.into().into_account() - }).collect::>(); - // sort to allow bin-search. - local_accounts.sort(); + if let Some(accounts) = accounts { + let mut local_accounts_and_keys = local_accounts_and_keys; + // sort by accountId to allow bin-search. + local_accounts_and_keys.sort_by(|a, b| a.0.cmp(&b.0)); - // get all the matching accounts - accounts.into_iter().filter_map(|acc| { - let idx = local_accounts.binary_search(&acc).ok()?; - Some(( - local_accounts.get(idx)?.clone(), - local_keys.get(idx)?.clone().into(), - )) - }).collect() + // get all the matching accounts + accounts.into_iter().filter_map(|acc| { + let idx = local_accounts_and_keys.binary_search_by(|a| a.0.cmp(&acc)).ok()?; + local_accounts_and_keys.get(idx).cloned() + }).collect() + } else { + // just return all account ids and keys + local_accounts_and_keys + } } } From dc8d71c3f0967e9721181e1c0d0d2b3cedbfc306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 3 Dec 2019 16:29:33 +0100 Subject: [PATCH 06/30] Split the method to avoid confusing type error message. --- frame/system/src/offchain.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index bc8ef3d517504..1e831c813ae2d 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -193,12 +193,31 @@ pub trait SubmitSignedTransaction { /// /// Returns a vector of results and account ids that were supported. #[must_use] - fn submit_signed( + fn submit_signed_from( call: impl Into + Clone, - accounts: Option>, + accounts: impl IntoIterator, ) -> Vec<(T::AccountId, Result<(), ()>)> { - let keys = Self::find_local_keys(accounts); + let keys = Self::find_local_keys(Some(accounts)); + keys.into_iter().map(|(account, pub_key)| { + let call = call.clone().into(); + ( + account, + Self::SignAndSubmit::sign_and_submit(call, pub_key) + ) + }).collect() + } + /// Create and submit signed transactions from all local accounts. + /// + /// This method submits a signed transaction from all local accounts + /// for given application crypto. + /// + /// Returns a vector of results and account ids that were supported. + #[must_use] + fn submit_signed( + call: impl Into + Clone, + ) -> Vec<(T::AccountId, Result<(), ()>)> { + let keys = Self::find_local_keys(None as Option>); keys.into_iter().map(|(account, pub_key)| { let call = call.clone().into(); ( From 173cf6c1b300ac1a0ca8b30b7f71c41d6a629da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 28 Nov 2019 10:39:08 +0100 Subject: [PATCH 07/30] Move executor tests to integration. --- bin/node/executor/src/lib.rs | 1206 ----------------------------- bin/node/executor/tests/basic.rs | 818 +++++++++++++++++++ bin/node/executor/tests/common.rs | 155 ++++ bin/node/executor/tests/fees.rs | 329 ++++++++ bin/node/runtime/src/lib.rs | 2 +- 5 files changed, 1303 insertions(+), 1207 deletions(-) create mode 100644 bin/node/executor/tests/basic.rs create mode 100644 bin/node/executor/tests/common.rs create mode 100644 bin/node/executor/tests/fees.rs diff --git a/bin/node/executor/src/lib.rs b/bin/node/executor/src/lib.rs index b2410ed15edeb..b5c18625ad93c 100644 --- a/bin/node/executor/src/lib.rs +++ b/bin/node/executor/src/lib.rs @@ -27,1209 +27,3 @@ native_executor_instance!( node_runtime::api::dispatch, node_runtime::native_version ); - -#[cfg(test)] -mod tests { - use sc_executor::error::Result; - use super::Executor; - use {balances, contracts, indices, system, timestamp}; - use codec::{Encode, Decode, Joiner}; - use runtime_support::{ - Hashable, StorageValue, StorageMap, - traits::Currency, - weights::{GetDispatchInfo, DispatchInfo, DispatchClass}, - }; - use state_machine::TestExternalities as CoreTestExternalities; - use primitives::{ - Blake2Hasher, NeverNativeValue, NativeOrEncoded, map, - traits::{CodeExecutor, Externalities}, storage::well_known_keys, - }; - use sp_runtime::{ - Fixed64, traits::{Header as HeaderT, Hash as HashT, Convert}, ApplyExtrinsicResult, - transaction_validity::InvalidTransaction, - }; - use contracts::ContractAddressFor; - use sc_executor::{NativeExecutor, WasmExecutionMethod}; - use system::{EventRecord, Phase}; - use node_runtime::{ - Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, BuildStorage, - System, TransactionPayment, Event, TransferFee, TransactionBaseFee, TransactionByteFee, - WeightFeeCoefficient, constants::currency::*, - }; - use node_runtime::impls::LinearWeightToFee; - use node_primitives::{Balance, Hash, BlockNumber}; - use node_testing::keyring::*; - use wabt; - - /// The wasm runtime code. - /// - /// `compact` since it is after post-processing with wasm-gc which performs tree-shaking thus - /// making the binary slimmer. There is a convention to use compact version of the runtime - /// as canonical. This is why `native_executor_instance` also uses the compact version of the - /// runtime. - const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY; - - /// The wasm runtime binary which hasn't undergone the compacting process. - /// - /// The idea here is to pass it as the current runtime code to the executor so the executor will - /// have to execute provided wasm code instead of the native equivalent. This trick is used to - /// test code paths that differ between native and wasm versions. - const BLOATY_CODE: &[u8] = node_runtime::WASM_BINARY_BLOATY; - - const GENESIS_HASH: [u8; 32] = [69u8; 32]; - - const VERSION: u32 = node_runtime::VERSION.spec_version; - - type TestExternalities = CoreTestExternalities; - - fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { - node_testing::keyring::sign(xt, VERSION, GENESIS_HASH) - } - - /// Default transfer fee - fn transfer_fee(extrinsic: &E, fee_multiplier: Fixed64) -> Balance { - let length_fee = TransactionBaseFee::get() + - TransactionByteFee::get() * - (extrinsic.encode().len() as Balance); - - let weight = default_transfer_call().get_dispatch_info().weight; - let weight_fee = ::WeightToFee::convert(weight); - - fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee) + TransferFee::get() - } - - fn default_transfer_call() -> balances::Call { - balances::Call::transfer::(bob().into(), 69 * DOLLARS) - } - - fn xt() -> UncheckedExtrinsic { - sign(CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, 0))), - function: Call::Balances(default_transfer_call()), - }) - } - - fn from_block_number(n: u32) -> Header { - Header::new(n, Default::default(), Default::default(), [69; 32].into(), Default::default()) - } - - fn executor() -> NativeExecutor { - NativeExecutor::new(WasmExecutionMethod::Interpreted, None) - } - - fn set_heap_pages(ext: &mut E, heap_pages: u64) { - ext.place_storage(well_known_keys::HEAP_PAGES.to_vec(), Some(heap_pages.encode())); - } - - fn executor_call< - R:Decode + Encode + PartialEq, - NC: FnOnce() -> std::result::Result + std::panic::UnwindSafe - >( - t: &mut TestExternalities, - method: &str, - data: &[u8], - use_native: bool, - native_call: Option, - ) -> (Result>, bool) { - let mut t = t.ext(); - executor().call::<_, R, NC>( - &mut t, - method, - data, - use_native, - native_call, - ) - } - - #[test] - fn panic_execution_with_foreign_code_gives_error() { - let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ - >::hashed_key_for(alice()) => { - 69_u128.encode() - }, - >::hashed_key().to_vec() => { - 69_u128.encode() - }, - >::hashed_key().to_vec() => { - 0_u128.encode() - }, - >::hashed_key_for(0) => { - vec![0u8; 32] - } - ], map![])); - - let r = executor_call:: _>( - &mut t, - "Core_initialize_block", - &vec![].and(&from_block_number(1u32)), - true, - None, - ).0; - assert!(r.is_ok()); - let v = executor_call:: _>( - &mut t, - "BlockBuilder_apply_extrinsic", - &vec![].and(&xt()), - true, - None, - ).0.unwrap(); - let r = ApplyExtrinsicResult::decode(&mut &v.as_encoded()[..]).unwrap(); - assert_eq!(r, Err(InvalidTransaction::Payment.into())); - } - - #[test] - fn bad_extrinsic_with_native_equivalent_code_gives_error() { - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ - >::hashed_key_for(alice()) => { - 69_u128.encode() - }, - >::hashed_key().to_vec() => { - 69_u128.encode() - }, - >::hashed_key().to_vec() => { - 0_u128.encode() - }, - >::hashed_key_for(0) => { - vec![0u8; 32] - } - ], map![])); - - let r = executor_call:: _>( - &mut t, - "Core_initialize_block", - &vec![].and(&from_block_number(1u32)), - true, - None, - ).0; - assert!(r.is_ok()); - let v = executor_call:: _>( - &mut t, - "BlockBuilder_apply_extrinsic", - &vec![].and(&xt()), - true, - None, - ).0.unwrap(); - let r = ApplyExtrinsicResult::decode(&mut &v.as_encoded()[..]).unwrap(); - assert_eq!(r, Err(InvalidTransaction::Payment.into())); - } - - #[test] - fn successful_execution_with_native_equivalent_code_gives_ok() { - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => vec![0u8; 16], - >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); - - let r = executor_call:: _>( - &mut t, - "Core_initialize_block", - &vec![].and(&from_block_number(1u32)), - true, - None, - ).0; - assert!(r.is_ok()); - - let fm = t.execute_with(TransactionPayment::next_fee_multiplier); - - let r = executor_call:: _>( - &mut t, - "BlockBuilder_apply_extrinsic", - &vec![].and(&xt()), - true, - None, - ).0; - assert!(r.is_ok()); - - t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); - assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); - }); - } - - #[test] - fn successful_execution_with_foreign_code_gives_ok() { - let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => vec![0u8; 16], - >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); - - let r = executor_call:: _>( - &mut t, - "Core_initialize_block", - &vec![].and(&from_block_number(1u32)), - true, - None, - ).0; - assert!(r.is_ok()); - - let fm = t.execute_with(TransactionPayment::next_fee_multiplier); - - let r = executor_call:: _>( - &mut t, - "BlockBuilder_apply_extrinsic", - &vec![].and(&xt()), - true, - None, - ).0; - assert!(r.is_ok()); - - t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); - assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); - }); - } - - fn new_test_ext(code: &[u8], support_changes_trie: bool) -> TestExternalities { - let mut ext = TestExternalities::new_with_code( - code, - node_testing::genesis::config(support_changes_trie, Some(code)).build_storage().unwrap(), - ); - ext.changes_trie_storage().insert(0, GENESIS_HASH.into(), Default::default()); - ext - } - - fn construct_block( - env: &mut TestExternalities, - number: BlockNumber, - parent_hash: Hash, - extrinsics: Vec, - ) -> (Vec, Hash) { - use trie::{TrieConfiguration, trie_types::Layout}; - - // sign extrinsics. - let extrinsics = extrinsics.into_iter().map(sign).collect::>(); - - // calculate the header fields that we can. - let extrinsics_root = Layout::::ordered_trie_root( - extrinsics.iter().map(Encode::encode) - ).to_fixed_bytes() - .into(); - - let header = Header { - parent_hash, - number, - extrinsics_root, - state_root: Default::default(), - digest: Default::default(), - }; - - // execute the block to get the real header. - executor_call:: _>( - env, - "Core_initialize_block", - &header.encode(), - true, - None, - ).0.unwrap(); - - for i in extrinsics.iter() { - executor_call:: _>( - env, - "BlockBuilder_apply_extrinsic", - &i.encode(), - true, - None, - ).0.unwrap(); - } - - let header = match executor_call:: _>( - env, - "BlockBuilder_finalize_block", - &[0u8;0], - true, - None, - ).0.unwrap() { - NativeOrEncoded::Native(_) => unreachable!(), - NativeOrEncoded::Encoded(h) => Header::decode(&mut &h[..]).unwrap(), - }; - - let hash = header.blake2_256(); - (Block { header, extrinsics }.encode(), hash.into()) - } - - fn changes_trie_block() -> (Vec, Hash) { - construct_block( - &mut new_test_ext(COMPACT_CODE, true), - 1, - GENESIS_HASH.into(), - vec![ - CheckedExtrinsic { - signed: None, - function: Call::Timestamp(timestamp::Call::set(42 * 1000)), - }, - CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, 0))), - function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), - }, - ] - ) - } - - // block 1 and 2 must be created together to ensure transactions are only signed once (since they - // are not guaranteed to be deterministic) and to ensure that the correct state is propagated - // from block1's execution to block2 to derive the correct storage_root. - fn blocks() -> ((Vec, Hash), (Vec, Hash)) { - let mut t = new_test_ext(COMPACT_CODE, false); - let block1 = construct_block( - &mut t, - 1, - GENESIS_HASH.into(), - vec![ - CheckedExtrinsic { - signed: None, - function: Call::Timestamp(timestamp::Call::set(42 * 1000)), - }, - CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, 0))), - function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), - }, - ] - ); - let block2 = construct_block( - &mut t, - 2, - block1.1.clone(), - vec![ - CheckedExtrinsic { - signed: None, - function: Call::Timestamp(timestamp::Call::set(52 * 1000)), - }, - CheckedExtrinsic { - signed: Some((bob(), signed_extra(0, 0))), - function: Call::Balances(balances::Call::transfer(alice().into(), 5 * DOLLARS)), - }, - CheckedExtrinsic { - signed: Some((alice(), signed_extra(1, 0))), - function: Call::Balances(balances::Call::transfer(bob().into(), 15 * DOLLARS)), - } - ] - ); - - // session change => consensus authorities change => authorities change digest item appears - let digest = Header::decode(&mut &block2.0[..]).unwrap().digest; - assert_eq!(digest.logs().len(), 0); - - (block1, block2) - } - - fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec, Hash) { - construct_block( - &mut new_test_ext(COMPACT_CODE, false), - 1, - GENESIS_HASH.into(), - vec![ - CheckedExtrinsic { - signed: None, - function: Call::Timestamp(timestamp::Call::set(time * 1000)), - }, - CheckedExtrinsic { - signed: Some((alice(), signed_extra(nonce, 0))), - function: Call::System(system::Call::remark(vec![0; size])), - } - ] - ) - } - - #[test] - fn full_native_block_import_works() { - let mut t = new_test_ext(COMPACT_CODE, false); - - let (block1, block2) = blocks(); - - let mut alice_last_known_balance: Balance = Default::default(); - let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); - - executor_call:: _>( - &mut t, - "Core_execute_block", - &block1.0, - true, - None, - ).0.unwrap(); - - t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); - assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); - alice_last_known_balance = Balances::total_balance(&alice()); - let events = vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: Event::system(system::Event::ExtrinsicSuccess( - DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::treasury(treasury::RawEvent::Deposit(1984800000000)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::balances(balances::RawEvent::Transfer( - alice().into(), - bob().into(), - 69 * DOLLARS, - 1 * CENTS, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::system(system::Event::ExtrinsicSuccess( - DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } - )), - topics: vec![], - }, - ]; - assert_eq!(System::events(), events); - }); - - fm = t.execute_with(TransactionPayment::next_fee_multiplier); - - executor_call:: _>( - &mut t, - "Core_execute_block", - &block2.0, - true, - None, - ).0.unwrap(); - - t.execute_with(|| { - assert_eq!( - Balances::total_balance(&alice()), - alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), - ); - assert_eq!( - Balances::total_balance(&bob()), - 179 * DOLLARS - transfer_fee(&xt(), fm), - ); - let events = vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: Event::system(system::Event::ExtrinsicSuccess( - DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::treasury(treasury::RawEvent::Deposit(1984780231392)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::balances( - balances::RawEvent::Transfer( - bob().into(), - alice().into(), - 5 * DOLLARS, - 1 * CENTS, - ) - ), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(1), - event: Event::system(system::Event::ExtrinsicSuccess( - DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(2), - event: Event::treasury(treasury::RawEvent::Deposit(1984780231392)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(2), - event: Event::balances( - balances::RawEvent::Transfer( - alice().into(), - bob().into(), - 15 * DOLLARS, - 1 * CENTS, - ) - ), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(2), - event: Event::system(system::Event::ExtrinsicSuccess( - DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } - )), - topics: vec![], - }, - ]; - assert_eq!(System::events(), events); - }); - } - - #[test] - fn full_wasm_block_import_works() { - let mut t = new_test_ext(COMPACT_CODE, false); - - let (block1, block2) = blocks(); - - let mut alice_last_known_balance: Balance = Default::default(); - let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); - - executor_call:: _>( - &mut t, - "Core_execute_block", - &block1.0, - false, - None, - ).0.unwrap(); - - t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); - assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); - alice_last_known_balance = Balances::total_balance(&alice()); - }); - - fm = t.execute_with(TransactionPayment::next_fee_multiplier); - - executor_call:: _>( - &mut t, - "Core_execute_block", - &block2.0, - false, - None, - ).0.unwrap(); - - t.execute_with(|| { - assert_eq!( - Balances::total_balance(&alice()), - alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), - ); - assert_eq!( - Balances::total_balance(&bob()), - 179 * DOLLARS - 1 * transfer_fee(&xt(), fm), - ); - }); - } - - const CODE_TRANSFER: &str = r#" -(module - ;; ext_call( - ;; callee_ptr: u32, - ;; callee_len: u32, - ;; gas: u64, - ;; value_ptr: u32, - ;; value_len: u32, - ;; input_data_ptr: u32, - ;; input_data_len: u32 - ;; ) -> u32 - (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) - (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) - (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) - (import "env" "memory" (memory 1 1)) - (func (export "deploy") - ) - (func (export "call") - (block $fail - ;; load and check the input data (which is stored in the scratch buffer). - ;; fail if the input size is not != 4 - (br_if $fail - (i32.ne - (i32.const 4) - (call $ext_scratch_size) - ) - ) - - (call $ext_scratch_read - (i32.const 0) - (i32.const 0) - (i32.const 4) - ) - - - (br_if $fail - (i32.ne - (i32.load8_u (i32.const 0)) - (i32.const 0) - ) - ) - (br_if $fail - (i32.ne - (i32.load8_u (i32.const 1)) - (i32.const 1) - ) - ) - (br_if $fail - (i32.ne - (i32.load8_u (i32.const 2)) - (i32.const 2) - ) - ) - (br_if $fail - (i32.ne - (i32.load8_u (i32.const 3)) - (i32.const 3) - ) - ) - - (drop - (call $ext_call - (i32.const 4) ;; Pointer to "callee" address. - (i32.const 32) ;; Length of "callee" address. - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. - (i32.const 36) ;; Pointer to the buffer with value to transfer - (i32.const 16) ;; Length of the buffer with value to transfer. - (i32.const 0) ;; Pointer to input data buffer address - (i32.const 0) ;; Length of input data buffer - ) - ) - - (return) - ) - unreachable - ) - ;; Destination AccountId to transfer the funds. - ;; Represented by H256 (32 bytes long) in little endian. - (data (i32.const 4) - "\09\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" - "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" - "\00\00\00\00" - ) - ;; Amount of value to transfer. - ;; Represented by u128 (16 bytes long) in little endian. - (data (i32.const 36) - "\06\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" - "\00\00" - ) -) -"#; - - #[test] - fn deploying_wasm_contract_should_work() { - let transfer_code = wabt::wat2wasm(CODE_TRANSFER).unwrap(); - let transfer_ch = ::Hashing::hash(&transfer_code); - - let addr = ::DetermineContractAddress::contract_address_for( - &transfer_ch, - &[], - &charlie(), - ); - - let b = construct_block( - &mut new_test_ext(COMPACT_CODE, false), - 1, - GENESIS_HASH.into(), - vec![ - CheckedExtrinsic { - signed: None, - function: Call::Timestamp(timestamp::Call::set(42 * 1000)), - }, - CheckedExtrinsic { - signed: Some((charlie(), signed_extra(0, 0))), - function: Call::Contracts( - contracts::Call::put_code::(10_000, transfer_code) - ), - }, - CheckedExtrinsic { - signed: Some((charlie(), signed_extra(1, 0))), - function: Call::Contracts( - contracts::Call::instantiate::(1 * DOLLARS, 10_000, transfer_ch, Vec::new()) - ), - }, - CheckedExtrinsic { - signed: Some((charlie(), signed_extra(2, 0))), - function: Call::Contracts( - contracts::Call::call::( - indices::address::Address::Id(addr.clone()), - 10, - 10_000, - vec![0x00, 0x01, 0x02, 0x03] - ) - ), - }, - ] - ); - - let mut t = new_test_ext(COMPACT_CODE, false); - - executor_call:: _>( - &mut t, - "Core_execute_block", - &b.0, - false, - None, - ).0.unwrap(); - - t.execute_with(|| { - // Verify that the contract constructor worked well and code of TRANSFER contract is actually deployed. - assert_eq!( - &contracts::ContractInfoOf::::get(addr) - .and_then(|c| c.get_alive()) - .unwrap() - .code_hash, - &transfer_ch - ); - }); - } - - #[test] - fn wasm_big_block_import_fails() { - let mut t = new_test_ext(COMPACT_CODE, false); - - set_heap_pages(&mut t.ext(), 4); - - let result = executor_call:: _>( - &mut t, - "Core_execute_block", - &block_with_size(42, 0, 120_000).0, - false, - None, - ).0; - assert!(result.is_err()); // Err(Wasmi(Trap(Trap { kind: Host(AllocatorOutOfSpace) }))) - } - - #[test] - fn native_big_block_import_succeeds() { - let mut t = new_test_ext(COMPACT_CODE, false); - - executor_call:: _>( - &mut t, - "Core_execute_block", - &block_with_size(42, 0, 120_000).0, - true, - None, - ).0.unwrap(); - } - - #[test] - fn native_big_block_import_fails_on_fallback() { - let mut t = new_test_ext(COMPACT_CODE, false); - - assert!( - executor_call:: _>( - &mut t, - "Core_execute_block", - &block_with_size(42, 0, 120_000).0, - false, - None, - ).0.is_err() - ); - } - - #[test] - fn panic_execution_gives_error() { - let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ - >::hashed_key_for(alice()) => { - 0_u128.encode() - }, - >::hashed_key().to_vec() => { - 0_u128.encode() - }, - >::hashed_key().to_vec() => vec![0u8; 16], - >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); - - let r = executor_call:: _>( - &mut t, - "Core_initialize_block", - &vec![].and(&from_block_number(1u32)), - false, - None, - ).0; - assert!(r.is_ok()); - let r = executor_call:: _>( - &mut t, - "BlockBuilder_apply_extrinsic", - &vec![].and(&xt()), - false, - None, - ).0.unwrap().into_encoded(); - let r = ApplyExtrinsicResult::decode(&mut &r[..]).unwrap(); - assert_eq!(r, Err(InvalidTransaction::Payment.into())); - } - - #[test] - fn successful_execution_gives_ok() { - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => vec![0u8; 16], - >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); - - let r = executor_call:: _>( - &mut t, - "Core_initialize_block", - &vec![].and(&from_block_number(1u32)), - false, - None, - ).0; - assert!(r.is_ok()); - let fm = t.execute_with(TransactionPayment::next_fee_multiplier); - let r = executor_call:: _>( - &mut t, - "BlockBuilder_apply_extrinsic", - &vec![].and(&xt()), - false, - None, - ).0.unwrap().into_encoded(); - ApplyExtrinsicResult::decode(&mut &r[..]) - .unwrap() - .expect("Extrinsic could be applied") - .expect("Extrinsic did not fail"); - - t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt(), fm)); - assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); - }); - } - - #[test] - fn full_native_block_import_works_with_changes_trie() { - let block1 = changes_trie_block(); - let block_data = block1.0; - let block = Block::decode(&mut &block_data[..]).unwrap(); - - let mut t = new_test_ext(COMPACT_CODE, true); - executor_call:: _>( - &mut t, - "Core_execute_block", - &block.encode(), - true, - None, - ).0.unwrap(); - - assert!(t.ext().storage_changes_root(&GENESIS_HASH.encode()).unwrap().is_some()); - } - - #[test] - fn full_wasm_block_import_works_with_changes_trie() { - let block1 = changes_trie_block(); - - let mut t = new_test_ext(COMPACT_CODE, true); - executor_call:: _>( - &mut t, - "Core_execute_block", - &block1.0, - false, - None, - ).0.unwrap(); - - assert!(t.ext().storage_changes_root(&GENESIS_HASH.encode()).unwrap().is_some()); - } - - #[test] - fn should_import_block_with_test_client() { - use node_testing::client::{ClientExt, TestClientBuilderExt, TestClientBuilder, consensus::BlockOrigin}; - - let client = TestClientBuilder::new().build(); - let block1 = changes_trie_block(); - let block_data = block1.0; - let block = node_primitives::Block::decode(&mut &block_data[..]).unwrap(); - - client.import(BlockOrigin::Own, block).unwrap(); - } - - - #[test] - fn fee_multiplier_increases_and_decreases_on_big_weight() { - let mut t = new_test_ext(COMPACT_CODE, false); - - // initial fee multiplier must be zero - let mut prev_multiplier = Fixed64::from_parts(0); - - t.execute_with(|| { - assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier); - }); - - let mut tt = new_test_ext(COMPACT_CODE, false); - - // big one in terms of weight. - let block1 = construct_block( - &mut tt, - 1, - GENESIS_HASH.into(), - vec![ - CheckedExtrinsic { - signed: None, - function: Call::Timestamp(timestamp::Call::set(42 * 1000)), - }, - CheckedExtrinsic { - signed: Some((charlie(), signed_extra(0, 0))), - function: Call::System(system::Call::fill_block()), - } - ] - ); - - // small one in terms of weight. - let block2 = construct_block( - &mut tt, - 2, - block1.1.clone(), - vec![ - CheckedExtrinsic { - signed: None, - function: Call::Timestamp(timestamp::Call::set(52 * 1000)), - }, - CheckedExtrinsic { - signed: Some((charlie(), signed_extra(1, 0))), - function: Call::System(system::Call::remark(vec![0; 1])), - } - ] - ); - - println!("++ Block 1 size: {} / Block 2 size {}", block1.0.encode().len(), block2.0.encode().len()); - - // execute a big block. - executor_call:: _>( - &mut t, - "Core_execute_block", - &block1.0, - true, - None, - ).0.unwrap(); - - // weight multiplier is increased for next block. - t.execute_with(|| { - let fm = TransactionPayment::next_fee_multiplier(); - println!("After a big block: {:?} -> {:?}", prev_multiplier, fm); - assert!(fm > prev_multiplier); - prev_multiplier = fm; - }); - - // execute a big block. - executor_call:: _>( - &mut t, - "Core_execute_block", - &block2.0, - true, - None, - ).0.unwrap(); - - // weight multiplier is increased for next block. - t.execute_with(|| { - let fm = TransactionPayment::next_fee_multiplier(); - println!("After a small block: {:?} -> {:?}", prev_multiplier, fm); - assert!(fm < prev_multiplier); - }); - } - - #[test] - fn transaction_fee_is_correct_ultimate() { - // This uses the exact values of substrate-node. - // - // weight of transfer call as of now: 1_000_000 - // if weight of the cheapest weight would be 10^7, this would be 10^9, which is: - // - 1 MILLICENTS in substrate node. - // - 1 milli-dot based on current polkadot runtime. - // (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`) - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ - >::hashed_key_for(alice()) => { - (100 * DOLLARS).encode() - }, - >::hashed_key_for(bob()) => { - (10 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => { - (110 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => vec![0u8; 16], - >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); - - let tip = 1_000_000; - let xt = sign(CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, tip))), - function: Call::Balances(default_transfer_call()), - }); - - let r = executor_call:: _>( - &mut t, - "Core_initialize_block", - &vec![].and(&from_block_number(1u32)), - true, - None, - ).0; - - assert!(r.is_ok()); - let r = executor_call:: _>( - &mut t, - "BlockBuilder_apply_extrinsic", - &vec![].and(&xt.clone()), - true, - None, - ).0; - assert!(r.is_ok()); - - t.execute_with(|| { - assert_eq!(Balances::total_balance(&bob()), (10 + 69) * DOLLARS); - // Components deducted from alice's balances: - // - Weight fee - // - Length fee - // - Tip - // - Creation-fee of bob's account. - let mut balance_alice = (100 - 69) * DOLLARS; - - let length_fee = TransactionBaseFee::get() + - TransactionByteFee::get() * - (xt.clone().encode().len() as Balance); - balance_alice -= length_fee; - - let weight = default_transfer_call().get_dispatch_info().weight; - let weight_fee = LinearWeightToFee::::convert(weight); - - // we know that weight to fee multiplier is effect-less in block 1. - assert_eq!(weight_fee as Balance, MILLICENTS); - balance_alice -= weight_fee; - - balance_alice -= tip; - balance_alice -= TransferFee::get(); - - assert_eq!(Balances::total_balance(&alice()), balance_alice); - }); - } - - #[test] - #[should_panic] - #[cfg(feature = "stress-test")] - fn block_weight_capacity_report() { - // Just report how many transfer calls you could fit into a block. The number should at least - // be a few hundred (250 at the time of writing but can change over time). Runs until panic. - use node_primitives::Index; - - // execution ext. - let mut t = new_test_ext(COMPACT_CODE, false); - // setup ext. - let mut tt = new_test_ext(COMPACT_CODE, false); - - let factor = 50; - let mut time = 10; - let mut nonce: Index = 0; - let mut block_number = 1; - let mut previous_hash: Hash = GENESIS_HASH.into(); - - loop { - let num_transfers = block_number * factor; - let mut xts = (0..num_transfers).map(|i| CheckedExtrinsic { - signed: Some((charlie(), signed_extra(nonce + i as Index, 0))), - function: Call::Balances(balances::Call::transfer(bob().into(), 0)), - }).collect::>(); - - xts.insert(0, CheckedExtrinsic { - signed: None, - function: Call::Timestamp(timestamp::Call::set(time * 1000)), - }); - - // NOTE: this is super slow. Can probably be improved. - let block = construct_block( - &mut tt, - block_number, - previous_hash, - xts - ); - - let len = block.0.len(); - print!( - "++ Executing block with {} transfers. Block size = {} bytes / {} kb / {} mb", - num_transfers, - len, - len / 1024, - len / 1024 / 1024, - ); - - let r = executor_call:: _>( - &mut t, - "Core_execute_block", - &block.0, - true, - None, - ).0; - - println!(" || Result = {:?}", r); - assert!(r.is_ok()); - - previous_hash = block.1; - nonce += num_transfers; - time += 10; - block_number += 1; - } - } - - #[test] - #[should_panic] - #[cfg(feature = "stress-test")] - fn block_length_capacity_report() { - // Just report how big a block can get. Executes until panic. Should be ignored unless if - // manually inspected. The number should at least be a few megabytes (5 at the time of - // writing but can change over time). - use node_primitives::Index; - - // execution ext. - let mut t = new_test_ext(COMPACT_CODE, false); - // setup ext. - let mut tt = new_test_ext(COMPACT_CODE, false); - - let factor = 256 * 1024; - let mut time = 10; - let mut nonce: Index = 0; - let mut block_number = 1; - let mut previous_hash: Hash = GENESIS_HASH.into(); - - loop { - // NOTE: this is super slow. Can probably be improved. - let block = construct_block( - &mut tt, - block_number, - previous_hash, - vec![ - CheckedExtrinsic { - signed: None, - function: Call::Timestamp(timestamp::Call::set(time * 1000)), - }, - CheckedExtrinsic { - signed: Some((charlie(), signed_extra(nonce, 0))), - function: Call::System(system::Call::remark(vec![0u8; (block_number * factor) as usize])), - }, - ] - ); - - let len = block.0.len(); - print!( - "++ Executing block with big remark. Block size = {} bytes / {} kb / {} mb", - len, - len / 1024, - len / 1024 / 1024, - ); - - let r = executor_call:: _>( - &mut t, - "Core_execute_block", - &block.0, - true, - None, - ).0; - - println!(" || Result = {:?}", r); - assert!(r.is_ok()); - - previous_hash = block.1; - nonce += 1; - time += 10; - block_number += 1; - } - } -} diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs new file mode 100644 index 0000000000000..990e0cdf4d9a3 --- /dev/null +++ b/bin/node/executor/tests/basic.rs @@ -0,0 +1,818 @@ +// Copyright 2018-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 . + +use {balances, contracts, indices, timestamp}; +use codec::{Encode, Decode, Joiner}; +use runtime_support::{ + StorageValue, StorageMap, + traits::Currency, + weights::{GetDispatchInfo, DispatchInfo, DispatchClass}, +}; +use primitives::{ + Blake2Hasher, NeverNativeValue, map, + traits::Externalities, + storage::well_known_keys, +}; +use sp_runtime::{ + ApplyExtrinsicResult, Fixed64, + traits::{Hash as HashT, Convert}, + transaction_validity::InvalidTransaction, +}; +use contracts::ContractAddressFor; +use system::{self, EventRecord, Phase}; + +use node_runtime::{ + Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, + System, TransactionPayment, Event, + TransferFee, TransactionBaseFee, TransactionByteFee, + constants::currency::*, +}; +use node_primitives::{Balance, Hash}; +use wabt; +use node_testing::keyring::*; + +mod common; + +use self::common::{*, sign}; + + +/// The wasm runtime binary which hasn't undergone the compacting process. +/// +/// The idea here is to pass it as the current runtime code to the executor so the executor will +/// have to execute provided wasm code instead of the native equivalent. This trick is used to +/// test code paths that differ between native and wasm versions. +pub const BLOATY_CODE: &[u8] = node_runtime::WASM_BINARY_BLOATY; + +/// Default transfer fee +fn transfer_fee(extrinsic: &E, fee_multiplier: Fixed64) -> Balance { + let length_fee = TransactionBaseFee::get() + + TransactionByteFee::get() * + (extrinsic.encode().len() as Balance); + + let weight = default_transfer_call().get_dispatch_info().weight; + let weight_fee = ::WeightToFee::convert(weight); + + fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee) + TransferFee::get() +} + +fn xt() -> UncheckedExtrinsic { + sign(CheckedExtrinsic { + signed: Some((alice(), signed_extra(0, 0))), + function: Call::Balances(default_transfer_call()), + }) +} + +fn set_heap_pages(ext: &mut E, heap_pages: u64) { + ext.place_storage(well_known_keys::HEAP_PAGES.to_vec(), Some(heap_pages.encode())); +} + +fn changes_trie_block() -> (Vec, Hash) { + construct_block( + &mut new_test_ext(COMPACT_CODE, true), + 1, + GENESIS_HASH.into(), + vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(42 * 1000)), + }, + CheckedExtrinsic { + signed: Some((alice(), signed_extra(0, 0))), + function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), + }, + ] + ) +} + + +/// block 1 and 2 must be created together to ensure transactions are only signed once (since they +/// are not guaranteed to be deterministic) and to ensure that the correct state is propagated +/// from block1's execution to block2 to derive the correct storage_root. +fn blocks() -> ((Vec, Hash), (Vec, Hash)) { + let mut t = new_test_ext(COMPACT_CODE, false); + let block1 = construct_block( + &mut t, + 1, + GENESIS_HASH.into(), + vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(42 * 1000)), + }, + CheckedExtrinsic { + signed: Some((alice(), signed_extra(0, 0))), + function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), + }, + ] + ); + let block2 = construct_block( + &mut t, + 2, + block1.1.clone(), + vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(52 * 1000)), + }, + CheckedExtrinsic { + signed: Some((bob(), signed_extra(0, 0))), + function: Call::Balances(balances::Call::transfer(alice().into(), 5 * DOLLARS)), + }, + CheckedExtrinsic { + signed: Some((alice(), signed_extra(1, 0))), + function: Call::Balances(balances::Call::transfer(bob().into(), 15 * DOLLARS)), + } + ] + ); + + // session change => consensus authorities change => authorities change digest item appears + let digest = Header::decode(&mut &block2.0[..]).unwrap().digest; + assert_eq!(digest.logs().len(), 0); + + (block1, block2) +} + +fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec, Hash) { + construct_block( + &mut new_test_ext(COMPACT_CODE, false), + 1, + GENESIS_HASH.into(), + vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(time * 1000)), + }, + CheckedExtrinsic { + signed: Some((alice(), signed_extra(nonce, 0))), + function: Call::System(system::Call::remark(vec![0; size])), + } + ] + ) +} + +#[test] +fn panic_execution_with_foreign_code_gives_error() { + let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ + >::hashed_key_for(alice()) => { + 69_u128.encode() + }, + >::hashed_key().to_vec() => { + 69_u128.encode() + }, + >::hashed_key().to_vec() => { + 0_u128.encode() + }, + >::hashed_key_for(0) => { + vec![0u8; 32] + } + ], map![])); + + let r = executor_call:: _>( + &mut t, + "Core_initialize_block", + &vec![].and(&from_block_number(1u32)), + true, + None, + ).0; + assert!(r.is_ok()); + let v = executor_call:: _>( + &mut t, + "BlockBuilder_apply_extrinsic", + &vec![].and(&xt()), + true, + None, + ).0.unwrap(); + let r = ApplyExtrinsicResult::decode(&mut &v.as_encoded()[..]).unwrap(); + assert_eq!(r, Err(InvalidTransaction::Payment.into())); +} + +#[test] +fn bad_extrinsic_with_native_equivalent_code_gives_error() { + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ + >::hashed_key_for(alice()) => { + 69_u128.encode() + }, + >::hashed_key().to_vec() => { + 69_u128.encode() + }, + >::hashed_key().to_vec() => { + 0_u128.encode() + }, + >::hashed_key_for(0) => { + vec![0u8; 32] + } + ], map![])); + + let r = executor_call:: _>( + &mut t, + "Core_initialize_block", + &vec![].and(&from_block_number(1u32)), + true, + None, + ).0; + assert!(r.is_ok()); + let v = executor_call:: _>( + &mut t, + "BlockBuilder_apply_extrinsic", + &vec![].and(&xt()), + true, + None, + ).0.unwrap(); + let r = ApplyExtrinsicResult::decode(&mut &v.as_encoded()[..]).unwrap(); + assert_eq!(r, Err(InvalidTransaction::Payment.into())); +} + +#[test] +fn successful_execution_with_native_equivalent_code_gives_ok() { + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ + >::hashed_key_for(alice()) => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => vec![0u8; 16], + >::hashed_key_for(0) => vec![0u8; 32] + ], map![])); + + let r = executor_call:: _>( + &mut t, + "Core_initialize_block", + &vec![].and(&from_block_number(1u32)), + true, + None, + ).0; + assert!(r.is_ok()); + + let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + let r = executor_call:: _>( + &mut t, + "BlockBuilder_apply_extrinsic", + &vec![].and(&xt()), + true, + None, + ).0; + assert!(r.is_ok()); + + t.execute_with(|| { + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); + assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); + }); +} + +#[test] +fn successful_execution_with_foreign_code_gives_ok() { + let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ + >::hashed_key_for(alice()) => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => vec![0u8; 16], + >::hashed_key_for(0) => vec![0u8; 32] + ], map![])); + + let r = executor_call:: _>( + &mut t, + "Core_initialize_block", + &vec![].and(&from_block_number(1u32)), + true, + None, + ).0; + assert!(r.is_ok()); + + let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + let r = executor_call:: _>( + &mut t, + "BlockBuilder_apply_extrinsic", + &vec![].and(&xt()), + true, + None, + ).0; + assert!(r.is_ok()); + + t.execute_with(|| { + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); + assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); + }); +} + +#[test] +fn full_native_block_import_works() { + let mut t = new_test_ext(COMPACT_CODE, false); + + let (block1, block2) = blocks(); + + let mut alice_last_known_balance: Balance = Default::default(); + let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + executor_call:: _>( + &mut t, + "Core_execute_block", + &block1.0, + true, + None, + ).0.unwrap(); + + t.execute_with(|| { + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); + assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); + alice_last_known_balance = Balances::total_balance(&alice()); + let events = vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: Event::system(system::Event::ExtrinsicSuccess( + DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } + )), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::treasury(treasury::RawEvent::Deposit(1984800000000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::balances(balances::RawEvent::Transfer( + alice().into(), + bob().into(), + 69 * DOLLARS, + 1 * CENTS, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::system(system::Event::ExtrinsicSuccess( + DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } + )), + topics: vec![], + }, + ]; + assert_eq!(System::events(), events); + }); + + fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + executor_call:: _>( + &mut t, + "Core_execute_block", + &block2.0, + true, + None, + ).0.unwrap(); + + t.execute_with(|| { + assert_eq!( + Balances::total_balance(&alice()), + alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), + ); + assert_eq!( + Balances::total_balance(&bob()), + 179 * DOLLARS - transfer_fee(&xt(), fm), + ); + let events = vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: Event::system(system::Event::ExtrinsicSuccess( + DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } + )), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::treasury(treasury::RawEvent::Deposit(1984780231392)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::balances( + balances::RawEvent::Transfer( + bob().into(), + alice().into(), + 5 * DOLLARS, + 1 * CENTS, + ) + ), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(1), + event: Event::system(system::Event::ExtrinsicSuccess( + DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } + )), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(2), + event: Event::treasury(treasury::RawEvent::Deposit(1984780231392)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(2), + event: Event::balances( + balances::RawEvent::Transfer( + alice().into(), + bob().into(), + 15 * DOLLARS, + 1 * CENTS, + ) + ), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(2), + event: Event::system(system::Event::ExtrinsicSuccess( + DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } + )), + topics: vec![], + }, + ]; + assert_eq!(System::events(), events); + }); +} + +#[test] +fn full_wasm_block_import_works() { + let mut t = new_test_ext(COMPACT_CODE, false); + + let (block1, block2) = blocks(); + + let mut alice_last_known_balance: Balance = Default::default(); + let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + executor_call:: _>( + &mut t, + "Core_execute_block", + &block1.0, + false, + None, + ).0.unwrap(); + + t.execute_with(|| { + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); + assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); + alice_last_known_balance = Balances::total_balance(&alice()); + }); + + fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + executor_call:: _>( + &mut t, + "Core_execute_block", + &block2.0, + false, + None, + ).0.unwrap(); + + t.execute_with(|| { + assert_eq!( + Balances::total_balance(&alice()), + alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), + ); + assert_eq!( + Balances::total_balance(&bob()), + 179 * DOLLARS - 1 * transfer_fee(&xt(), fm), + ); + }); +} + +const CODE_TRANSFER: &str = r#" +(module +;; ext_call( +;; callee_ptr: u32, +;; callee_len: u32, +;; gas: u64, +;; value_ptr: u32, +;; value_len: u32, +;; input_data_ptr: u32, +;; input_data_len: u32 +;; ) -> u32 +(import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) +(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) +(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) +(import "env" "memory" (memory 1 1)) +(func (export "deploy") +) +(func (export "call") + (block $fail + ;; load and check the input data (which is stored in the scratch buffer). + ;; fail if the input size is not != 4 + (br_if $fail + (i32.ne + (i32.const 4) + (call $ext_scratch_size) + ) + ) + + (call $ext_scratch_read + (i32.const 0) + (i32.const 0) + (i32.const 4) + ) + + + (br_if $fail + (i32.ne + (i32.load8_u (i32.const 0)) + (i32.const 0) + ) + ) + (br_if $fail + (i32.ne + (i32.load8_u (i32.const 1)) + (i32.const 1) + ) + ) + (br_if $fail + (i32.ne + (i32.load8_u (i32.const 2)) + (i32.const 2) + ) + ) + (br_if $fail + (i32.ne + (i32.load8_u (i32.const 3)) + (i32.const 3) + ) + ) + + (drop + (call $ext_call + (i32.const 4) ;; Pointer to "callee" address. + (i32.const 32) ;; Length of "callee" address. + (i64.const 0) ;; How much gas to devote for the execution. 0 = all. + (i32.const 36) ;; Pointer to the buffer with value to transfer + (i32.const 16) ;; Length of the buffer with value to transfer. + (i32.const 0) ;; Pointer to input data buffer address + (i32.const 0) ;; Length of input data buffer + ) + ) + + (return) + ) + unreachable +) +;; Destination AccountId to transfer the funds. +;; Represented by H256 (32 bytes long) in little endian. +(data (i32.const 4) + "\09\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" + "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" + "\00\00\00\00" +) +;; Amount of value to transfer. +;; Represented by u128 (16 bytes long) in little endian. +(data (i32.const 36) + "\06\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" + "\00\00" +) +) +"#; + +#[test] +fn deploying_wasm_contract_should_work() { + let transfer_code = wabt::wat2wasm(CODE_TRANSFER).unwrap(); + let transfer_ch = ::Hashing::hash(&transfer_code); + + let addr = ::DetermineContractAddress::contract_address_for( + &transfer_ch, + &[], + &charlie(), + ); + + let b = construct_block( + &mut new_test_ext(COMPACT_CODE, false), + 1, + GENESIS_HASH.into(), + vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(42 * 1000)), + }, + CheckedExtrinsic { + signed: Some((charlie(), signed_extra(0, 0))), + function: Call::Contracts( + contracts::Call::put_code::(10_000, transfer_code) + ), + }, + CheckedExtrinsic { + signed: Some((charlie(), signed_extra(1, 0))), + function: Call::Contracts( + contracts::Call::instantiate::(1 * DOLLARS, 10_000, transfer_ch, Vec::new()) + ), + }, + CheckedExtrinsic { + signed: Some((charlie(), signed_extra(2, 0))), + function: Call::Contracts( + contracts::Call::call::( + indices::address::Address::Id(addr.clone()), + 10, + 10_000, + vec![0x00, 0x01, 0x02, 0x03] + ) + ), + }, + ] + ); + + let mut t = new_test_ext(COMPACT_CODE, false); + + executor_call:: _>( + &mut t, + "Core_execute_block", + &b.0, + false, + None, + ).0.unwrap(); + + t.execute_with(|| { + // Verify that the contract constructor worked well and code of TRANSFER contract is actually deployed. + assert_eq!( + &contracts::ContractInfoOf::::get(addr) + .and_then(|c| c.get_alive()) + .unwrap() + .code_hash, + &transfer_ch + ); + }); +} + +#[test] +fn wasm_big_block_import_fails() { + let mut t = new_test_ext(COMPACT_CODE, false); + + set_heap_pages(&mut t.ext(), 4); + + let result = executor_call:: _>( + &mut t, + "Core_execute_block", + &block_with_size(42, 0, 120_000).0, + false, + None, + ).0; + assert!(result.is_err()); // Err(Wasmi(Trap(Trap { kind: Host(AllocatorOutOfSpace) }))) +} + +#[test] +fn native_big_block_import_succeeds() { + let mut t = new_test_ext(COMPACT_CODE, false); + + executor_call:: _>( + &mut t, + "Core_execute_block", + &block_with_size(42, 0, 120_000).0, + true, + None, + ).0.unwrap(); +} + +#[test] +fn native_big_block_import_fails_on_fallback() { + let mut t = new_test_ext(COMPACT_CODE, false); + + assert!( + executor_call:: _>( + &mut t, + "Core_execute_block", + &block_with_size(42, 0, 120_000).0, + false, + None, + ).0.is_err() + ); +} + +#[test] +fn panic_execution_gives_error() { + let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ + >::hashed_key_for(alice()) => { + 0_u128.encode() + }, + >::hashed_key().to_vec() => { + 0_u128.encode() + }, + >::hashed_key().to_vec() => vec![0u8; 16], + >::hashed_key_for(0) => vec![0u8; 32] + ], map![])); + + let r = executor_call:: _>( + &mut t, + "Core_initialize_block", + &vec![].and(&from_block_number(1u32)), + false, + None, + ).0; + assert!(r.is_ok()); + let r = executor_call:: _>( + &mut t, + "BlockBuilder_apply_extrinsic", + &vec![].and(&xt()), + false, + None, + ).0.unwrap().into_encoded(); + let r = ApplyExtrinsicResult::decode(&mut &r[..]).unwrap(); + assert_eq!(r, Err(InvalidTransaction::Payment.into())); +} + +#[test] +fn successful_execution_gives_ok() { + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ + >::hashed_key_for(alice()) => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => vec![0u8; 16], + >::hashed_key_for(0) => vec![0u8; 32] + ], map![])); + + let r = executor_call:: _>( + &mut t, + "Core_initialize_block", + &vec![].and(&from_block_number(1u32)), + false, + None, + ).0; + assert!(r.is_ok()); + let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + let r = executor_call:: _>( + &mut t, + "BlockBuilder_apply_extrinsic", + &vec![].and(&xt()), + false, + None, + ).0.unwrap().into_encoded(); + ApplyExtrinsicResult::decode(&mut &r[..]) + .unwrap() + .expect("Extrinsic could be applied") + .expect("Extrinsic did not fail"); + + t.execute_with(|| { + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt(), fm)); + assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); + }); +} + +#[test] +fn full_native_block_import_works_with_changes_trie() { + let block1 = changes_trie_block(); + let block_data = block1.0; + let block = Block::decode(&mut &block_data[..]).unwrap(); + + let mut t = new_test_ext(COMPACT_CODE, true); + executor_call:: _>( + &mut t, + "Core_execute_block", + &block.encode(), + true, + None, + ).0.unwrap(); + + assert!(t.ext().storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); +} + +#[test] +fn full_wasm_block_import_works_with_changes_trie() { + let block1 = changes_trie_block(); + + let mut t = new_test_ext(COMPACT_CODE, true); + executor_call:: _>( + &mut t, + "Core_execute_block", + &block1.0, + false, + None, + ).0.unwrap(); + + assert!(t.ext().storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); +} + +#[test] +fn should_import_block_with_test_client() { + use node_testing::client::{ClientExt, TestClientBuilderExt, TestClientBuilder, consensus::BlockOrigin}; + + let client = TestClientBuilder::new().build(); + let block1 = changes_trie_block(); + let block_data = block1.0; + let block = node_primitives::Block::decode(&mut &block_data[..]).unwrap(); + + client.import(BlockOrigin::Own, block).unwrap(); +} + + diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs new file mode 100644 index 0000000000000..0e7af95538720 --- /dev/null +++ b/bin/node/executor/tests/common.rs @@ -0,0 +1,155 @@ +// Copyright 2018-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 . + +use codec::{Encode, Decode}; +use runtime_support::{ + Hashable, +}; +use state_machine::TestExternalities as CoreTestExternalities; +use primitives::{ + Blake2Hasher, NeverNativeValue, NativeOrEncoded, + traits::CodeExecutor, +}; +use sp_runtime::traits::{Header as HeaderT}; +use sc_executor::{NativeExecutor, WasmExecutionMethod}; +use sc_executor::error::Result; + +use node_executor::Executor; +use node_runtime::{ + Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Runtime, BuildStorage, + constants::currency::*, +}; +use node_primitives::{Hash, BlockNumber}; +use node_testing::keyring::*; + +/// The wasm runtime code. +/// +/// `compact` since it is after post-processing with wasm-gc which performs tree-shaking thus +/// making the binary slimmer. There is a convention to use compact version of the runtime +/// as canonical. This is why `native_executor_instance` also uses the compact version of the +/// runtime. +pub const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY; + +pub const GENESIS_HASH: [u8; 32] = [69u8; 32]; + +pub const VERSION: u32 = node_runtime::VERSION.spec_version; + +pub type TestExternalities = CoreTestExternalities; + +pub fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { + node_testing::keyring::sign(xt, VERSION, GENESIS_HASH) +} + +pub fn default_transfer_call() -> balances::Call { + balances::Call::transfer::(bob().into(), 69 * DOLLARS) +} + +pub fn from_block_number(n: u32) -> Header { + Header::new(n, Default::default(), Default::default(), [69; 32].into(), Default::default()) +} + +pub fn executor() -> NativeExecutor { + NativeExecutor::new(WasmExecutionMethod::Interpreted, None) +} + +pub fn executor_call< + R:Decode + Encode + PartialEq, + NC: FnOnce() -> std::result::Result + std::panic::UnwindSafe +>( + t: &mut TestExternalities, + method: &str, + data: &[u8], + use_native: bool, + native_call: Option, +) -> (Result>, bool) { + let mut t = t.ext(); + executor().call::<_, R, NC>( + &mut t, + method, + data, + use_native, + native_call, + ) +} + +pub fn new_test_ext(code: &[u8], support_changes_trie: bool) -> TestExternalities { + let mut ext = TestExternalities::new_with_code( + code, + node_testing::genesis::config(support_changes_trie, Some(code)).build_storage().unwrap(), + ); + ext.changes_trie_storage().insert(0, GENESIS_HASH.into(), Default::default()); + ext +} + +pub fn construct_block( + env: &mut TestExternalities, + number: BlockNumber, + parent_hash: Hash, + extrinsics: Vec, +) -> (Vec, Hash) { + use trie::{TrieConfiguration, trie_types::Layout}; + + // sign extrinsics. + let extrinsics = extrinsics.into_iter().map(sign).collect::>(); + + // calculate the header fields that we can. + let extrinsics_root = Layout::::ordered_trie_root( + extrinsics.iter().map(Encode::encode) + ).to_fixed_bytes() + .into(); + + let header = Header { + parent_hash, + number, + extrinsics_root, + state_root: Default::default(), + digest: Default::default(), + }; + + // execute the block to get the real header. + executor_call:: _>( + env, + "Core_initialize_block", + &header.encode(), + true, + None, + ).0.unwrap(); + + for i in extrinsics.iter() { + executor_call:: _>( + env, + "BlockBuilder_apply_extrinsic", + &i.encode(), + true, + None, + ).0.unwrap(); + } + + let header = match executor_call:: _>( + env, + "BlockBuilder_finalize_block", + &[0u8;0], + true, + None, + ).0.unwrap() { + NativeOrEncoded::Native(_) => unreachable!(), + NativeOrEncoded::Encoded(h) => Header::decode(&mut &h[..]).unwrap(), + }; + + let hash = header.blake2_256(); + (Block { header, extrinsics }.encode(), hash.into()) +} + diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs new file mode 100644 index 0000000000000..a191ef51363f8 --- /dev/null +++ b/bin/node/executor/tests/fees.rs @@ -0,0 +1,329 @@ +// Copyright 2018-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 . + +use codec::{Encode, Joiner}; +use runtime_support::{ + StorageValue, StorageMap, + traits::Currency, + weights::GetDispatchInfo, +}; +use primitives::{ + Blake2Hasher, NeverNativeValue, map, +}; +use sp_runtime::{ + Fixed64, + traits::Convert, +}; +use node_runtime::{ + CheckedExtrinsic, Call, Runtime, Balances, + TransactionPayment, TransferFee, TransactionBaseFee, TransactionByteFee, + WeightFeeCoefficient, constants::currency::*, +}; +use node_runtime::impls::LinearWeightToFee; +use node_primitives::Balance; +use node_testing::keyring::*; + +mod common; + +use self::common::{*, sign}; + +#[test] +fn fee_multiplier_increases_and_decreases_on_big_weight() { + let mut t = new_test_ext(COMPACT_CODE, false); + + // initial fee multiplier must be zero + let mut prev_multiplier = Fixed64::from_parts(0); + + t.execute_with(|| { + assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier); + }); + + let mut tt = new_test_ext(COMPACT_CODE, false); + + // big one in terms of weight. + let block1 = construct_block( + &mut tt, + 1, + GENESIS_HASH.into(), + vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(42 * 1000)), + }, + CheckedExtrinsic { + signed: Some((charlie(), signed_extra(0, 0))), + function: Call::System(system::Call::fill_block()), + } + ] + ); + + // small one in terms of weight. + let block2 = construct_block( + &mut tt, + 2, + block1.1.clone(), + vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(52 * 1000)), + }, + CheckedExtrinsic { + signed: Some((charlie(), signed_extra(1, 0))), + function: Call::System(system::Call::remark(vec![0; 1])), + } + ] + ); + + println!("++ Block 1 size: {} / Block 2 size {}", block1.0.encode().len(), block2.0.encode().len()); + + // execute a big block. + executor_call:: _>( + &mut t, + "Core_execute_block", + &block1.0, + true, + None, + ).0.unwrap(); + + // weight multiplier is increased for next block. + t.execute_with(|| { + let fm = TransactionPayment::next_fee_multiplier(); + println!("After a big block: {:?} -> {:?}", prev_multiplier, fm); + assert!(fm > prev_multiplier); + prev_multiplier = fm; + }); + + // execute a big block. + executor_call:: _>( + &mut t, + "Core_execute_block", + &block2.0, + true, + None, + ).0.unwrap(); + + // weight multiplier is increased for next block. + t.execute_with(|| { + let fm = TransactionPayment::next_fee_multiplier(); + println!("After a small block: {:?} -> {:?}", prev_multiplier, fm); + assert!(fm < prev_multiplier); + }); +} + +#[test] +fn transaction_fee_is_correct_ultimate() { + // This uses the exact values of substrate-node. + // + // weight of transfer call as of now: 1_000_000 + // if weight of the cheapest weight would be 10^7, this would be 10^9, which is: + // - 1 MILLICENTS in substrate node. + // - 1 milli-dot based on current polkadot runtime. + // (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`) + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ + >::hashed_key_for(alice()) => { + (100 * DOLLARS).encode() + }, + >::hashed_key_for(bob()) => { + (10 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => { + (110 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => vec![0u8; 16], + >::hashed_key_for(0) => vec![0u8; 32] + ], map![])); + + let tip = 1_000_000; + let xt = sign(CheckedExtrinsic { + signed: Some((alice(), signed_extra(0, tip))), + function: Call::Balances(default_transfer_call()), + }); + + let r = executor_call:: _>( + &mut t, + "Core_initialize_block", + &vec![].and(&from_block_number(1u32)), + true, + None, + ).0; + + assert!(r.is_ok()); + let r = executor_call:: _>( + &mut t, + "BlockBuilder_apply_extrinsic", + &vec![].and(&xt.clone()), + true, + None, + ).0; + assert!(r.is_ok()); + + t.execute_with(|| { + assert_eq!(Balances::total_balance(&bob()), (10 + 69) * DOLLARS); + // Components deducted from alice's balances: + // - Weight fee + // - Length fee + // - Tip + // - Creation-fee of bob's account. + let mut balance_alice = (100 - 69) * DOLLARS; + + let length_fee = TransactionBaseFee::get() + + TransactionByteFee::get() * + (xt.clone().encode().len() as Balance); + balance_alice -= length_fee; + + let weight = default_transfer_call().get_dispatch_info().weight; + let weight_fee = LinearWeightToFee::::convert(weight); + + // we know that weight to fee multiplier is effect-less in block 1. + assert_eq!(weight_fee as Balance, MILLICENTS); + balance_alice -= weight_fee; + + balance_alice -= tip; + balance_alice -= TransferFee::get(); + + assert_eq!(Balances::total_balance(&alice()), balance_alice); + }); +} + +#[test] +#[should_panic] +#[cfg(feature = "stress-test")] +fn block_weight_capacity_report() { + // Just report how many transfer calls you could fit into a block. The number should at least + // be a few hundred (250 at the time of writing but can change over time). Runs until panic. + use node_primitives::Index; + + // execution ext. + let mut t = new_test_ext(COMPACT_CODE, false); + // setup ext. + let mut tt = new_test_ext(COMPACT_CODE, false); + + let factor = 50; + let mut time = 10; + let mut nonce: Index = 0; + let mut block_number = 1; + let mut previous_hash: Hash = GENESIS_HASH.into(); + + loop { + let num_transfers = block_number * factor; + let mut xts = (0..num_transfers).map(|i| CheckedExtrinsic { + signed: Some((charlie(), signed_extra(nonce + i as Index, 0))), + function: Call::Balances(balances::Call::transfer(bob().into(), 0)), + }).collect::>(); + + xts.insert(0, CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(time * 1000)), + }); + + // NOTE: this is super slow. Can probably be improved. + let block = construct_block( + &mut tt, + block_number, + previous_hash, + xts + ); + + let len = block.0.len(); + print!( + "++ Executing block with {} transfers. Block size = {} bytes / {} kb / {} mb", + num_transfers, + len, + len / 1024, + len / 1024 / 1024, + ); + + let r = executor_call:: _>( + &mut t, + "Core_execute_block", + &block.0, + true, + None, + ).0; + + println!(" || Result = {:?}", r); + assert!(r.is_ok()); + + previous_hash = block.1; + nonce += num_transfers; + time += 10; + block_number += 1; + } +} + +#[test] +#[should_panic] +#[cfg(feature = "stress-test")] +fn block_length_capacity_report() { + // Just report how big a block can get. Executes until panic. Should be ignored unless if + // manually inspected. The number should at least be a few megabytes (5 at the time of + // writing but can change over time). + use node_primitives::Index; + + // execution ext. + let mut t = new_test_ext(COMPACT_CODE, false); + // setup ext. + let mut tt = new_test_ext(COMPACT_CODE, false); + + let factor = 256 * 1024; + let mut time = 10; + let mut nonce: Index = 0; + let mut block_number = 1; + let mut previous_hash: Hash = GENESIS_HASH.into(); + + loop { + // NOTE: this is super slow. Can probably be improved. + let block = construct_block( + &mut tt, + block_number, + previous_hash, + vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(time * 1000)), + }, + CheckedExtrinsic { + signed: Some((charlie(), signed_extra(nonce, 0))), + function: Call::System(system::Call::remark(vec![0u8; (block_number * factor) as usize])), + }, + ] + ); + + let len = block.0.len(); + print!( + "++ Executing block with big remark. Block size = {} bytes / {} kb / {} mb", + len, + len / 1024, + len / 1024 / 1024, + ); + + let r = executor_call:: _>( + &mut t, + "Core_execute_block", + &block.0, + true, + None, + ).0; + + println!(" || Result = {:?}", r); + assert!(r.is_ok()); + + previous_hash = block.1; + nonce += 1; + time += 10; + block_number += 1; + } +} diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 377e3257a6db4..107082208a1a3 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -724,7 +724,7 @@ mod tests { use system::offchain::{SignAndSubmitTransaction, SubmitSignedTransaction}; #[test] - fn validate_bounds() { + fn validate_transaction_submitter_bounds() { fn is_submit_signed_transaction() where T: SubmitSignedTransaction< Runtime, From 90a00fdafd9da1587a2ff772bf2bcc5d0aa645f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 3 Dec 2019 17:51:09 +0100 Subject: [PATCH 08/30] Add submit transactions tests. --- Cargo.lock | 1 + bin/node/executor/Cargo.toml | 17 ++--- bin/node/executor/tests/basic.rs | 1 - bin/node/executor/tests/fees.rs | 1 - bin/node/executor/tests/submit_transaction.rs | 69 +++++++++++++++++++ bin/node/runtime/src/lib.rs | 3 +- frame/im-online/src/lib.rs | 12 ++-- primitives/core/src/offchain/mod.rs | 1 + 8 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 bin/node/executor/tests/submit_transaction.rs diff --git a/Cargo.lock b/Cargo.lock index 557d1dd1f2997..12754d252ced9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3043,6 +3043,7 @@ dependencies = [ "pallet-balances 2.0.0", "pallet-contracts 2.0.0", "pallet-grandpa 2.0.0", + "pallet-im-online 0.1.0", "pallet-indices 2.0.0", "pallet-session 2.0.0", "pallet-timestamp 2.0.0", diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index bafe514393f9e..9f93989ae38bf 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -17,21 +17,22 @@ node-primitives = { path = "../primitives" } node-runtime = { path = "../runtime" } [dev-dependencies] +balances = { package = "pallet-balances", path = "../../../frame/balances" } +contracts = { package = "pallet-contracts", path = "../../../frame/contracts" } +criterion = "0.3.0" +grandpa = { package = "pallet-grandpa", path = "../../../frame/grandpa" } +imonline = { package = "pallet-im-online", path = "../../../frame/im-online" } +indices = { package = "pallet-indices", path = "../../../frame/indices" } node-testing = { path = "../testing" } -test-client = { package = "substrate-test-client", path = "../../../test/utils/client" } -sp-runtime = { path = "../../../primitives/sr-primitives" } runtime_support = { package = "frame-support", path = "../../../frame/support" } -balances = { package = "pallet-balances", path = "../../../frame/balances" } -transaction-payment = { package = "pallet-transaction-payment", path = "../../../frame/transaction-payment" } session = { package = "pallet-session", path = "../../../frame/session" } +sp-runtime = { path = "../../../primitives/sr-primitives" } system = { package = "frame-system", path = "../../../frame/system" } +test-client = { package = "substrate-test-client", path = "../../../test/utils/client" } timestamp = { package = "pallet-timestamp", path = "../../../frame/timestamp" } +transaction-payment = { package = "pallet-transaction-payment", path = "../../../frame/transaction-payment" } treasury = { package = "pallet-treasury", path = "../../../frame/treasury" } -contracts = { package = "pallet-contracts", path = "../../../frame/contracts" } -grandpa = { package = "pallet-grandpa", path = "../../../frame/grandpa" } -indices = { package = "pallet-indices", path = "../../../frame/indices" } wabt = "0.9.2" -criterion = "0.3.0" [features] wasmtime = [ diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 990e0cdf4d9a3..4be7edb1cdf9d 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -45,7 +45,6 @@ use wabt; use node_testing::keyring::*; mod common; - use self::common::{*, sign}; diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs index a191ef51363f8..f7e5a661b5537 100644 --- a/bin/node/executor/tests/fees.rs +++ b/bin/node/executor/tests/fees.rs @@ -37,7 +37,6 @@ use node_primitives::Balance; use node_testing::keyring::*; mod common; - use self::common::{*, sign}; #[test] diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs new file mode 100644 index 0000000000000..d9816e204b4af --- /dev/null +++ b/bin/node/executor/tests/submit_transaction.rs @@ -0,0 +1,69 @@ +// Copyright 2018-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 . + +use node_runtime::{ + Call, Runtime, SubmitTransaction, +}; +use primitives::offchain::{ + TransactionPoolExt, + testing::TestTransactionPoolExt, +}; +use system::offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction}; + +mod common; +use self::common::*; + +#[test] +fn should_submit_unsigned_transaction() { + let mut t = new_test_ext(COMPACT_CODE, false); + let (pool, state) = TestTransactionPoolExt::new(); + t.register_extension(TransactionPoolExt::new(pool)); + + t.execute_with(|| { + let signature = Default::default(); + let heartbeat_data = imonline::Heartbeat { + block_number: 1, + network_state: Default::default(), + session_index: 1, + authority_index: 0, + }; + + let call = imonline::Call::heartbeat(heartbeat_data, signature); + > + ::submit_unsigned(call) + .unwrap(); + + assert_eq!(state.read().transactions.len(), 1) + }); +} + +#[test] +fn should_submit_signed_transaction() { + let mut t = new_test_ext(COMPACT_CODE, false); + let (pool, state) = TestTransactionPoolExt::new(); + t.register_extension(TransactionPoolExt::new(pool)); + + t.execute_with(|| { + let call = balances::Call::transfer(Default::default()); + let results = SubmitSignedTransaction + ::submit_signed(call, vec![]); + + let len = results.len(); + assert_eq!(len, 3); + assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len); + assert_eq!(state.read().transactions.len(), len); + }); +} diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 107082208a1a3..9bc600c68c1b8 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -423,7 +423,8 @@ impl sudo::Trait for Runtime { type Proposal = Call; } -type SubmitTransaction = TransactionSubmitter; +/// A runtime transaction submitter. +pub type SubmitTransaction = TransactionSubmitter; parameter_types! { pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _; diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 7892054b7f7a5..b341964a874a5 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -171,10 +171,14 @@ pub type AuthIndex = u32; pub struct Heartbeat where BlockNumber: PartialEq + Eq + Decode + Encode, { - block_number: BlockNumber, - network_state: OpaqueNetworkState, - session_index: SessionIndex, - authority_index: AuthIndex, + /// Block number at the time heartbeat is created.. + pub block_number: BlockNumber, + /// A state of local network (peer id and external addresses) + pub network_state: OpaqueNetworkState, + /// Index of the current session. + pub session_index: SessionIndex, + /// An index of the authority on the list of validators. + pub authority_index: AuthIndex, } pub trait Trait: system::Trait + session::historical::Trait { diff --git a/primitives/core/src/offchain/mod.rs b/primitives/core/src/offchain/mod.rs index 8afabc0392a2a..fa259798bf77a 100644 --- a/primitives/core/src/offchain/mod.rs +++ b/primitives/core/src/offchain/mod.rs @@ -174,6 +174,7 @@ impl TryFrom for HttpRequestStatus { /// A blob to hold information about the local node's network state /// without committing to its format. #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByCodec)] +#[cfg_attr(feature = "std", derive(Default))] pub struct OpaqueNetworkState { /// PeerId of the local node. pub peer_id: OpaquePeerId, From d103fa19f735db6c75407bea115279a6d74148d6 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Thu, 19 Dec 2019 00:28:06 -0500 Subject: [PATCH 09/30] Make `submit_transaction` tests compile --- bin/node/executor/tests/basic.rs | 4 +- bin/node/executor/tests/submit_transaction.rs | 11 +- bin/node/testing/src/keyring.rs | 4 +- diff.txt | 138 ++++++++++++++++++ frame/system/src/offchain.rs | 1 - 5 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 diff.txt diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 4be7edb1cdf9d..0391cf80c19bd 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -783,7 +783,7 @@ fn full_native_block_import_works_with_changes_trie() { None, ).0.unwrap(); - assert!(t.ext().storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); + assert!(t.ext().storage_changes_root(&GENESIS_HASH).unwrap().is_some()); } #[test] @@ -799,7 +799,7 @@ fn full_wasm_block_import_works_with_changes_trie() { None, ).0.unwrap(); - assert!(t.ext().storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); + assert!(t.ext().storage_changes_root(&GENESIS_HASH).unwrap().is_some()); } #[test] diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index d9816e204b4af..4e78dacad1258 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -17,6 +17,8 @@ use node_runtime::{ Call, Runtime, SubmitTransaction, }; +use primitives::testing::{KeyStore, ED25519, SR25519}; +use primitives::traits::{KeystoreExt, BareCryptoStorePtr}; use primitives::offchain::{ TransactionPoolExt, testing::TestTransactionPoolExt, @@ -56,10 +58,13 @@ fn should_submit_signed_transaction() { let (pool, state) = TestTransactionPoolExt::new(); t.register_extension(TransactionPoolExt::new(pool)); + let mut keystore = KeyStore::new(); + t.register_extension(KeystoreExt(keystore)); + t.execute_with(|| { - let call = balances::Call::transfer(Default::default()); - let results = SubmitSignedTransaction - ::submit_signed(call, vec![]); + let call = balances::Call::transfer(Default::default(), Default::default()); + let results = + >::submit_signed(call); let len = results.len(); assert_eq!(len, 3); diff --git a/bin/node/testing/src/keyring.rs b/bin/node/testing/src/keyring.rs index 53dac6809651c..55b21cb34e46e 100644 --- a/bin/node/testing/src/keyring.rs +++ b/bin/node/testing/src/keyring.rs @@ -23,7 +23,9 @@ use sp_runtime::generic::Era; use codec::Encode; /// Alice's account id. -pub fn alice() -> AccountId { AccountKeyring::Alice.into() } +pub fn alice() -> AccountId { + AccountKeyring::Alice.into() +} /// Bob's account id. pub fn bob() -> AccountId { diff --git a/diff.txt b/diff.txt new file mode 100644 index 0000000000000..a198f50416dab --- /dev/null +++ b/diff.txt @@ -0,0 +1,138 @@ +diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml +index f5e04960b..7302cda4d 100644 +--- a/bin/node/executor/Cargo.toml ++++ b/bin/node/executor/Cargo.toml +@@ -23,6 +23,7 @@ criterion = "0.3.0" + grandpa = { package = "pallet-grandpa", path = "../../../frame/grandpa" } + imonline = { package = "pallet-im-online", path = "../../../frame/im-online" } + indices = { package = "pallet-indices", path = "../../../frame/indices" } ++# keyring = { package = "sp-keyring", path = "../../../primitives/keyring" } + node-testing = { path = "../testing" } + runtime_support = { package = "frame-support", path = "../../../frame/support" } + session = { package = "pallet-session", path = "../../../frame/session" } +diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs +index 4be7edb1c..0391cf80c 100644 +--- a/bin/node/executor/tests/basic.rs ++++ b/bin/node/executor/tests/basic.rs +@@ -783,7 +783,7 @@ fn full_native_block_import_works_with_changes_trie() { + None, + ).0.unwrap(); + +- assert!(t.ext().storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); ++ assert!(t.ext().storage_changes_root(&GENESIS_HASH).unwrap().is_some()); + } + + #[test] +@@ -799,7 +799,7 @@ fn full_wasm_block_import_works_with_changes_trie() { + None, + ).0.unwrap(); + +- assert!(t.ext().storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); ++ assert!(t.ext().storage_changes_root(&GENESIS_HASH).unwrap().is_some()); + } + + #[test] +diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs +index d9816e204..065870e6e 100644 +--- a/bin/node/executor/tests/submit_transaction.rs ++++ b/bin/node/executor/tests/submit_transaction.rs +@@ -17,11 +17,15 @@ + use node_runtime::{ + Call, Runtime, SubmitTransaction, + }; ++use primitives::traits::{KeystoreExt, BareCryptoStorePtr}; + use primitives::offchain::{ + TransactionPoolExt, + testing::TestTransactionPoolExt, + }; ++use primitives::testing::KeyStore; + use system::offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction}; ++use node_testing::keyring; ++ + + mod common; + use self::common::*; +@@ -50,16 +54,34 @@ fn should_submit_unsigned_transaction() { + }); + } + ++use primitives::testing::{ED25519, SR25519}; + #[test] + fn should_submit_signed_transaction() { + let mut t = new_test_ext(COMPACT_CODE, false); + let (pool, state) = TestTransactionPoolExt::new(); + t.register_extension(TransactionPoolExt::new(pool)); + ++ let alice = keyring::alice(); ++ let bob = keyring::bob(); ++ ++ let mut keystore = KeyStore::new(); ++ ++ let charlie = keystore.write().sr25519_generate_new(SR25519, Some("//beep")).unwrap(); ++ dbg!(&charlie); ++ // let _ = keystore.write().sr25519_key_pair(SR25519, alice).unwrap(); ++ dbg!(keystore.read().sr25519_public_keys(SR25519)); ++ ++ t.register_extension(KeystoreExt(keystore)); ++ + t.execute_with(|| { +- let call = balances::Call::transfer(Default::default()); +- let results = SubmitSignedTransaction +- ::submit_signed(call, vec![]); ++ let call = balances::Call::transfer(Default::default(), Default::default()); ++ dbg!(&call); ++ ++ // This should submit Txs from all local accounts (which should be in the keystore) ++ // This mean we should have three local accounts right now (but we have 0) ++ let results = ++ >::submit_signed(call); ++ dbg!(&results); + + let len = results.len(); + assert_eq!(len, 3); +@@ -67,3 +89,18 @@ fn should_submit_signed_transaction() { + assert_eq!(state.read().transactions.len(), len); + }); + } ++ ++#[test] ++#[ignore] ++fn should_check_bug_that_tomek_described() { ++ let mut t = new_test_ext(COMPACT_CODE, false); ++ let (pool, state) = TestTransactionPoolExt::new(); ++ t.register_extension(TransactionPoolExt::new(pool)); ++ ++ let mut keystore = KeyStore::new(); ++ t.register_extension(KeystoreExt(keystore)); ++ ++ t.execute_with(|| { ++ // -- call submit_sign twice -- ++ }); ++} +diff --git a/bin/node/testing/src/keyring.rs b/bin/node/testing/src/keyring.rs +index 53dac6809..55b21cb34 100644 +--- a/bin/node/testing/src/keyring.rs ++++ b/bin/node/testing/src/keyring.rs +@@ -23,7 +23,9 @@ use sp_runtime::generic::Era; + use codec::Encode; + + /// Alice's account id. +-pub fn alice() -> AccountId { AccountKeyring::Alice.into() } ++pub fn alice() -> AccountId { ++ AccountKeyring::Alice.into() ++} + + /// Bob's account id. + pub fn bob() -> AccountId { +diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs +index 8c8e15021..b0a0b210d 100644 +--- a/frame/system/src/offchain.rs ++++ b/frame/system/src/offchain.rs +@@ -228,7 +228,6 @@ pub trait SubmitSignedTransaction { + } + } + +- + /// A default type used to submit transactions to the pool. + /// + /// This is passed into each runtime as an opaque associated type that can have either of: diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 8c8e15021d470..b0a0b210d3e62 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -228,7 +228,6 @@ pub trait SubmitSignedTransaction { } } - /// A default type used to submit transactions to the pool. /// /// This is passed into each runtime as an opaque associated type that can have either of: From 99c54b7dba09cd0f611d5b67b865ed4c7d953461 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Thu, 19 Dec 2019 00:33:43 -0500 Subject: [PATCH 10/30] Remove a file that was accidently committed --- diff.txt | 138 ------------------------------------------------------- 1 file changed, 138 deletions(-) delete mode 100644 diff.txt diff --git a/diff.txt b/diff.txt deleted file mode 100644 index a198f50416dab..0000000000000 --- a/diff.txt +++ /dev/null @@ -1,138 +0,0 @@ -diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml -index f5e04960b..7302cda4d 100644 ---- a/bin/node/executor/Cargo.toml -+++ b/bin/node/executor/Cargo.toml -@@ -23,6 +23,7 @@ criterion = "0.3.0" - grandpa = { package = "pallet-grandpa", path = "../../../frame/grandpa" } - imonline = { package = "pallet-im-online", path = "../../../frame/im-online" } - indices = { package = "pallet-indices", path = "../../../frame/indices" } -+# keyring = { package = "sp-keyring", path = "../../../primitives/keyring" } - node-testing = { path = "../testing" } - runtime_support = { package = "frame-support", path = "../../../frame/support" } - session = { package = "pallet-session", path = "../../../frame/session" } -diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs -index 4be7edb1c..0391cf80c 100644 ---- a/bin/node/executor/tests/basic.rs -+++ b/bin/node/executor/tests/basic.rs -@@ -783,7 +783,7 @@ fn full_native_block_import_works_with_changes_trie() { - None, - ).0.unwrap(); - -- assert!(t.ext().storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); -+ assert!(t.ext().storage_changes_root(&GENESIS_HASH).unwrap().is_some()); - } - - #[test] -@@ -799,7 +799,7 @@ fn full_wasm_block_import_works_with_changes_trie() { - None, - ).0.unwrap(); - -- assert!(t.ext().storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); -+ assert!(t.ext().storage_changes_root(&GENESIS_HASH).unwrap().is_some()); - } - - #[test] -diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs -index d9816e204..065870e6e 100644 ---- a/bin/node/executor/tests/submit_transaction.rs -+++ b/bin/node/executor/tests/submit_transaction.rs -@@ -17,11 +17,15 @@ - use node_runtime::{ - Call, Runtime, SubmitTransaction, - }; -+use primitives::traits::{KeystoreExt, BareCryptoStorePtr}; - use primitives::offchain::{ - TransactionPoolExt, - testing::TestTransactionPoolExt, - }; -+use primitives::testing::KeyStore; - use system::offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction}; -+use node_testing::keyring; -+ - - mod common; - use self::common::*; -@@ -50,16 +54,34 @@ fn should_submit_unsigned_transaction() { - }); - } - -+use primitives::testing::{ED25519, SR25519}; - #[test] - fn should_submit_signed_transaction() { - let mut t = new_test_ext(COMPACT_CODE, false); - let (pool, state) = TestTransactionPoolExt::new(); - t.register_extension(TransactionPoolExt::new(pool)); - -+ let alice = keyring::alice(); -+ let bob = keyring::bob(); -+ -+ let mut keystore = KeyStore::new(); -+ -+ let charlie = keystore.write().sr25519_generate_new(SR25519, Some("//beep")).unwrap(); -+ dbg!(&charlie); -+ // let _ = keystore.write().sr25519_key_pair(SR25519, alice).unwrap(); -+ dbg!(keystore.read().sr25519_public_keys(SR25519)); -+ -+ t.register_extension(KeystoreExt(keystore)); -+ - t.execute_with(|| { -- let call = balances::Call::transfer(Default::default()); -- let results = SubmitSignedTransaction -- ::submit_signed(call, vec![]); -+ let call = balances::Call::transfer(Default::default(), Default::default()); -+ dbg!(&call); -+ -+ // This should submit Txs from all local accounts (which should be in the keystore) -+ // This mean we should have three local accounts right now (but we have 0) -+ let results = -+ >::submit_signed(call); -+ dbg!(&results); - - let len = results.len(); - assert_eq!(len, 3); -@@ -67,3 +89,18 @@ fn should_submit_signed_transaction() { - assert_eq!(state.read().transactions.len(), len); - }); - } -+ -+#[test] -+#[ignore] -+fn should_check_bug_that_tomek_described() { -+ let mut t = new_test_ext(COMPACT_CODE, false); -+ let (pool, state) = TestTransactionPoolExt::new(); -+ t.register_extension(TransactionPoolExt::new(pool)); -+ -+ let mut keystore = KeyStore::new(); -+ t.register_extension(KeystoreExt(keystore)); -+ -+ t.execute_with(|| { -+ // -- call submit_sign twice -- -+ }); -+} -diff --git a/bin/node/testing/src/keyring.rs b/bin/node/testing/src/keyring.rs -index 53dac6809..55b21cb34 100644 ---- a/bin/node/testing/src/keyring.rs -+++ b/bin/node/testing/src/keyring.rs -@@ -23,7 +23,9 @@ use sp_runtime::generic::Era; - use codec::Encode; - - /// Alice's account id. --pub fn alice() -> AccountId { AccountKeyring::Alice.into() } -+pub fn alice() -> AccountId { -+ AccountKeyring::Alice.into() -+} - - /// Bob's account id. - pub fn bob() -> AccountId { -diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs -index 8c8e15021..b0a0b210d 100644 ---- a/frame/system/src/offchain.rs -+++ b/frame/system/src/offchain.rs -@@ -228,7 +228,6 @@ pub trait SubmitSignedTransaction { - } - } - -- - /// A default type used to submit transactions to the pool. - /// - /// This is passed into each runtime as an opaque associated type that can have either of: From 9eb7a2ea562c002ad36f3c9b1ccebc7a08a31f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 20 Dec 2019 15:40:38 +0100 Subject: [PATCH 11/30] Add can_sign helper function. --- frame/system/src/offchain.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 75e03c4e85f3c..9c406622eeb11 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -184,6 +184,15 @@ pub trait SubmitSignedTransaction { PublicOf, )>; + /// Check if there are any keys that could be used to send a transaction. + /// + /// This check can be used as an early-exit condition to avoid doing too + /// much work, before we actually realise that there are no accounts that you + /// we could use for signing. + fn can_sign(accounts: Option>) -> bool { + !Self::find_local_keys(accounts).is_empty() + } + /// Create and submit signed transactions from supported accounts. /// /// This method should intersect given list of accounts with the ones @@ -194,7 +203,7 @@ pub trait SubmitSignedTransaction { #[must_use] fn submit_signed_from( call: impl Into + Clone, - accounts: impl IntoIterator, + accounts: impl IntoIterator, ) -> Vec<(T::AccountId, Result<(), ()>)> { let keys = Self::find_local_keys(Some(accounts)); keys.into_iter().map(|(account, pub_key)| { @@ -322,5 +331,14 @@ impl SubmitSignedTransaction for TransactionSubmitter local_accounts_and_keys } } + + fn can_sign(accounts: Option>) -> bool { + // early exit if we care about any account. + if accounts.is_none() { + !S::all().is_empty() + } else { + !Self::find_local_keys(accounts).is_empty() + } + } } From 0ccb951109d703724cde787cb7911319f689edcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 20 Dec 2019 16:10:10 +0100 Subject: [PATCH 12/30] Fix compilation. --- Cargo.lock | 1 + bin/node/executor/Cargo.toml | 1 + bin/node/executor/tests/basic.rs | 233 ++++++++++-------- bin/node/executor/tests/common.rs | 12 +- bin/node/executor/tests/fees.rs | 50 ++-- bin/node/executor/tests/submit_transaction.rs | 14 +- 6 files changed, 169 insertions(+), 142 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a597abd67f729..73e04eb8ec8e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3074,6 +3074,7 @@ dependencies = [ "pallet-balances 2.0.0", "pallet-contracts 2.0.0", "pallet-grandpa 2.0.0", + "pallet-im-online 2.0.0", "pallet-indices 2.0.0", "pallet-session 2.0.0", "pallet-timestamp 2.0.0", diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 5884650ff03ba..20812c78d3282 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -25,6 +25,7 @@ pallet-contracts = { version = "2.0.0", path = "../../../frame/contracts" } pallet-grandpa = { version = "2.0.0", path = "../../../frame/grandpa" } pallet-indices = { version = "2.0.0", path = "../../../frame/indices" } pallet-session = { version = "2.0.0", path = "../../../frame/session" } +pallet-im-online = { version = "2.0.0", path = "../../../frame/im-online" } pallet-timestamp = { version = "2.0.0", path = "../../../frame/timestamp" } pallet-transaction-payment = { version = "2.0.0", path = "../../../frame/transaction-payment" } pallet-treasury = { version = "2.0.0", path = "../../../frame/treasury" } diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 76fb6aeeb124a..ba7b229dca2a3 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -14,25 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use {balances, contracts, indices, timestamp}; use codec::{Encode, Decode, Joiner}; -use runtime_support::{ +use frame_support::{ StorageValue, StorageMap, traits::Currency, weights::{GetDispatchInfo, DispatchInfo, DispatchClass}, }; -use primitives::{ +use sp_core::{ Blake2Hasher, NeverNativeValue, map, traits::Externalities, - storage::well_known_keys, + storage::{well_known_keys, Storage}, }; use sp_runtime::{ ApplyExtrinsicResult, Fixed64, traits::{Hash as HashT, Convert}, transaction_validity::InvalidTransaction, }; -use contracts::ContractAddressFor; -use system::{self, EventRecord, Phase}; +use pallet_contracts::ContractAddressFor; +use frame_system::{self, EventRecord, Phase}; use node_runtime::{ Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, @@ -60,7 +59,8 @@ fn transfer_fee(extrinsic: &E, fee_multiplier: Fixed64) -> Balance { let length_fee = TransactionByteFee::get() * (extrinsic.encode().len() as Balance); let weight = default_transfer_call().get_dispatch_info().weight; - let weight_fee = ::WeightToFee::convert(weight); + let weight_fee = + ::WeightToFee::convert(weight); let base_fee = TransactionBaseFee::get(); base_fee + fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee) + TransferFee::get() @@ -85,11 +85,11 @@ fn changes_trie_block() -> (Vec, Hash) { vec![ CheckedExtrinsic { signed: None, - function: Call::Timestamp(timestamp::Call::set(42 * 1000)), + function: Call::Timestamp(pallet_timestamp::Call::set(42 * 1000)), }, CheckedExtrinsic { signed: Some((alice(), signed_extra(0, 0))), - function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), + function: Call::Balances(pallet_balances::Call::transfer(bob().into(), 69 * DOLLARS)), }, ] ) @@ -108,11 +108,11 @@ fn blocks() -> ((Vec, Hash), (Vec, Hash)) { vec![ CheckedExtrinsic { signed: None, - function: Call::Timestamp(timestamp::Call::set(42 * 1000)), + function: Call::Timestamp(pallet_timestamp::Call::set(42 * 1000)), }, CheckedExtrinsic { signed: Some((alice(), signed_extra(0, 0))), - function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), + function: Call::Balances(pallet_balances::Call::transfer(bob().into(), 69 * DOLLARS)), }, ] ); @@ -123,15 +123,15 @@ fn blocks() -> ((Vec, Hash), (Vec, Hash)) { vec![ CheckedExtrinsic { signed: None, - function: Call::Timestamp(timestamp::Call::set(52 * 1000)), + function: Call::Timestamp(pallet_timestamp::Call::set(52 * 1000)), }, CheckedExtrinsic { signed: Some((bob(), signed_extra(0, 0))), - function: Call::Balances(balances::Call::transfer(alice().into(), 5 * DOLLARS)), + function: Call::Balances(pallet_balances::Call::transfer(alice().into(), 5 * DOLLARS)), }, CheckedExtrinsic { signed: Some((alice(), signed_extra(1, 0))), - function: Call::Balances(balances::Call::transfer(bob().into(), 15 * DOLLARS)), + function: Call::Balances(pallet_balances::Call::transfer(bob().into(), 15 * DOLLARS)), } ] ); @@ -151,11 +151,11 @@ fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec, Hash) { vec![ CheckedExtrinsic { signed: None, - function: Call::Timestamp(timestamp::Call::set(time * 1000)), + function: Call::Timestamp(pallet_timestamp::Call::set(time * 1000)), }, CheckedExtrinsic { signed: Some((alice(), signed_extra(nonce, 0))), - function: Call::System(system::Call::remark(vec![0; size])), + function: Call::System(frame_system::Call::remark(vec![0; size])), } ] ) @@ -163,20 +163,23 @@ fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec, Hash) { #[test] fn panic_execution_with_foreign_code_gives_error() { - let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ - >::hashed_key_for(alice()) => { - 69_u128.encode() - }, - >::hashed_key().to_vec() => { - 69_u128.encode() - }, - >::hashed_key().to_vec() => { - 0_u128.encode() - }, - >::hashed_key_for(0) => { - vec![0u8; 32] - } - ], map![])); + let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { + top: map![ + >::hashed_key_for(alice()) => { + 69_u128.encode() + }, + >::hashed_key().to_vec() => { + 69_u128.encode() + }, + >::hashed_key().to_vec() => { + 0_u128.encode() + }, + >::hashed_key_for(0) => { + vec![0u8; 32] + } + ], + children: map![], + }); let r = executor_call:: _>( &mut t, @@ -199,20 +202,23 @@ fn panic_execution_with_foreign_code_gives_error() { #[test] fn bad_extrinsic_with_native_equivalent_code_gives_error() { - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ - >::hashed_key_for(alice()) => { - 69_u128.encode() - }, - >::hashed_key().to_vec() => { - 69_u128.encode() - }, - >::hashed_key().to_vec() => { - 0_u128.encode() - }, - >::hashed_key_for(0) => { - vec![0u8; 32] - } - ], map![])); + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { + top: map![ + >::hashed_key_for(alice()) => { + 69_u128.encode() + }, + >::hashed_key().to_vec() => { + 69_u128.encode() + }, + >::hashed_key().to_vec() => { + 0_u128.encode() + }, + >::hashed_key_for(0) => { + vec![0u8; 32] + } + ], + children: map![], + }); let r = executor_call:: _>( &mut t, @@ -235,16 +241,19 @@ fn bad_extrinsic_with_native_equivalent_code_gives_error() { #[test] fn successful_execution_with_native_equivalent_code_gives_ok() { - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => vec![0u8; 16], - >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { + top: map![ + >::hashed_key_for(alice()) => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => vec![0u8; 16], + >::hashed_key_for(0) => vec![0u8; 32] + ], + children: map![], + }); let r = executor_call:: _>( &mut t, @@ -274,16 +283,19 @@ fn successful_execution_with_native_equivalent_code_gives_ok() { #[test] fn successful_execution_with_foreign_code_gives_ok() { - let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => vec![0u8; 16], - >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { + top: map![ + >::hashed_key_for(alice()) => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => vec![0u8; 16], + >::hashed_key_for(0) => vec![0u8; 32] + ], + children: map![], + }); let r = executor_call:: _>( &mut t, @@ -335,19 +347,19 @@ fn full_native_block_import_works() { let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: Event::system(system::Event::ExtrinsicSuccess( + event: Event::system(frame_system::Event::ExtrinsicSuccess( DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } )), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::treasury(treasury::RawEvent::Deposit(1984800000000)), + event: Event::pallet_treasury(pallet_treasury::RawEvent::Deposit(1984800000000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::balances(balances::RawEvent::Transfer( + event: Event::pallet_balances(pallet_balances::RawEvent::Transfer( alice().into(), bob().into(), 69 * DOLLARS, @@ -357,7 +369,7 @@ fn full_native_block_import_works() { }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::system(system::Event::ExtrinsicSuccess( + event: Event::system(frame_system::Event::ExtrinsicSuccess( DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } )), topics: vec![], @@ -388,20 +400,20 @@ fn full_native_block_import_works() { let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: Event::system(system::Event::ExtrinsicSuccess( + event: Event::system(frame_system::Event::ExtrinsicSuccess( DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } )), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::treasury(treasury::RawEvent::Deposit(1984788199392)), + event: Event::pallet_treasury(pallet_treasury::RawEvent::Deposit(1984788199392)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::balances( - balances::RawEvent::Transfer( + event: Event::pallet_balances( + pallet_balances::RawEvent::Transfer( bob().into(), alice().into(), 5 * DOLLARS, @@ -412,20 +424,20 @@ fn full_native_block_import_works() { }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::system(system::Event::ExtrinsicSuccess( + event: Event::system(frame_system::Event::ExtrinsicSuccess( DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } )), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(2), - event: Event::treasury(treasury::RawEvent::Deposit(1984788199392)), + event: Event::pallet_treasury(pallet_treasury::RawEvent::Deposit(1984788199392)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(2), - event: Event::balances( - balances::RawEvent::Transfer( + event: Event::pallet_balances( + pallet_balances::RawEvent::Transfer( alice().into(), bob().into(), 15 * DOLLARS, @@ -436,7 +448,7 @@ fn full_native_block_import_works() { }, EventRecord { phase: Phase::ApplyExtrinsic(2), - event: Event::system(system::Event::ExtrinsicSuccess( + event: Event::system(frame_system::Event::ExtrinsicSuccess( DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } )), topics: vec![], @@ -586,9 +598,9 @@ const CODE_TRANSFER: &str = r#" #[test] fn deploying_wasm_contract_should_work() { let transfer_code = wabt::wat2wasm(CODE_TRANSFER).unwrap(); - let transfer_ch = ::Hashing::hash(&transfer_code); + let transfer_ch = ::Hashing::hash(&transfer_code); - let addr = ::DetermineContractAddress::contract_address_for( + let addr = ::DetermineContractAddress::contract_address_for( &transfer_ch, &[], &charlie(), @@ -601,25 +613,25 @@ fn deploying_wasm_contract_should_work() { vec![ CheckedExtrinsic { signed: None, - function: Call::Timestamp(timestamp::Call::set(42 * 1000)), + function: Call::Timestamp(pallet_timestamp::Call::set(42 * 1000)), }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(0, 0))), function: Call::Contracts( - contracts::Call::put_code::(10_000, transfer_code) + pallet_contracts::Call::put_code::(10_000, transfer_code) ), }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(1, 0))), function: Call::Contracts( - contracts::Call::instantiate::(1 * DOLLARS, 10_000, transfer_ch, Vec::new()) + pallet_contracts::Call::instantiate::(1 * DOLLARS, 10_000, transfer_ch, Vec::new()) ), }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(2, 0))), function: Call::Contracts( - contracts::Call::call::( - indices::address::Address::Id(addr.clone()), + pallet_contracts::Call::call::( + pallet_indices::address::Address::Id(addr.clone()), 10, 10_000, vec![0x00, 0x01, 0x02, 0x03] @@ -642,7 +654,7 @@ fn deploying_wasm_contract_should_work() { t.execute_with(|| { // Verify that the contract constructor worked well and code of TRANSFER contract is actually deployed. assert_eq!( - &contracts::ContractInfoOf::::get(addr) + &pallet_contracts::ContractInfoOf::::get(addr) .and_then(|c| c.get_alive()) .unwrap() .code_hash, @@ -697,16 +709,19 @@ fn native_big_block_import_fails_on_fallback() { #[test] fn panic_execution_gives_error() { - let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ - >::hashed_key_for(alice()) => { - 0_u128.encode() - }, - >::hashed_key().to_vec() => { - 0_u128.encode() - }, - >::hashed_key().to_vec() => vec![0u8; 16], - >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { + top: map![ + >::hashed_key_for(alice()) => { + 0_u128.encode() + }, + >::hashed_key().to_vec() => { + 0_u128.encode() + }, + >::hashed_key().to_vec() => vec![0u8; 16], + >::hashed_key_for(0) => vec![0u8; 32] + ], + children: map![], + }); let r = executor_call:: _>( &mut t, @@ -729,16 +744,19 @@ fn panic_execution_gives_error() { #[test] fn successful_execution_gives_ok() { - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => { - (111 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => vec![0u8; 16], - >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { + top: map![ + >::hashed_key_for(alice()) => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => { + (111 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => vec![0u8; 16], + >::hashed_key_for(0) => vec![0u8; 32] + ], + children: map![], + }); let r = executor_call:: _>( &mut t, @@ -803,7 +821,10 @@ fn full_wasm_block_import_works_with_changes_trie() { #[test] fn should_import_block_with_test_client() { - use node_testing::client::{ClientExt, TestClientBuilderExt, TestClientBuilder, consensus::BlockOrigin}; + use node_testing::client::{ + ClientExt, TestClientBuilderExt, TestClientBuilder, + sp_consensus::BlockOrigin, + }; let client = TestClientBuilder::new().build(); let block1 = changes_trie_block(); diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index 0e7af95538720..38c9fb2b1d9b5 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -15,11 +15,11 @@ // along with Substrate. If not, see . use codec::{Encode, Decode}; -use runtime_support::{ +use frame_support::{ Hashable, }; -use state_machine::TestExternalities as CoreTestExternalities; -use primitives::{ +use sp_state_machine::TestExternalities as CoreTestExternalities; +use sp_core::{ Blake2Hasher, NeverNativeValue, NativeOrEncoded, traits::CodeExecutor, }; @@ -53,8 +53,8 @@ pub fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { node_testing::keyring::sign(xt, VERSION, GENESIS_HASH) } -pub fn default_transfer_call() -> balances::Call { - balances::Call::transfer::(bob().into(), 69 * DOLLARS) +pub fn default_transfer_call() -> pallet_balances::Call { + pallet_balances::Call::transfer::(bob().into(), 69 * DOLLARS) } pub fn from_block_number(n: u32) -> Header { @@ -100,7 +100,7 @@ pub fn construct_block( parent_hash: Hash, extrinsics: Vec, ) -> (Vec, Hash) { - use trie::{TrieConfiguration, trie_types::Layout}; + use sp_trie::{TrieConfiguration, trie_types::Layout}; // sign extrinsics. let extrinsics = extrinsics.into_iter().map(sign).collect::>(); diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs index f7e5a661b5537..06bdbc2eb28d5 100644 --- a/bin/node/executor/tests/fees.rs +++ b/bin/node/executor/tests/fees.rs @@ -15,13 +15,14 @@ // along with Substrate. If not, see . use codec::{Encode, Joiner}; -use runtime_support::{ +use frame_support::{ StorageValue, StorageMap, traits::Currency, weights::GetDispatchInfo, }; -use primitives::{ +use sp_core::{ Blake2Hasher, NeverNativeValue, map, + storage::Storage, }; use sp_runtime::{ Fixed64, @@ -60,11 +61,11 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() { vec![ CheckedExtrinsic { signed: None, - function: Call::Timestamp(timestamp::Call::set(42 * 1000)), + function: Call::Timestamp(pallet_timestamp::Call::set(42 * 1000)), }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(0, 0))), - function: Call::System(system::Call::fill_block()), + function: Call::System(frame_system::Call::fill_block()), } ] ); @@ -77,11 +78,11 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() { vec![ CheckedExtrinsic { signed: None, - function: Call::Timestamp(timestamp::Call::set(52 * 1000)), + function: Call::Timestamp(pallet_timestamp::Call::set(52 * 1000)), }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(1, 0))), - function: Call::System(system::Call::remark(vec![0; 1])), + function: Call::System(frame_system::Call::remark(vec![0; 1])), } ] ); @@ -131,19 +132,22 @@ fn transaction_fee_is_correct_ultimate() { // - 1 MILLICENTS in substrate node. // - 1 milli-dot based on current polkadot runtime. // (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`) - let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ - >::hashed_key_for(alice()) => { - (100 * DOLLARS).encode() - }, - >::hashed_key_for(bob()) => { - (10 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => { - (110 * DOLLARS).encode() - }, - >::hashed_key().to_vec() => vec![0u8; 16], - >::hashed_key_for(0) => vec![0u8; 32] - ], map![])); + let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { + top: map![ + >::hashed_key_for(alice()) => { + (100 * DOLLARS).encode() + }, + >::hashed_key_for(bob()) => { + (10 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => { + (110 * DOLLARS).encode() + }, + >::hashed_key().to_vec() => vec![0u8; 16], + >::hashed_key_for(0) => vec![0u8; 32] + ], + children: map![], + }); let tip = 1_000_000; let xt = sign(CheckedExtrinsic { @@ -220,12 +224,12 @@ fn block_weight_capacity_report() { let num_transfers = block_number * factor; let mut xts = (0..num_transfers).map(|i| CheckedExtrinsic { signed: Some((charlie(), signed_extra(nonce + i as Index, 0))), - function: Call::Balances(balances::Call::transfer(bob().into(), 0)), + function: Call::Balances(pallet_balances::Call::transfer(bob().into(), 0)), }).collect::>(); xts.insert(0, CheckedExtrinsic { signed: None, - function: Call::Timestamp(timestamp::Call::set(time * 1000)), + function: Call::Timestamp(pallet_timestamp::Call::set(time * 1000)), }); // NOTE: this is super slow. Can probably be improved. @@ -292,11 +296,11 @@ fn block_length_capacity_report() { vec![ CheckedExtrinsic { signed: None, - function: Call::Timestamp(timestamp::Call::set(time * 1000)), + function: Call::Timestamp(pallet_timestamp::Call::set(time * 1000)), }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(nonce, 0))), - function: Call::System(system::Call::remark(vec![0u8; (block_number * factor) as usize])), + function: Call::System(frame_system::Call::remark(vec![0u8; (block_number * factor) as usize])), }, ] ); diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 4e78dacad1258..8c31a0322463d 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -17,13 +17,13 @@ use node_runtime::{ Call, Runtime, SubmitTransaction, }; -use primitives::testing::{KeyStore, ED25519, SR25519}; -use primitives::traits::{KeystoreExt, BareCryptoStorePtr}; -use primitives::offchain::{ +use sp_core::testing::{KeyStore, ED25519, SR25519}; +use sp_core::traits::{KeystoreExt, BareCryptoStorePtr}; +use sp_core::offchain::{ TransactionPoolExt, testing::TestTransactionPoolExt, }; -use system::offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction}; +use frame_system::offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction}; mod common; use self::common::*; @@ -36,14 +36,14 @@ fn should_submit_unsigned_transaction() { t.execute_with(|| { let signature = Default::default(); - let heartbeat_data = imonline::Heartbeat { + let heartbeat_data = pallet_im_online::Heartbeat { block_number: 1, network_state: Default::default(), session_index: 1, authority_index: 0, }; - let call = imonline::Call::heartbeat(heartbeat_data, signature); + let call = pallet_im_online::Call::heartbeat(heartbeat_data, signature); > ::submit_unsigned(call) .unwrap(); @@ -62,7 +62,7 @@ fn should_submit_signed_transaction() { t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { - let call = balances::Call::transfer(Default::default(), Default::default()); + let call = pallet_balances::Call::transfer(Default::default(), Default::default()); let results = >::submit_signed(call); From 73a936ca90911e4c89f41f9c9a09ab423b76e6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 20 Dec 2019 16:22:11 +0100 Subject: [PATCH 13/30] Add a key to keystore. --- Cargo.lock | 1 + bin/node/executor/Cargo.toml | 3 ++- bin/node/executor/tests/submit_transaction.rs | 9 ++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 73e04eb8ec8e0..12dca324de826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3082,6 +3082,7 @@ dependencies = [ "pallet-treasury 2.0.0", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "sc-executor 2.0.0", + "sp-application-crypto 2.0.0", "sp-core 2.0.0", "sp-io 2.0.0", "sp-runtime 2.0.0", diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 20812c78d3282..65787d2986f9a 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -23,12 +23,13 @@ node-testing = { version = "2.0.0", path = "../testing" } pallet-balances = { version = "2.0.0", path = "../../../frame/balances" } pallet-contracts = { version = "2.0.0", path = "../../../frame/contracts" } pallet-grandpa = { version = "2.0.0", path = "../../../frame/grandpa" } +pallet-im-online = { version = "2.0.0", path = "../../../frame/im-online" } pallet-indices = { version = "2.0.0", path = "../../../frame/indices" } pallet-session = { version = "2.0.0", path = "../../../frame/session" } -pallet-im-online = { version = "2.0.0", path = "../../../frame/im-online" } pallet-timestamp = { version = "2.0.0", path = "../../../frame/timestamp" } pallet-transaction-payment = { version = "2.0.0", path = "../../../frame/transaction-payment" } pallet-treasury = { version = "2.0.0", path = "../../../frame/treasury" } +sp-application-crypto = { version = "2.0.0", path = "../../../primitives/application-crypto" } sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } substrate-test-client = { version = "2.0.0", path = "../../../test-utils/client" } wabt = "0.9.2" diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 8c31a0322463d..cfa2eab6291b0 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -54,11 +54,18 @@ fn should_submit_unsigned_transaction() { #[test] fn should_submit_signed_transaction() { + use pallet_im_online::sr25519 as im_online_crypto; + use sp_application_crypto::traits::AppKey; + let mut t = new_test_ext(COMPACT_CODE, false); let (pool, state) = TestTransactionPoolExt::new(); t.register_extension(TransactionPoolExt::new(pool)); - let mut keystore = KeyStore::new(); + let keystore = KeyStore::new(); + keystore.write().sr25519_generate_new( + im_online_crypto::AuthorityPair::ID, + Some("hunter2") + ); t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { From d776d5099a7a43ebc3eab93c553dedefba5a5753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 20 Dec 2019 17:22:58 +0100 Subject: [PATCH 14/30] Fix the tests. --- Cargo.lock | 1 + bin/node/executor/Cargo.toml | 1 + bin/node/executor/tests/submit_transaction.rs | 67 ++++++++++++++++--- bin/node/runtime/src/lib.rs | 5 +- frame/system/src/lib.rs | 3 + frame/system/src/offchain.rs | 35 +++++++--- 6 files changed, 94 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12dca324de826..7c01176790d7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3066,6 +3066,7 @@ dependencies = [ name = "node-executor" version = "2.0.0" dependencies = [ + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "frame-support 2.0.0", "frame-system 2.0.0", "node-primitives 2.0.0", diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 65787d2986f9a..1c72f982934c5 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -17,6 +17,7 @@ node-primitives = { version = "2.0.0", path = "../primitives" } node-runtime = { version = "2.0.0", path = "../runtime" } [dev-dependencies] +env_logger = "*" frame-support = { version = "2.0.0", path = "../../../frame/support" } frame-system = { version = "2.0.0", path = "../../../frame/system" } node-testing = { version = "2.0.0", path = "../testing" } diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index cfa2eab6291b0..f5f8d61cdb3ec 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -17,13 +17,15 @@ use node_runtime::{ Call, Runtime, SubmitTransaction, }; -use sp_core::testing::{KeyStore, ED25519, SR25519}; -use sp_core::traits::{KeystoreExt, BareCryptoStorePtr}; +use sp_application_crypto::AppKey; +use sp_core::testing::KeyStore; +use sp_core::traits::KeystoreExt; use sp_core::offchain::{ TransactionPoolExt, testing::TestTransactionPoolExt, }; use frame_system::offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction}; +use pallet_im_online::sr25519::AuthorityPair as Key; mod common; use self::common::*; @@ -52,23 +54,31 @@ fn should_submit_unsigned_transaction() { }); } +const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put clutch what kitten"; + #[test] fn should_submit_signed_transaction() { - use pallet_im_online::sr25519 as im_online_crypto; - use sp_application_crypto::traits::AppKey; + env_logger::try_init(); let mut t = new_test_ext(COMPACT_CODE, false); let (pool, state) = TestTransactionPoolExt::new(); t.register_extension(TransactionPoolExt::new(pool)); let keystore = KeyStore::new(); - keystore.write().sr25519_generate_new( - im_online_crypto::AuthorityPair::ID, - Some("hunter2") - ); + keystore.write().sr25519_generate_new(Key::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(Key::ID, Some(&format!("{}/hunter2", PHRASE))).unwrap(); + keystore.write().sr25519_generate_new(Key::ID, Some(&format!("{}/hunter3", PHRASE))).unwrap(); t.register_extension(KeystoreExt(keystore)); t.execute_with(|| { + let keys = > + ::find_all_local_keys(); + assert_eq!(keys.len(), 3, "Missing keys: {:?}", keys); + + let can_sign = > + ::can_sign(); + assert!(can_sign, "Since there are keys, `can_sign` should return true"); + let call = pallet_balances::Call::transfer(Default::default(), Default::default()); let results = >::submit_signed(call); @@ -79,3 +89,44 @@ fn should_submit_signed_transaction() { assert_eq!(state.read().transactions.len(), len); }); } + +#[test] +fn should_submit_signed_twice_from_the_same_account() { + env_logger::try_init(); + + let mut t = new_test_ext(COMPACT_CODE, false); + let (pool, state) = TestTransactionPoolExt::new(); + t.register_extension(TransactionPoolExt::new(pool)); + + let keystore = KeyStore::new(); + keystore.write().sr25519_generate_new(Key::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + t.register_extension(KeystoreExt(keystore)); + + t.execute_with(|| { + let call = pallet_balances::Call::transfer(Default::default(), Default::default()); + let results = + >::submit_signed(call); + + let len = results.len(); + assert_eq!(len, 1); + assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len); + assert_eq!(state.read().transactions.len(), len); + + // submit another one from the same account. The nonce should be incremented. + let call = pallet_balances::Call::transfer(Default::default(), Default::default()); + let results = + >::submit_signed(call); + + let len = results.len(); + assert_eq!(len, 1); + assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len); + assert_eq!(state.read().transactions.len(), len + 1); + + // now check that the transactions are not equal + let s = state.read(); + assert!( + s.transactions[0] != s.transactions[1], + "Transactions should have different nonces." + ); + }); +} diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 86a1eb9f54e80..d1c2257d22800 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -490,7 +490,10 @@ impl frame_system::offchain::CreateTransaction for index: Index, ) -> Option<(Call, ::SignaturePayload)> { let period = 1 << 8; - let current_block = System::block_number().saturated_into::(); + let current_block = System::block_number() + .saturated_into::() + // make sure we actually construct on top of the parent block. + .saturating_sub(1); let tip = 0; let extra: SignedExtra = ( frame_system::CheckVersion::::new(), diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index f713811f21510..b8ab070f54d96 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1046,6 +1046,9 @@ impl SignedExtension for CheckEra { fn additional_signed(&self) -> Result { let current_u64 = >::block_number().saturated_into::(); let n = (self.0).0.birth(current_u64).saturated_into::(); + frame_support::debug::native::trace!( + "Looking for hash of {:?} (current: {:?})", n, current_u64 + ); if !>::exists(n) { Err(InvalidTransaction::AncientBirthBlock.into()) } else { diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 9c406622eeb11..aee93882a5a64 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -56,8 +56,8 @@ pub trait CreateTransaction { /// It has a blanket implementation for all `RuntimeAppPublic` types, /// so it's enough to pass an application-specific crypto type. /// -/// To easily create `SignedTransaction`s have a look at the -/// `TransactionSubmitter` type. +/// To easily create `SignedTransaction`s have a look at the +/// [`TransactionSubmitter`](frame_system::offchain::TransactionSubmitter) type. pub trait Signer { /// Sign any encodable payload with given account and produce a signature. /// @@ -104,7 +104,8 @@ pub type PublicOf = < /// A trait to sign and submit transactions in off-chain calls. /// /// NOTE: Most likely you should not implement this trait yourself. -/// There is an implementation for `TransactionSubmitter` type, which +/// There is an implementation for +/// [`TransactionSubmitter`](frame_system::offchain::TransactionSubmitter) type, which /// you should use. pub trait SignAndSubmitTransaction { /// Unchecked extrinsic type. @@ -145,7 +146,8 @@ pub trait SignAndSubmitTransaction { /// A trait to submit unsigned transactions in off-chain calls. /// /// NOTE: Most likely you should not implement this trait yourself. -/// There is an implementation for `TransactionSubmitter` type, which +/// There is an implementation for +/// [`TransactionSubmitter`](frame_system::offchain::TransactionSubmitter) type, which /// you should use. pub trait SubmitUnsignedTransaction { /// Unchecked extrinsic type. @@ -165,7 +167,8 @@ pub trait SubmitUnsignedTransaction { /// from accounts in node's local keystore. /// /// NOTE: Most likely you should not implement this trait yourself. -/// There is an implementation for `TransactionSubmitter` type, which +/// There is an implementation for +/// [`TransactionSubmitter`](frame_system::offchain::TransactionSubmitter) type, which /// you should use. pub trait SubmitSignedTransaction { /// A `SignAndSubmitTransaction` implementation. @@ -184,15 +187,29 @@ pub trait SubmitSignedTransaction { PublicOf, )>; - /// Check if there are any keys that could be used to send a transaction. + /// Find all available local keys. + /// + /// This is equivalent of calling `find_local_keys(None)`. + fn find_all_local_keys() -> Vec<(T::AccountId, PublicOf)> { + Self::find_local_keys(None as Option>) + } + + /// Check if there are keys for any of given accounts that could be used to send a transaction. /// /// This check can be used as an early-exit condition to avoid doing too /// much work, before we actually realise that there are no accounts that you /// we could use for signing. - fn can_sign(accounts: Option>) -> bool { + fn can_sign_with(accounts: Option>) -> bool { !Self::find_local_keys(accounts).is_empty() } + /// Check if there are any keys that could be used for signing. + /// + /// This is equivalent of calling `can_sign_with(None)`. + fn can_sign() -> bool { + Self::can_sign_with(None as Option>) + } + /// Create and submit signed transactions from supported accounts. /// /// This method should intersect given list of accounts with the ones @@ -225,7 +242,7 @@ pub trait SubmitSignedTransaction { fn submit_signed( call: impl Into + Clone, ) -> Vec<(T::AccountId, Result<(), ()>)> { - let keys = Self::find_local_keys(None as Option>); + let keys = Self::find_all_local_keys(); keys.into_iter().map(|(account, pub_key)| { let call = call.clone().into(); ( @@ -332,7 +349,7 @@ impl SubmitSignedTransaction for TransactionSubmitter } } - fn can_sign(accounts: Option>) -> bool { + fn can_sign_with(accounts: Option>) -> bool { // early exit if we care about any account. if accounts.is_none() { !S::all().is_empty() From 0ac61d7c7a4d03e9c24b19f0bfeabac20f867178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 20 Dec 2019 17:24:40 +0100 Subject: [PATCH 15/30] Remove env_logger. --- bin/node/executor/Cargo.toml | 1 - bin/node/executor/tests/submit_transaction.rs | 4 ---- 2 files changed, 5 deletions(-) diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 1c72f982934c5..65787d2986f9a 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -17,7 +17,6 @@ node-primitives = { version = "2.0.0", path = "../primitives" } node-runtime = { version = "2.0.0", path = "../runtime" } [dev-dependencies] -env_logger = "*" frame-support = { version = "2.0.0", path = "../../../frame/support" } frame-system = { version = "2.0.0", path = "../../../frame/system" } node-testing = { version = "2.0.0", path = "../testing" } diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index f5f8d61cdb3ec..b16b40cc38755 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -58,8 +58,6 @@ const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put c #[test] fn should_submit_signed_transaction() { - env_logger::try_init(); - let mut t = new_test_ext(COMPACT_CODE, false); let (pool, state) = TestTransactionPoolExt::new(); t.register_extension(TransactionPoolExt::new(pool)); @@ -92,8 +90,6 @@ fn should_submit_signed_transaction() { #[test] fn should_submit_signed_twice_from_the_same_account() { - env_logger::try_init(); - let mut t = new_test_ext(COMPACT_CODE, false); let (pool, state) = TestTransactionPoolExt::new(); t.register_extension(TransactionPoolExt::new(pool)); From 87555396010bcb176a1acf38ae765b8c058133ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 20 Dec 2019 17:39:24 +0100 Subject: [PATCH 16/30] Fix sending multiple transactions. --- Cargo.lock | 1 - bin/node/executor/tests/submit_transaction.rs | 19 +++++++++++++------ frame/system/src/offchain.rs | 6 +++++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c01176790d7c..12dca324de826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3066,7 +3066,6 @@ dependencies = [ name = "node-executor" version = "2.0.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "frame-support 2.0.0", "frame-system 2.0.0", "node-primitives 2.0.0", diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index b16b40cc38755..f041eca7e5e4d 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -15,7 +15,7 @@ // along with Substrate. If not, see . use node_runtime::{ - Call, Runtime, SubmitTransaction, + Call, Runtime, SubmitTransaction, UncheckedExtrinsic, }; use sp_application_crypto::AppKey; use sp_core::testing::KeyStore; @@ -26,6 +26,7 @@ use sp_core::offchain::{ }; use frame_system::offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction}; use pallet_im_online::sr25519::AuthorityPair as Key; +use codec::Decode; mod common; use self::common::*; @@ -106,7 +107,7 @@ fn should_submit_signed_twice_from_the_same_account() { let len = results.len(); assert_eq!(len, 1); assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len); - assert_eq!(state.read().transactions.len(), len); + assert_eq!(state.read().transactions.len(), 1); // submit another one from the same account. The nonce should be incremented. let call = pallet_balances::Call::transfer(Default::default(), Default::default()); @@ -116,13 +117,19 @@ fn should_submit_signed_twice_from_the_same_account() { let len = results.len(); assert_eq!(len, 1); assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len); - assert_eq!(state.read().transactions.len(), len + 1); + assert_eq!(state.read().transactions.len(), 2); - // now check that the transactions are not equal + // now check that the transaction nonces are not equal let s = state.read(); + fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce { + let extra = tx.signature.unwrap().2; + extra.3 + } + let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap()); + let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap()); assert!( - s.transactions[0] != s.transactions[1], - "Transactions should have different nonces." + nonce1 != nonce2, + "Transactions should have different nonces. Got: {:?}", nonce1 ); }); } diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index aee93882a5a64..fcc355e695b70 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -136,8 +136,12 @@ pub trait SignAndSubmitTransaction { expected, ); let (call, signature_data) = Self::CreateTransaction - ::create_transaction::(call, public, id, expected) + ::create_transaction::(call, public, id.clone(), expected) .ok_or(())?; + // increment the nonce. This is fine, since the code should always + // be running in off-chain context, so we NEVER persists data. + >::inc_account_nonce(&id); + let xt = Self::Extrinsic::new(call, Some(signature_data)).ok_or(())?; sp_io::offchain::submit_transaction(xt.encode()) } From fc6b5fea1a53c812f43a94b7f89b587d559136fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 20 Dec 2019 17:53:49 +0100 Subject: [PATCH 17/30] Remove commented code. --- bin/node/executor/src/lib.rs | 1232 +--------------------------------- 1 file changed, 1 insertion(+), 1231 deletions(-) diff --git a/bin/node/executor/src/lib.rs b/bin/node/executor/src/lib.rs index 0ac2650ca3a0d..6aabb0b2545ab 100644 --- a/bin/node/executor/src/lib.rs +++ b/bin/node/executor/src/lib.rs @@ -27,1234 +27,4 @@ native_executor_instance!( node_runtime::api::dispatch, node_runtime::native_version ); -// <<<<<<< HEAD -// ======= -// -// #[cfg(test)] -// mod tests { -// use sc_executor::error::Result; -// use super::Executor; -// use codec::{Encode, Decode, Joiner}; -// use frame_support::{ -// Hashable, StorageValue, StorageMap, -// traits::Currency, -// weights::{GetDispatchInfo, DispatchInfo, DispatchClass}, -// }; -// use sp_state_machine::TestExternalities as CoreTestExternalities; -// use sp_core::{ -// Blake2Hasher, NeverNativeValue, NativeOrEncoded, map, -// traits::{CodeExecutor, Externalities}, storage::{well_known_keys, Storage}, -// }; -// use sp_runtime::{ -// Fixed64, traits::{Header as HeaderT, Hash as HashT, Convert}, ApplyExtrinsicResult, -// transaction_validity::InvalidTransaction, -// }; -// use pallet_contracts::ContractAddressFor; -// use sc_executor::{NativeExecutor, WasmExecutionMethod}; -// use frame_system::{EventRecord, Phase}; -// use node_runtime::{ -// Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, BuildStorage, -// System, TransactionPayment, Event, TransferFee, TransactionBaseFee, TransactionByteFee, -// WeightFeeCoefficient, constants::currency::*, -// }; -// use node_runtime::impls::LinearWeightToFee; -// use node_primitives::{Balance, Hash, BlockNumber}; -// use node_testing::keyring::*; -// use wabt; -// -// /// The wasm runtime code. -// /// -// /// `compact` since it is after post-processing with wasm-gc which performs tree-shaking thus -// /// making the binary slimmer. There is a convention to use compact version of the runtime -// /// as canonical. This is why `native_executor_instance` also uses the compact version of the -// /// runtime. -// const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY; -// -// /// The wasm runtime binary which hasn't undergone the compacting process. -// /// -// /// The idea here is to pass it as the current runtime code to the executor so the executor will -// /// have to execute provided wasm code instead of the native equivalent. This trick is used to -// /// test code paths that differ between native and wasm versions. -// const BLOATY_CODE: &[u8] = node_runtime::WASM_BINARY_BLOATY; -// -// const GENESIS_HASH: [u8; 32] = [69u8; 32]; -// -// const VERSION: u32 = node_runtime::VERSION.spec_version; -// -// type TestExternalities = CoreTestExternalities; -// -// fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { -// node_testing::keyring::sign(xt, VERSION, GENESIS_HASH) -// } -// -// /// Default transfer fee -// fn transfer_fee(extrinsic: &E, fee_multiplier: Fixed64) -> Balance { -// let length_fee = TransactionByteFee::get() * (extrinsic.encode().len() as Balance); -// -// let weight = default_transfer_call().get_dispatch_info().weight; -// let weight_fee = ::WeightToFee::convert(weight); -// -// let base_fee = TransactionBaseFee::get(); -// -// base_fee + fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee) + TransferFee::get() -// } -// -// fn default_transfer_call() -> pallet_balances::Call { -// pallet_balances::Call::transfer::(bob().into(), 69 * DOLLARS) -// } -// -// fn xt() -> UncheckedExtrinsic { -// sign(CheckedExtrinsic { -// signed: Some((alice(), signed_extra(0, 0))), -// function: Call::Balances(default_transfer_call()), -// }) -// } -// -// fn from_block_number(n: u32) -> Header { -// Header::new(n, Default::default(), Default::default(), [69; 32].into(), Default::default()) -// } -// -// fn executor() -> NativeExecutor { -// NativeExecutor::new(WasmExecutionMethod::Interpreted, None) -// } -// -// fn set_heap_pages(ext: &mut E, heap_pages: u64) { -// ext.place_storage(well_known_keys::HEAP_PAGES.to_vec(), Some(heap_pages.encode())); -// } -// -// fn executor_call< -// R:Decode + Encode + PartialEq, -// NC: FnOnce() -> std::result::Result + std::panic::UnwindSafe -// >( -// t: &mut TestExternalities, -// method: &str, -// data: &[u8], -// use_native: bool, -// native_call: Option, -// ) -> (Result>, bool) { -// let mut t = t.ext(); -// executor().call::<_, R, NC>( -// &mut t, -// method, -// data, -// use_native, -// native_call, -// ) -// } -// -// #[test] -// fn panic_execution_with_foreign_code_gives_error() { -// let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { -// top: map![ -// >::hashed_key_for(alice()) => { -// 69_u128.encode() -// }, -// >::hashed_key().to_vec() => { -// 69_u128.encode() -// }, -// >::hashed_key().to_vec() => { -// 0_u128.encode() -// }, -// >::hashed_key_for(0) => { -// vec![0u8; 32] -// } -// ], -// children: map![], -// }); -// -// let r = executor_call:: _>( -// &mut t, -// "Core_initialize_block", -// &vec![].and(&from_block_number(1u32)), -// true, -// None, -// ).0; -// assert!(r.is_ok()); -// let v = executor_call:: _>( -// &mut t, -// "BlockBuilder_apply_extrinsic", -// &vec![].and(&xt()), -// true, -// None, -// ).0.unwrap(); -// let r = ApplyExtrinsicResult::decode(&mut &v.as_encoded()[..]).unwrap(); -// assert_eq!(r, Err(InvalidTransaction::Payment.into())); -// } -// -// #[test] -// fn bad_extrinsic_with_native_equivalent_code_gives_error() { -// let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { -// top: map![ -// >::hashed_key_for(alice()) => { -// 69_u128.encode() -// }, -// >::hashed_key().to_vec() => { -// 69_u128.encode() -// }, -// >::hashed_key().to_vec() => { -// 0_u128.encode() -// }, -// >::hashed_key_for(0) => { -// vec![0u8; 32] -// } -// ], -// children: map![], -// }); -// -// let r = executor_call:: _>( -// &mut t, -// "Core_initialize_block", -// &vec![].and(&from_block_number(1u32)), -// true, -// None, -// ).0; -// assert!(r.is_ok()); -// let v = executor_call:: _>( -// &mut t, -// "BlockBuilder_apply_extrinsic", -// &vec![].and(&xt()), -// true, -// None, -// ).0.unwrap(); -// let r = ApplyExtrinsicResult::decode(&mut &v.as_encoded()[..]).unwrap(); -// assert_eq!(r, Err(InvalidTransaction::Payment.into())); -// } -// -// #[test] -// fn successful_execution_with_native_equivalent_code_gives_ok() { -// let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { -// top: map![ -// >::hashed_key_for(alice()) => { -// (111 * DOLLARS).encode() -// }, -// >::hashed_key().to_vec() => { -// (111 * DOLLARS).encode() -// }, -// >::hashed_key().to_vec() => vec![0u8; 16], -// >::hashed_key_for(0) => vec![0u8; 32] -// ], -// children: map![], -// }); -// -// let r = executor_call:: _>( -// &mut t, -// "Core_initialize_block", -// &vec![].and(&from_block_number(1u32)), -// true, -// None, -// ).0; -// assert!(r.is_ok()); -// -// let fm = t.execute_with(TransactionPayment::next_fee_multiplier); -// -// let r = executor_call:: _>( -// &mut t, -// "BlockBuilder_apply_extrinsic", -// &vec![].and(&xt()), -// true, -// None, -// ).0; -// assert!(r.is_ok()); -// -// t.execute_with(|| { -// assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); -// assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); -// }); -// } -// -// #[test] -// fn successful_execution_with_foreign_code_gives_ok() { -// let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { -// top: map![ -// >::hashed_key_for(alice()) => { -// (111 * DOLLARS).encode() -// }, -// >::hashed_key().to_vec() => { -// (111 * DOLLARS).encode() -// }, -// >::hashed_key().to_vec() => vec![0u8; 16], -// >::hashed_key_for(0) => vec![0u8; 32] -// ], -// children: map![], -// }); -// -// let r = executor_call:: _>( -// &mut t, -// "Core_initialize_block", -// &vec![].and(&from_block_number(1u32)), -// true, -// None, -// ).0; -// assert!(r.is_ok()); -// -// let fm = t.execute_with(TransactionPayment::next_fee_multiplier); -// -// let r = executor_call:: _>( -// &mut t, -// "BlockBuilder_apply_extrinsic", -// &vec![].and(&xt()), -// true, -// None, -// ).0; -// assert!(r.is_ok()); -// -// t.execute_with(|| { -// assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); -// assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); -// }); -// } -// -// fn new_test_ext(code: &[u8], support_changes_trie: bool) -> TestExternalities { -// let mut ext = TestExternalities::new_with_code( -// code, -// node_testing::genesis::config(support_changes_trie, Some(code)).build_storage().unwrap(), -// ); -// ext.changes_trie_storage().insert(0, GENESIS_HASH.into(), Default::default()); -// ext -// } -// -// fn construct_block( -// env: &mut TestExternalities, -// number: BlockNumber, -// parent_hash: Hash, -// extrinsics: Vec, -// ) -> (Vec, Hash) { -// use sp_trie::{TrieConfiguration, trie_types::Layout}; -// -// // sign extrinsics. -// let extrinsics = extrinsics.into_iter().map(sign).collect::>(); -// -// // calculate the header fields that we can. -// let extrinsics_root = Layout::::ordered_trie_root( -// extrinsics.iter().map(Encode::encode) -// ).to_fixed_bytes() -// .into(); -// -// let header = Header { -// parent_hash, -// number, -// extrinsics_root, -// state_root: Default::default(), -// digest: Default::default(), -// }; -// -// // execute the block to get the real header. -// executor_call:: _>( -// env, -// "Core_initialize_block", -// &header.encode(), -// true, -// None, -// ).0.unwrap(); -// -// for i in extrinsics.iter() { -// executor_call:: _>( -// env, -// "BlockBuilder_apply_extrinsic", -// &i.encode(), -// true, -// None, -// ).0.unwrap(); -// } -// -// let header = match executor_call:: _>( -// env, -// "BlockBuilder_finalize_block", -// &[0u8;0], -// true, -// None, -// ).0.unwrap() { -// NativeOrEncoded::Native(_) => unreachable!(), -// NativeOrEncoded::Encoded(h) => Header::decode(&mut &h[..]).unwrap(), -// }; -// -// let hash = header.blake2_256(); -// (Block { header, extrinsics }.encode(), hash.into()) -// } -// -// fn changes_trie_block() -> (Vec, Hash) { -// construct_block( -// &mut new_test_ext(COMPACT_CODE, true), -// 1, -// GENESIS_HASH.into(), -// vec![ -// CheckedExtrinsic { -// signed: None, -// function: Call::Timestamp(pallet_timestamp::Call::set(42 * 1000)), -// }, -// CheckedExtrinsic { -// signed: Some((alice(), signed_extra(0, 0))), -// function: Call::Balances(pallet_balances::Call::transfer(bob().into(), 69 * DOLLARS)), -// }, -// ] -// ) -// } -// -// // block 1 and 2 must be created together to ensure transactions are only signed once (since they -// // are not guaranteed to be deterministic) and to ensure that the correct state is propagated -// // from block1's execution to block2 to derive the correct storage_root. -// fn blocks() -> ((Vec, Hash), (Vec, Hash)) { -// let mut t = new_test_ext(COMPACT_CODE, false); -// let block1 = construct_block( -// &mut t, -// 1, -// GENESIS_HASH.into(), -// vec![ -// CheckedExtrinsic { -// signed: None, -// function: Call::Timestamp(pallet_timestamp::Call::set(42 * 1000)), -// }, -// CheckedExtrinsic { -// signed: Some((alice(), signed_extra(0, 0))), -// function: Call::Balances(pallet_balances::Call::transfer(bob().into(), 69 * DOLLARS)), -// }, -// ] -// ); -// let block2 = construct_block( -// &mut t, -// 2, -// block1.1.clone(), -// vec![ -// CheckedExtrinsic { -// signed: None, -// function: Call::Timestamp(pallet_timestamp::Call::set(52 * 1000)), -// }, -// CheckedExtrinsic { -// signed: Some((bob(), signed_extra(0, 0))), -// function: Call::Balances(pallet_balances::Call::transfer(alice().into(), 5 * DOLLARS)), -// }, -// CheckedExtrinsic { -// signed: Some((alice(), signed_extra(1, 0))), -// function: Call::Balances(pallet_balances::Call::transfer(bob().into(), 15 * DOLLARS)), -// } -// ] -// ); -// -// // session change => consensus authorities change => authorities change digest item appears -// let digest = Header::decode(&mut &block2.0[..]).unwrap().digest; -// assert_eq!(digest.logs().len(), 0); -// -// (block1, block2) -// } -// -// fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec, Hash) { -// construct_block( -// &mut new_test_ext(COMPACT_CODE, false), -// 1, -// GENESIS_HASH.into(), -// vec![ -// CheckedExtrinsic { -// signed: None, -// function: Call::Timestamp(pallet_timestamp::Call::set(time * 1000)), -// }, -// CheckedExtrinsic { -// signed: Some((alice(), signed_extra(nonce, 0))), -// function: Call::System(frame_system::Call::remark(vec![0; size])), -// } -// ] -// ) -// } -// -// #[test] -// fn full_native_block_import_works() { -// let mut t = new_test_ext(COMPACT_CODE, false); -// -// let (block1, block2) = blocks(); -// -// let mut alice_last_known_balance: Balance = Default::default(); -// let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); -// -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block1.0, -// true, -// None, -// ).0.unwrap(); -// -// t.execute_with(|| { -// assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); -// assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); -// alice_last_known_balance = Balances::total_balance(&alice()); -// let events = vec![ -// EventRecord { -// phase: Phase::ApplyExtrinsic(0), -// event: Event::system(frame_system::Event::ExtrinsicSuccess( -// DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } -// )), -// topics: vec![], -// }, -// EventRecord { -// phase: Phase::ApplyExtrinsic(1), -// event: Event::pallet_treasury(pallet_treasury::RawEvent::Deposit(1984800000000)), -// topics: vec![], -// }, -// EventRecord { -// phase: Phase::ApplyExtrinsic(1), -// event: Event::pallet_balances(pallet_balances::RawEvent::Transfer( -// alice().into(), -// bob().into(), -// 69 * DOLLARS, -// 1 * CENTS, -// )), -// topics: vec![], -// }, -// EventRecord { -// phase: Phase::ApplyExtrinsic(1), -// event: Event::system(frame_system::Event::ExtrinsicSuccess( -// DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } -// )), -// topics: vec![], -// }, -// ]; -// assert_eq!(System::events(), events); -// }); -// -// fm = t.execute_with(TransactionPayment::next_fee_multiplier); -// -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block2.0, -// true, -// None, -// ).0.unwrap(); -// -// t.execute_with(|| { -// assert_eq!( -// Balances::total_balance(&alice()), -// alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), -// ); -// assert_eq!( -// Balances::total_balance(&bob()), -// 179 * DOLLARS - transfer_fee(&xt(), fm), -// ); -// let events = vec![ -// EventRecord { -// phase: Phase::ApplyExtrinsic(0), -// event: Event::system(frame_system::Event::ExtrinsicSuccess( -// DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } -// )), -// topics: vec![], -// }, -// EventRecord { -// phase: Phase::ApplyExtrinsic(1), -// event: Event::pallet_treasury(pallet_treasury::RawEvent::Deposit(1984788199392)), -// topics: vec![], -// }, -// EventRecord { -// phase: Phase::ApplyExtrinsic(1), -// event: Event::pallet_balances( -// pallet_balances::RawEvent::Transfer( -// bob().into(), -// alice().into(), -// 5 * DOLLARS, -// 1 * CENTS, -// ) -// ), -// topics: vec![], -// }, -// EventRecord { -// phase: Phase::ApplyExtrinsic(1), -// event: Event::system(frame_system::Event::ExtrinsicSuccess( -// DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } -// )), -// topics: vec![], -// }, -// EventRecord { -// phase: Phase::ApplyExtrinsic(2), -// event: Event::pallet_treasury(pallet_treasury::RawEvent::Deposit(1984788199392)), -// topics: vec![], -// }, -// EventRecord { -// phase: Phase::ApplyExtrinsic(2), -// event: Event::pallet_balances( -// pallet_balances::RawEvent::Transfer( -// alice().into(), -// bob().into(), -// 15 * DOLLARS, -// 1 * CENTS, -// ) -// ), -// topics: vec![], -// }, -// EventRecord { -// phase: Phase::ApplyExtrinsic(2), -// event: Event::system(frame_system::Event::ExtrinsicSuccess( -// DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } -// )), -// topics: vec![], -// }, -// ]; -// assert_eq!(System::events(), events); -// }); -// } -// -// #[test] -// fn full_wasm_block_import_works() { -// let mut t = new_test_ext(COMPACT_CODE, false); -// -// let (block1, block2) = blocks(); -// -// let mut alice_last_known_balance: Balance = Default::default(); -// let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); -// -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block1.0, -// false, -// None, -// ).0.unwrap(); -// -// t.execute_with(|| { -// assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); -// assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); -// alice_last_known_balance = Balances::total_balance(&alice()); -// }); -// -// fm = t.execute_with(TransactionPayment::next_fee_multiplier); -// -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block2.0, -// false, -// None, -// ).0.unwrap(); -// -// t.execute_with(|| { -// assert_eq!( -// Balances::total_balance(&alice()), -// alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), -// ); -// assert_eq!( -// Balances::total_balance(&bob()), -// 179 * DOLLARS - 1 * transfer_fee(&xt(), fm), -// ); -// }); -// } -// -// const CODE_TRANSFER: &str = r#" -// (module -// ;; ext_call( -// ;; callee_ptr: u32, -// ;; callee_len: u32, -// ;; gas: u64, -// ;; value_ptr: u32, -// ;; value_len: u32, -// ;; input_data_ptr: u32, -// ;; input_data_len: u32 -// ;; ) -> u32 -// (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) -// (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) -// (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) -// (import "env" "memory" (memory 1 1)) -// (func (export "deploy") -// ) -// (func (export "call") -// (block $fail -// ;; load and check the input data (which is stored in the scratch buffer). -// ;; fail if the input size is not != 4 -// (br_if $fail -// (i32.ne -// (i32.const 4) -// (call $ext_scratch_size) -// ) -// ) -// -// (call $ext_scratch_read -// (i32.const 0) -// (i32.const 0) -// (i32.const 4) -// ) -// -// -// (br_if $fail -// (i32.ne -// (i32.load8_u (i32.const 0)) -// (i32.const 0) -// ) -// ) -// (br_if $fail -// (i32.ne -// (i32.load8_u (i32.const 1)) -// (i32.const 1) -// ) -// ) -// (br_if $fail -// (i32.ne -// (i32.load8_u (i32.const 2)) -// (i32.const 2) -// ) -// ) -// (br_if $fail -// (i32.ne -// (i32.load8_u (i32.const 3)) -// (i32.const 3) -// ) -// ) -// -// (drop -// (call $ext_call -// (i32.const 4) ;; Pointer to "callee" address. -// (i32.const 32) ;; Length of "callee" address. -// (i64.const 0) ;; How much gas to devote for the execution. 0 = all. -// (i32.const 36) ;; Pointer to the buffer with value to transfer -// (i32.const 16) ;; Length of the buffer with value to transfer. -// (i32.const 0) ;; Pointer to input data buffer address -// (i32.const 0) ;; Length of input data buffer -// ) -// ) -// -// (return) -// ) -// unreachable -// ) -// ;; Destination AccountId to transfer the funds. -// ;; Represented by H256 (32 bytes long) in little endian. -// (data (i32.const 4) -// "\09\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" -// "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" -// "\00\00\00\00" -// ) -// ;; Amount of value to transfer. -// ;; Represented by u128 (16 bytes long) in little endian. -// (data (i32.const 36) -// "\06\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" -// "\00\00" -// ) -// ) -// "#; -// -// #[test] -// fn deploying_wasm_contract_should_work() { -// let transfer_code = wabt::wat2wasm(CODE_TRANSFER).unwrap(); -// let transfer_ch = ::Hashing::hash(&transfer_code); -// -// let addr = ::DetermineContractAddress::contract_address_for( -// &transfer_ch, -// &[], -// &charlie(), -// ); -// -// let b = construct_block( -// &mut new_test_ext(COMPACT_CODE, false), -// 1, -// GENESIS_HASH.into(), -// vec![ -// CheckedExtrinsic { -// signed: None, -// function: Call::Timestamp(pallet_timestamp::Call::set(42 * 1000)), -// }, -// CheckedExtrinsic { -// signed: Some((charlie(), signed_extra(0, 0))), -// function: Call::Contracts( -// pallet_contracts::Call::put_code::(10_000, transfer_code) -// ), -// }, -// CheckedExtrinsic { -// signed: Some((charlie(), signed_extra(1, 0))), -// function: Call::Contracts( -// pallet_contracts::Call::instantiate::(1 * DOLLARS, 10_000, transfer_ch, Vec::new()) -// ), -// }, -// CheckedExtrinsic { -// signed: Some((charlie(), signed_extra(2, 0))), -// function: Call::Contracts( -// pallet_contracts::Call::call::( -// pallet_indices::address::Address::Id(addr.clone()), -// 10, -// 10_000, -// vec![0x00, 0x01, 0x02, 0x03] -// ) -// ), -// }, -// ] -// ); -// -// let mut t = new_test_ext(COMPACT_CODE, false); -// -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &b.0, -// false, -// None, -// ).0.unwrap(); -// -// t.execute_with(|| { -// // Verify that the contract constructor worked well and code of TRANSFER contract is actually deployed. -// assert_eq!( -// &pallet_contracts::ContractInfoOf::::get(addr) -// .and_then(|c| c.get_alive()) -// .unwrap() -// .code_hash, -// &transfer_ch -// ); -// }); -// } -// -// #[test] -// fn wasm_big_block_import_fails() { -// let mut t = new_test_ext(COMPACT_CODE, false); -// -// set_heap_pages(&mut t.ext(), 4); -// -// let result = executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block_with_size(42, 0, 120_000).0, -// false, -// None, -// ).0; -// assert!(result.is_err()); // Err(Wasmi(Trap(Trap { kind: Host(AllocatorOutOfSpace) }))) -// } -// -// #[test] -// fn native_big_block_import_succeeds() { -// let mut t = new_test_ext(COMPACT_CODE, false); -// -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block_with_size(42, 0, 120_000).0, -// true, -// None, -// ).0.unwrap(); -// } -// -// #[test] -// fn native_big_block_import_fails_on_fallback() { -// let mut t = new_test_ext(COMPACT_CODE, false); -// -// assert!( -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block_with_size(42, 0, 120_000).0, -// false, -// None, -// ).0.is_err() -// ); -// } -// -// #[test] -// fn panic_execution_gives_error() { -// let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { -// top: map![ -// >::hashed_key_for(alice()) => { -// 0_u128.encode() -// }, -// >::hashed_key().to_vec() => { -// 0_u128.encode() -// }, -// >::hashed_key().to_vec() => vec![0u8; 16], -// >::hashed_key_for(0) => vec![0u8; 32] -// ], -// children: map![], -// }); -// -// let r = executor_call:: _>( -// &mut t, -// "Core_initialize_block", -// &vec![].and(&from_block_number(1u32)), -// false, -// None, -// ).0; -// assert!(r.is_ok()); -// let r = executor_call:: _>( -// &mut t, -// "BlockBuilder_apply_extrinsic", -// &vec![].and(&xt()), -// false, -// None, -// ).0.unwrap().into_encoded(); -// let r = ApplyExtrinsicResult::decode(&mut &r[..]).unwrap(); -// assert_eq!(r, Err(InvalidTransaction::Payment.into())); -// } -// -// #[test] -// fn successful_execution_gives_ok() { -// let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { -// top: map![ -// >::hashed_key_for(alice()) => { -// (111 * DOLLARS).encode() -// }, -// >::hashed_key().to_vec() => { -// (111 * DOLLARS).encode() -// }, -// >::hashed_key().to_vec() => vec![0u8; 16], -// >::hashed_key_for(0) => vec![0u8; 32] -// ], -// children: map![], -// }); -// -// let r = executor_call:: _>( -// &mut t, -// "Core_initialize_block", -// &vec![].and(&from_block_number(1u32)), -// false, -// None, -// ).0; -// assert!(r.is_ok()); -// let fm = t.execute_with(TransactionPayment::next_fee_multiplier); -// let r = executor_call:: _>( -// &mut t, -// "BlockBuilder_apply_extrinsic", -// &vec![].and(&xt()), -// false, -// None, -// ).0.unwrap().into_encoded(); -// ApplyExtrinsicResult::decode(&mut &r[..]) -// .unwrap() -// .expect("Extrinsic could be applied") -// .expect("Extrinsic did not fail"); -// -// t.execute_with(|| { -// assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt(), fm)); -// assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); -// }); -// } -// -// #[test] -// fn full_native_block_import_works_with_changes_trie() { -// let block1 = changes_trie_block(); -// let block_data = block1.0; -// let block = Block::decode(&mut &block_data[..]).unwrap(); -// -// let mut t = new_test_ext(COMPACT_CODE, true); -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block.encode(), -// true, -// None, -// ).0.unwrap(); -// -// assert!(t.ext().storage_changes_root(&GENESIS_HASH.encode()).unwrap().is_some()); -// } -// -// #[test] -// fn full_wasm_block_import_works_with_changes_trie() { -// let block1 = changes_trie_block(); -// -// let mut t = new_test_ext(COMPACT_CODE, true); -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block1.0, -// false, -// None, -// ).0.unwrap(); -// -// assert!(t.ext().storage_changes_root(&GENESIS_HASH.encode()).unwrap().is_some()); -// } -// -// #[test] -// fn should_import_block_with_test_client() { -// use node_testing::client::{ -// ClientExt, TestClientBuilderExt, TestClientBuilder, sp_consensus::BlockOrigin -// }; -// -// let client = TestClientBuilder::new().build(); -// let block1 = changes_trie_block(); -// let block_data = block1.0; -// let block = node_primitives::Block::decode(&mut &block_data[..]).unwrap(); -// -// client.import(BlockOrigin::Own, block).unwrap(); -// } -// -// -// #[test] -// fn fee_multiplier_increases_and_decreases_on_big_weight() { -// let mut t = new_test_ext(COMPACT_CODE, false); -// -// // initial fee multiplier must be zero -// let mut prev_multiplier = Fixed64::from_parts(0); -// -// t.execute_with(|| { -// assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier); -// }); -// -// let mut tt = new_test_ext(COMPACT_CODE, false); -// -// // big one in terms of weight. -// let block1 = construct_block( -// &mut tt, -// 1, -// GENESIS_HASH.into(), -// vec![ -// CheckedExtrinsic { -// signed: None, -// function: Call::Timestamp(pallet_timestamp::Call::set(42 * 1000)), -// }, -// CheckedExtrinsic { -// signed: Some((charlie(), signed_extra(0, 0))), -// function: Call::System(frame_system::Call::fill_block()), -// } -// ] -// ); -// -// // small one in terms of weight. -// let block2 = construct_block( -// &mut tt, -// 2, -// block1.1.clone(), -// vec![ -// CheckedExtrinsic { -// signed: None, -// function: Call::Timestamp(pallet_timestamp::Call::set(52 * 1000)), -// }, -// CheckedExtrinsic { -// signed: Some((charlie(), signed_extra(1, 0))), -// function: Call::System(frame_system::Call::remark(vec![0; 1])), -// } -// ] -// ); -// -// println!("++ Block 1 size: {} / Block 2 size {}", block1.0.encode().len(), block2.0.encode().len()); -// -// // execute a big block. -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block1.0, -// true, -// None, -// ).0.unwrap(); -// -// // weight multiplier is increased for next block. -// t.execute_with(|| { -// let fm = TransactionPayment::next_fee_multiplier(); -// println!("After a big block: {:?} -> {:?}", prev_multiplier, fm); -// assert!(fm > prev_multiplier); -// prev_multiplier = fm; -// }); -// -// // execute a big block. -// executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block2.0, -// true, -// None, -// ).0.unwrap(); -// -// // weight multiplier is increased for next block. -// t.execute_with(|| { -// let fm = TransactionPayment::next_fee_multiplier(); -// println!("After a small block: {:?} -> {:?}", prev_multiplier, fm); -// assert!(fm < prev_multiplier); -// }); -// } -// -// #[test] -// fn transaction_fee_is_correct_ultimate() { -// // This uses the exact values of substrate-node. -// // -// // weight of transfer call as of now: 1_000_000 -// // if weight of the cheapest weight would be 10^7, this would be 10^9, which is: -// // - 1 MILLICENTS in substrate node. -// // - 1 milli-dot based on current polkadot runtime. -// // (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`) -// let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { -// top: map![ -// >::hashed_key_for(alice()) => { -// (100 * DOLLARS).encode() -// }, -// >::hashed_key_for(bob()) => { -// (10 * DOLLARS).encode() -// }, -// >::hashed_key().to_vec() => { -// (110 * DOLLARS).encode() -// }, -// >::hashed_key().to_vec() => vec![0u8; 16], -// >::hashed_key_for(0) => vec![0u8; 32] -// ], -// children: map![], -// }); -// -// let tip = 1_000_000; -// let xt = sign(CheckedExtrinsic { -// signed: Some((alice(), signed_extra(0, tip))), -// function: Call::Balances(default_transfer_call()), -// }); -// -// let r = executor_call:: _>( -// &mut t, -// "Core_initialize_block", -// &vec![].and(&from_block_number(1u32)), -// true, -// None, -// ).0; -// -// assert!(r.is_ok()); -// let r = executor_call:: _>( -// &mut t, -// "BlockBuilder_apply_extrinsic", -// &vec![].and(&xt.clone()), -// true, -// None, -// ).0; -// assert!(r.is_ok()); -// -// t.execute_with(|| { -// assert_eq!(Balances::total_balance(&bob()), (10 + 69) * DOLLARS); -// // Components deducted from alice's balances: -// // - Weight fee -// // - Length fee -// // - Tip -// // - Creation-fee of bob's account. -// let mut balance_alice = (100 - 69) * DOLLARS; -// -// let length_fee = TransactionBaseFee::get() + -// TransactionByteFee::get() * -// (xt.clone().encode().len() as Balance); -// balance_alice -= length_fee; -// -// let weight = default_transfer_call().get_dispatch_info().weight; -// let weight_fee = LinearWeightToFee::::convert(weight); -// -// // we know that weight to fee multiplier is effect-less in block 1. -// assert_eq!(weight_fee as Balance, MILLICENTS); -// balance_alice -= weight_fee; -// -// balance_alice -= tip; -// balance_alice -= TransferFee::get(); -// -// assert_eq!(Balances::total_balance(&alice()), balance_alice); -// }); -// } -// -// #[test] -// #[should_panic] -// #[cfg(feature = "stress-test")] -// fn block_weight_capacity_report() { -// // Just report how many transfer calls you could fit into a block. The number should at least -// // be a few hundred (250 at the time of writing but can change over time). Runs until panic. -// use node_primitives::Index; -// -// // execution ext. -// let mut t = new_test_ext(COMPACT_CODE, false); -// // setup ext. -// let mut tt = new_test_ext(COMPACT_CODE, false); -// -// let factor = 50; -// let mut time = 10; -// let mut nonce: Index = 0; -// let mut block_number = 1; -// let mut previous_hash: Hash = GENESIS_HASH.into(); -// -// loop { -// let num_transfers = block_number * factor; -// let mut xts = (0..num_transfers).map(|i| CheckedExtrinsic { -// signed: Some((charlie(), signed_extra(nonce + i as Index, 0))), -// function: Call::Balances(pallet_balances::Call::transfer(bob().into(), 0)), -// }).collect::>(); -// -// xts.insert(0, CheckedExtrinsic { -// signed: None, -// function: Call::Timestamp(pallet_timestamp::Call::set(time * 1000)), -// }); -// -// // NOTE: this is super slow. Can probably be improved. -// let block = construct_block( -// &mut tt, -// block_number, -// previous_hash, -// xts -// ); -// -// let len = block.0.len(); -// print!( -// "++ Executing block with {} transfers. Block size = {} bytes / {} kb / {} mb", -// num_transfers, -// len, -// len / 1024, -// len / 1024 / 1024, -// ); -// -// let r = executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block.0, -// true, -// None, -// ).0; -// -// println!(" || Result = {:?}", r); -// assert!(r.is_ok()); -// -// previous_hash = block.1; -// nonce += num_transfers; -// time += 10; -// block_number += 1; -// } -// } -// -// #[test] -// #[should_panic] -// #[cfg(feature = "stress-test")] -// fn block_length_capacity_report() { -// // Just report how big a block can get. Executes until panic. Should be ignored unless if -// // manually inspected. The number should at least be a few megabytes (5 at the time of -// // writing but can change over time). -// use node_primitives::Index; -// -// // execution ext. -// let mut t = new_test_ext(COMPACT_CODE, false); -// // setup ext. -// let mut tt = new_test_ext(COMPACT_CODE, false); -// -// let factor = 256 * 1024; -// let mut time = 10; -// let mut nonce: Index = 0; -// let mut block_number = 1; -// let mut previous_hash: Hash = GENESIS_HASH.into(); -// -// loop { -// // NOTE: this is super slow. Can probably be improved. -// let block = construct_block( -// &mut tt, -// block_number, -// previous_hash, -// vec![ -// CheckedExtrinsic { -// signed: None, -// function: Call::Timestamp(pallet_timestamp::Call::set(time * 1000)), -// }, -// CheckedExtrinsic { -// signed: Some((charlie(), signed_extra(nonce, 0))), -// function: Call::System(frame_system::Call::remark(vec![0u8; (block_number * factor) as usize])), -// }, -// ] -// ); -// -// let len = block.0.len(); -// print!( -// "++ Executing block with big remark. Block size = {} bytes / {} kb / {} mb", -// len, -// len / 1024, -// len / 1024 / 1024, -// ); -// -// let r = executor_call:: _>( -// &mut t, -// "Core_execute_block", -// &block.0, -// true, -// None, -// ).0; -// -// println!(" || Result = {:?}", r); -// assert!(r.is_ok()); -// -// previous_hash = block.1; -// nonce += 1; -// time += 10; -// block_number += 1; -// } -// } -// } -// >>>>>>> master + From af1782e25da48be65389135d5d264546c416fa12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 20 Dec 2019 18:07:10 +0100 Subject: [PATCH 18/30] Bring back criterion. --- Cargo.lock | 1 + bin/node/executor/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 12dca324de826..1c7109cc97cb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3066,6 +3066,7 @@ dependencies = [ name = "node-executor" version = "2.0.0" dependencies = [ + "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "frame-support 2.0.0", "frame-system 2.0.0", "node-primitives 2.0.0", diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 65787d2986f9a..56a7489c0ea5f 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -17,6 +17,7 @@ node-primitives = { version = "2.0.0", path = "../primitives" } node-runtime = { version = "2.0.0", path = "../runtime" } [dev-dependencies] +criterion = "0.3.0" frame-support = { version = "2.0.0", path = "../../../frame/support" } frame-system = { version = "2.0.0", path = "../../../frame/system" } node-testing = { version = "2.0.0", path = "../testing" } From bacf48c0e529b47e27aede0a6ba0bf20aa8d00d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 23 Dec 2019 10:37:07 +0100 Subject: [PATCH 19/30] Remove stray debug log. --- frame/system/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 168f58683eaf5..903523fdf82c5 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1048,9 +1048,6 @@ impl SignedExtension for CheckEra { fn additional_signed(&self) -> Result { let current_u64 = >::block_number().saturated_into::(); let n = (self.0).0.birth(current_u64).saturated_into::(); - frame_support::debug::native::trace!( - "Looking for hash of {:?} (current: {:?})", n, current_u64 - ); if !>::exists(n) { Err(InvalidTransaction::AncientBirthBlock.into()) } else { From da816e2f3a0dca437c934d4e913214c231b84d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 6 Jan 2020 10:39:08 +0100 Subject: [PATCH 20/30] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Bastian Köcher --- bin/node/executor/tests/common.rs | 3 +-- bin/node/executor/tests/fees.rs | 2 +- bin/node/executor/tests/submit_transaction.rs | 4 ++-- frame/system/src/offchain.rs | 9 ++++----- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index 38c9fb2b1d9b5..d580e7c545047 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -152,4 +152,3 @@ pub fn construct_block( let hash = header.blake2_256(); (Block { header, extrinsics }.encode(), hash.into()) } - diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs index 06bdbc2eb28d5..1210812c0565a 100644 --- a/bin/node/executor/tests/fees.rs +++ b/bin/node/executor/tests/fees.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index f041eca7e5e4d..bc48bb5b7fa5b 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -110,7 +110,7 @@ fn should_submit_signed_twice_from_the_same_account() { assert_eq!(state.read().transactions.len(), 1); // submit another one from the same account. The nonce should be incremented. - let call = pallet_balances::Call::transfer(Default::default(), Default::default()); + let call = pallet_balances::Call::transfer(Default::default(), Default::default()); let results = >::submit_signed(call); diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index fcc355e695b70..eb6934a41a06f 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -57,7 +57,7 @@ pub trait CreateTransaction { /// so it's enough to pass an application-specific crypto type. /// /// To easily create `SignedTransaction`s have a look at the -/// [`TransactionSubmitter`](frame_system::offchain::TransactionSubmitter) type. +/// [`TransactionSubmitter`] type. pub trait Signer { /// Sign any encodable payload with given account and produce a signature. /// @@ -105,7 +105,7 @@ pub type PublicOf = < /// /// NOTE: Most likely you should not implement this trait yourself. /// There is an implementation for -/// [`TransactionSubmitter`](frame_system::offchain::TransactionSubmitter) type, which +/// [`TransactionSubmitter`] type, which /// you should use. pub trait SignAndSubmitTransaction { /// Unchecked extrinsic type. @@ -151,7 +151,7 @@ pub trait SignAndSubmitTransaction { /// /// NOTE: Most likely you should not implement this trait yourself. /// There is an implementation for -/// [`TransactionSubmitter`](frame_system::offchain::TransactionSubmitter) type, which +/// [`TransactionSubmitter`] type, which /// you should use. pub trait SubmitUnsignedTransaction { /// Unchecked extrinsic type. @@ -172,7 +172,7 @@ pub trait SubmitUnsignedTransaction { /// /// NOTE: Most likely you should not implement this trait yourself. /// There is an implementation for -/// [`TransactionSubmitter`](frame_system::offchain::TransactionSubmitter) type, which +/// [`TransactionSubmitter`] type, which /// you should use. pub trait SubmitSignedTransaction { /// A `SignAndSubmitTransaction` implementation. @@ -362,4 +362,3 @@ impl SubmitSignedTransaction for TransactionSubmitter } } } - From a2c6d0933431165f206f116efb8a4214b911324b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 8 Jan 2020 13:41:35 +0100 Subject: [PATCH 21/30] Make sure to initialize block correctly. --- frame/executive/src/lib.rs | 19 +++++++++++++++- frame/system/src/lib.rs | 46 +++++++++++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index fd05c410d9f70..30987ed697032 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -165,7 +165,13 @@ where extrinsics_root: &System::Hash, digest: &Digest, ) { - >::initialize(block_number, parent_hash, extrinsics_root, digest); + >::initialize( + block_number, + parent_hash, + extrinsics_root, + digest, + frame_system::InitKind::Full, + ); >::on_initialize(*block_number); >::register_extra_weight_unchecked( >::on_initialize(*block_number) @@ -311,6 +317,17 @@ where /// Start an offchain worker and generate extrinsics. pub fn offchain_worker(n: System::BlockNumber) { + let parent_hash = Default::default(); + let txs_root = Default::default(); + let digest = Default::default(); + + >::initialize( + &n, + &parent_hash, + &txs_root, + &digest, + frame_system::InitKind::Introspection, + ); >::offchain_worker(n) } } diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 903523fdf82c5..e1761408d27f3 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -535,6 +535,21 @@ pub fn ensure_none(o: OuterOrigin) -> Result<(), BadOrig } } +/// A type of block initialization to perform. +pub enum InitKind { + /// Leave inspectable storage entries in state. + /// + /// i.e. `Events` are not being reset. + /// Should only be used for off-chain calls, + /// regular block execution should clear those. + Inspection, + + /// Reset also inspectable storage entries. + /// + /// This should be used for regular block execution. + Full, +} + impl Module { /// Deposits an event into this block's event record. pub fn deposit_event(event: impl Into) { @@ -632,11 +647,15 @@ impl Module { } /// Start the execution of a particular block. + /// + /// Setting `is_for_inspection` flag will not cause the events + /// to be removed from the state. pub fn initialize( number: &T::BlockNumber, parent_hash: &T::Hash, txs_root: &T::Hash, digest: &DigestOf, + kind: InitKind, ) { // populate environment storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &0u32); @@ -645,9 +664,12 @@ impl Module { >::put(parent_hash); >::insert(*number - One::one(), parent_hash); >::put(txs_root); - >::kill(); - EventCount::kill(); - >::remove_prefix(&()); + + if !is_for_inspection { + >::kill(); + EventCount::kill(); + >::remove_prefix(&()); + } } /// Remove temporary "environment" entries in storage. @@ -1217,7 +1239,13 @@ mod tests { #[test] fn deposit_event_should_work() { new_test_ext().execute_with(|| { - System::initialize(&1, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + System::initialize( + &1, + &[0u8; 32].into(), + &[0u8; 32].into(), + &Default::default(), + InitKind::Full, + ); System::note_finished_extrinsics(); System::deposit_event(1u16); System::finalize(); @@ -1232,7 +1260,13 @@ mod tests { ] ); - System::initialize(&2, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + System::initialize( + &2, + &[0u8; 32].into(), + &[0u8; 32].into(), + &Default::default(), + InitKind::Full, + ); System::deposit_event(42u16); System::note_applied_extrinsic(&Ok(()), 0, Default::default()); System::note_applied_extrinsic(&Err(DispatchError::BadOrigin), 0, Default::default()); @@ -1261,6 +1295,7 @@ mod tests { &[0u8; 32].into(), &[0u8; 32].into(), &Default::default(), + InitKind::Full, ); System::note_finished_extrinsics(); @@ -1326,6 +1361,7 @@ mod tests { &[n as u8 - 1; 32].into(), &[0u8; 32].into(), &Default::default(), + InitKind::Full, ); System::finalize(); From af5724c4545850c5e5ec5094a93be133a45c9939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 8 Jan 2020 13:46:57 +0100 Subject: [PATCH 22/30] Initialize block for offchain workers. --- frame/executive/src/lib.rs | 2 +- frame/system/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 30987ed697032..0c4806c008912 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -326,7 +326,7 @@ where &parent_hash, &txs_root, &digest, - frame_system::InitKind::Introspection, + frame_system::InitKind::Inspection, ); >::offchain_worker(n) } diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index e1761408d27f3..ac890a1c640d4 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -665,7 +665,7 @@ impl Module { >::insert(*number - One::one(), parent_hash); >::put(txs_root); - if !is_for_inspection { + if let InitKind::Full = kind { >::kill(); EventCount::kill(); >::remove_prefix(&()); From 3eda535406938414269910d1d85ab97b6d7b10da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 8 Jan 2020 14:54:27 +0100 Subject: [PATCH 23/30] Add test for transaction validity. --- bin/node/executor/Cargo.toml | 10 ++-- bin/node/executor/tests/submit_transaction.rs | 51 ++++++++++++++++++- primitives/runtime/src/traits.rs | 6 +-- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 56a7489c0ea5f..0c594a95f9f53 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -6,15 +6,15 @@ description = "Substrate node implementation in Rust." edition = "2018" [dependencies] -trie-root = "0.15.2" codec = { package = "parity-scale-codec", version = "1.0.0" } -sp-io = { version = "2.0.0", path = "../../../primitives/io" } -sp-state-machine = { version = "2.0.0", path = "../../../primitives/state-machine" } +node-primitives = { version = "2.0.0", path = "../primitives" } +node-runtime = { version = "2.0.0", path = "../runtime" } sc-executor = { version = "2.0.0", path = "../../../client/executor" } sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-io = { version = "2.0.0", path = "../../../primitives/io" } +sp-state-machine = { version = "2.0.0", path = "../../../primitives/state-machine" } sp-trie = { version = "2.0.0", path = "../../../primitives/trie" } -node-primitives = { version = "2.0.0", path = "../primitives" } -node-runtime = { version = "2.0.0", path = "../runtime" } +trie-root = "0.15.2" [dev-dependencies] criterion = "0.3.0" diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index bc48bb5b7fa5b..b8fa389b8693d 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -15,7 +15,7 @@ // along with Substrate. If not, see . use node_runtime::{ - Call, Runtime, SubmitTransaction, UncheckedExtrinsic, + Call, Executive, Indices, Balances, Runtime, SubmitTransaction, UncheckedExtrinsic, }; use sp_application_crypto::AppKey; use sp_core::testing::KeyStore; @@ -133,3 +133,52 @@ fn should_submit_signed_twice_from_the_same_account() { ); }); } + +#[test] +fn submitted_transaction_should_be_valid() { + use codec::Encode; + use frame_support::storage::StorageMap; + use sp_runtime::transaction_validity::ValidTransaction; + use sp_runtime::traits::{Dispatchable, StaticLookup}; + + let mut t = new_test_ext(COMPACT_CODE, false); + let (pool, state) = TestTransactionPoolExt::new(); + t.register_extension(TransactionPoolExt::new(pool)); + + let keystore = KeyStore::new(); + keystore.write().sr25519_generate_new(Key::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + t.register_extension(KeystoreExt(keystore)); + + t.execute_with(|| { + let call = pallet_balances::Call::transfer(Default::default(), Default::default()); + let results = + >::submit_signed(call); + let len = results.len(); + assert_eq!(len, 1); + assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len); + }); + + // check that transaction is valid, but reset environment storage, + // since CreateTransaction increments the nonce + let tx0 = state.read().transactions[0].clone(); + let mut t = new_test_ext(COMPACT_CODE, false); + t.execute_with(|| { + let extrinsic = UncheckedExtrinsic::decode(&mut &*tx0).unwrap(); + // add balance to the account + let author = extrinsic.signature.clone().unwrap().0; + let address = Indices::lookup(author).unwrap(); + >::insert(&address, 5_000_000_000_000); + + // check validity + let res = Executive::validate_transaction(extrinsic); + + assert_eq!(res.unwrap(), ValidTransaction { + priority: 2_411_002_000_000, + requires: vec![], + provides: vec![(address, 0).encode()], + longevity: 127, + propagate: true, + }); + }); +} + diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index a0970accd8740..4b7d239fba4f6 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -23,7 +23,7 @@ use sp_io; use std::fmt::Display; #[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned}; -use sp_core::{self, Hasher, Blake2Hasher, TypeId}; +use sp_core::{self, Hasher, Blake2Hasher, TypeId, RuntimeDebug}; use crate::codec::{Codec, Encode, Decode}; use crate::transaction_validity::{ ValidTransaction, TransactionValidity, TransactionValidityError, UnknownTransaction, @@ -157,7 +157,7 @@ pub trait EnsureOrigin { } /// An error that indicates that a lookup failed. -#[derive(Encode, Decode)] +#[derive(Encode, Decode, RuntimeDebug)] pub struct LookupError; impl From for &'static str { @@ -391,7 +391,7 @@ pub trait Hash: 'static + MaybeSerializeDeserialize + Debug + Clone + Eq + Parti } /// Blake2-256 Hash implementation. -#[derive(PartialEq, Eq, Clone, sp_core::RuntimeDebug)] +#[derive(PartialEq, Eq, Clone, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct BlakeTwo256; From 6bafaff285f94c4794994a028d64bc577d6e80e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 8 Jan 2020 16:01:49 +0100 Subject: [PATCH 24/30] Fix tests. --- bin/node/runtime/src/lib.rs | 6 ++- frame/authorship/src/lib.rs | 20 +++++++-- frame/babe/src/tests.rs | 8 +++- frame/contracts/src/tests.rs | 36 ++++++++++------ frame/executive/src/lib.rs | 5 ++- frame/finality-tracker/src/lib.rs | 16 ++++++- frame/grandpa/src/tests.rs | 48 +++++++++++++-------- frame/randomness-collective-flip/src/lib.rs | 8 +++- frame/system/src/lib.rs | 6 +++ 9 files changed, 111 insertions(+), 42 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index d82ad8d90677d..2e761bcacab8c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -22,7 +22,7 @@ use sp_std::prelude::*; use frame_support::{ - construct_runtime, parameter_types, + construct_runtime, parameter_types, debug, weights::Weight, traits::{SplitTwoWays, Currency, Randomness}, }; @@ -520,7 +520,9 @@ impl frame_system::offchain::CreateTransaction for pallet_transaction_payment::ChargeTransactionPayment::::from(tip), Default::default(), ); - let raw_payload = SignedPayload::new(call, extra).ok()?; + let raw_payload = SignedPayload::new(call, extra).map_err(|e| { + debug::warn!("Unable to create signed payload: {:?}", e); + }).ok()?; let signature = TSigner::sign(public, &raw_payload)?; let address = Indices::unlookup(account); let (call, extra, _) = raw_payload.deconstruct(); diff --git a/frame/authorship/src/lib.rs b/frame/authorship/src/lib.rs index 1c25b81790aa1..216f5c729a9c4 100644 --- a/frame/authorship/src/lib.rs +++ b/frame/authorship/src/lib.rs @@ -571,14 +571,22 @@ mod tests { inner: vec![seal_header(create_header(0, Default::default(), Default::default()), 999)], }; + let initialize_block = |number, hash: H256| System::initialize( + &number, + &hash, + &Default::default(), + &Default::default(), + Default::default() + ); + for number in 1..8 { - System::initialize(&number, &canon_chain.best_hash(), &Default::default(), &Default::default()); + initialize_block(number, canon_chain.best_hash()); let header = seal_header(System::finalize(), author_a); canon_chain.push(header); } // initialize so system context is set up correctly. - System::initialize(&8, &canon_chain.best_hash(), &Default::default(), &Default::default()); + initialize_block(8, canon_chain.best_hash()); // 2 of the same uncle at once { @@ -663,7 +671,13 @@ mod tests { ); header.digest_mut().pop(); // pop the seal off. - System::initialize(&1, &Default::default(), &Default::default(), header.digest()); + System::initialize( + &1, + &Default::default(), + &Default::default(), + header.digest(), + Default::default(), + ); assert_eq!(Authorship::author(), author); }); diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 4e47014a8a54e..dbd61238166b0 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -81,7 +81,13 @@ fn first_block_epoch_zero_start() { ); assert_eq!(Babe::genesis_slot(), 0); - System::initialize(&1, &Default::default(), &Default::default(), &pre_digest); + System::initialize( + &1, + &Default::default(), + &Default::default(), + &pre_digest, + Default::default(), + ); // see implementation of the function for details why: we issue an // epoch-change digest but don't do it via the normal session mechanism. diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index ca158980099c9..f6e712577bece 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -860,6 +860,16 @@ fn storage_size() { }); } +fn initialize_block(number: u64) { + System::initialize( + &number, + &[0u8; 32].into(), + &[0u8; 32].into(), + &Default::default(), + Default::default(), + ); +} + #[test] fn deduct_blocks() { let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); @@ -880,7 +890,7 @@ fn deduct_blocks() { assert_eq!(bob_contract.rent_allowance, 1_000); // Advance 4 blocks - System::initialize(&5, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(5); // Trigger rent through call assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); @@ -895,7 +905,7 @@ fn deduct_blocks() { assert_eq!(Balances::free_balance(BOB), 30_000 - rent); // Advance 7 blocks more - System::initialize(&12, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(12); // Trigger rent through call assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); @@ -970,7 +980,7 @@ fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool) )); // Advance blocks - System::initialize(&blocks, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(blocks); // Trigger rent through call assert!(trigger_call()); @@ -1010,7 +1020,7 @@ fn removals(trigger_call: impl Fn() -> bool) { assert_eq!(Balances::free_balance(&BOB), 100); // Advance blocks - System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(10); // Trigger rent through call assert!(trigger_call()); @@ -1018,7 +1028,7 @@ fn removals(trigger_call: impl Fn() -> bool) { assert_eq!(Balances::free_balance(&BOB), subsistence_threshold); // Advance blocks - System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(20); // Trigger rent must have no effect assert!(trigger_call()); @@ -1044,7 +1054,7 @@ fn removals(trigger_call: impl Fn() -> bool) { assert_eq!(Balances::free_balance(&BOB), 1_000); // Advance blocks - System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(10); // Trigger rent through call assert!(trigger_call()); @@ -1053,7 +1063,7 @@ fn removals(trigger_call: impl Fn() -> bool) { assert_eq!(Balances::free_balance(&BOB), 900); // Advance blocks - System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(20); // Trigger rent must have no effect assert!(trigger_call()); @@ -1084,7 +1094,7 @@ fn removals(trigger_call: impl Fn() -> bool) { assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); // Advance blocks - System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(10); // Trigger rent through call assert!(trigger_call()); @@ -1092,7 +1102,7 @@ fn removals(trigger_call: impl Fn() -> bool) { assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); // Advance blocks - System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(20); // Trigger rent must have no effect assert!(trigger_call()); @@ -1121,7 +1131,7 @@ fn call_removed_contract() { assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); // Advance blocks - System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(10); // Calling contract should remove contract and fail. assert_err!( @@ -1208,7 +1218,7 @@ fn default_rent_allowance_on_instantiate() { assert_eq!(bob_contract.rent_allowance, >::max_value()); // Advance blocks - System::initialize(&5, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(5); // Trigger rent through call assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); @@ -1354,7 +1364,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: } // Advance 4 blocks, to the 5th. - System::initialize(&5, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(5); // Call `BOB`, which makes it pay rent. Since the rent allowance is set to 0 // we expect that it will get removed leaving tombstone. @@ -1383,7 +1393,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: if !test_restore_to_with_dirty_storage { // Advance 1 block, to the 6th. - System::initialize(&6, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + initialize_block(6); } // Perform a call to `DJANGO`. This should either perform restoration successfully or diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index cb5b1e03ea3cc..cecc2911bae21 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -317,12 +317,15 @@ where /// Start an offchain worker and generate extrinsics. pub fn offchain_worker(n: System::BlockNumber) { + // We need to keep events available for offchain workers, + // hence we initialize the block manually. + // OffchainWorker RuntimeApi should skip initialization. let parent_hash = Default::default(); let txs_root = Default::default(); let digest = Default::default(); >::initialize( - &n, + &(n + 1.into()), &parent_hash, &txs_root, &digest, diff --git a/frame/finality-tracker/src/lib.rs b/frame/finality-tracker/src/lib.rs index 1489fcd0ddfb7..c9c9fbb0b5743 100644 --- a/frame/finality-tracker/src/lib.rs +++ b/frame/finality-tracker/src/lib.rs @@ -291,7 +291,13 @@ mod tests { TestExternalities::new(t).execute_with(|| { let mut parent_hash = System::parent_hash(); for i in 2..106 { - System::initialize(&i, &parent_hash, &Default::default(), &Default::default()); + System::initialize( + &i, + &parent_hash, + &Default::default(), + &Default::default(), + Default::default() + ); FinalityTracker::on_finalize(i); let hdr = System::finalize(); parent_hash = hdr.hash(); @@ -310,7 +316,13 @@ mod tests { TestExternalities::new(t).execute_with(|| { let mut parent_hash = System::parent_hash(); for i in 2..106 { - System::initialize(&i, &parent_hash, &Default::default(), &Default::default()); + System::initialize( + &i, + &parent_hash, + &Default::default(), + &Default::default(), + Default::default(), + ); assert_ok!(FinalityTracker::dispatch( Call::final_hint(i-1), Origin::NONE, diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index c6728c70c8fb4..ff3841b8d4593 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -18,17 +18,27 @@ #![cfg(test)] -use sp_runtime::{testing::Digest, traits::{Header, OnFinalize}}; +use sp_runtime::{testing::{H256, Digest}, traits::{Header, OnFinalize}}; use crate::mock::*; use frame_system::{EventRecord, Phase}; use codec::{Decode, Encode}; use fg_primitives::ScheduledChange; use super::*; +fn initialize_block(number: u64, parent_hash: H256) { + System::initialize( + &number, + &parent_hash, + &Default::default(), + &Default::default(), + Default::default(), + ); +} + #[test] fn authorities_change_logged() { new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { - System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); + initialize_block(1, Default::default()); Grandpa::schedule_change(to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 0, None).unwrap(); System::note_finished_extrinsics(); @@ -56,7 +66,7 @@ fn authorities_change_logged() { #[test] fn authorities_change_logged_after_delay() { new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { - System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); + initialize_block(1, Default::default()); Grandpa::schedule_change(to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 1, None).unwrap(); Grandpa::on_finalize(1); let header = System::finalize(); @@ -71,7 +81,7 @@ fn authorities_change_logged_after_delay() { // no change at this height. assert_eq!(System::events(), vec![]); - System::initialize(&2, &header.hash(), &Default::default(), &Default::default()); + initialize_block(2, header.hash()); System::note_finished_extrinsics(); Grandpa::on_finalize(2); @@ -89,7 +99,7 @@ fn authorities_change_logged_after_delay() { #[test] fn cannot_schedule_change_when_one_pending() { new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { - System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); + initialize_block(1, Default::default()); Grandpa::schedule_change(to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 1, None).unwrap(); assert!(>::exists()); assert!(Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None).is_err()); @@ -97,14 +107,14 @@ fn cannot_schedule_change_when_one_pending() { Grandpa::on_finalize(1); let header = System::finalize(); - System::initialize(&2, &header.hash(), &Default::default(), &Default::default()); + initialize_block(2, header.hash()); assert!(>::exists()); assert!(Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None).is_err()); Grandpa::on_finalize(2); let header = System::finalize(); - System::initialize(&3, &header.hash(), &Default::default(), &Default::default()); + initialize_block(3, header.hash()); assert!(!>::exists()); assert!(Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None).is_ok()); @@ -132,7 +142,7 @@ fn new_decodes_from_old() { #[test] fn dispatch_forced_change() { new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { - System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); + initialize_block(1, Default::default()); Grandpa::schedule_change( to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 5, @@ -146,7 +156,7 @@ fn dispatch_forced_change() { let mut header = System::finalize(); for i in 2..7 { - System::initialize(&i, &header.hash(), &Default::default(), &Default::default()); + initialize_block(i, header.hash()); assert!(>::get().unwrap().forced.is_some()); assert_eq!(Grandpa::next_forced(), Some(11)); assert!(Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None).is_err()); @@ -159,7 +169,7 @@ fn dispatch_forced_change() { // change has been applied at the end of block 6. // add a normal change. { - System::initialize(&7, &header.hash(), &Default::default(), &Default::default()); + initialize_block(7, header.hash()); assert!(!>::exists()); assert_eq!(Grandpa::grandpa_authorities(), to_authorities(vec![(4, 1), (5, 1), (6, 1)])); assert!(Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None).is_ok()); @@ -169,7 +179,7 @@ fn dispatch_forced_change() { // run the normal change. { - System::initialize(&8, &header.hash(), &Default::default(), &Default::default()); + initialize_block(8, header.hash()); assert!(>::exists()); assert_eq!(Grandpa::grandpa_authorities(), to_authorities(vec![(4, 1), (5, 1), (6, 1)])); assert!(Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None).is_err()); @@ -180,7 +190,7 @@ fn dispatch_forced_change() { // normal change applied. but we can't apply a new forced change for some // time. for i in 9..11 { - System::initialize(&i, &header.hash(), &Default::default(), &Default::default()); + initialize_block(i, header.hash()); assert!(!>::exists()); assert_eq!(Grandpa::grandpa_authorities(), to_authorities(vec![(5, 1)])); assert_eq!(Grandpa::next_forced(), Some(11)); @@ -190,7 +200,7 @@ fn dispatch_forced_change() { } { - System::initialize(&11, &header.hash(), &Default::default(), &Default::default()); + initialize_block(11, header.hash()); assert!(!>::exists()); assert!(Grandpa::schedule_change(to_authorities(vec![(5, 1), (6, 1), (7, 1)]), 5, Some(0)).is_ok()); assert_eq!(Grandpa::next_forced(), Some(21)); @@ -205,7 +215,7 @@ fn dispatch_forced_change() { fn schedule_pause_only_when_live() { new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { // we schedule a pause at block 1 with delay of 1 - System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); + initialize_block(1, Default::default()); Grandpa::schedule_pause(1).unwrap(); // we've switched to the pending pause state @@ -220,7 +230,7 @@ fn schedule_pause_only_when_live() { Grandpa::on_finalize(1); let _ = System::finalize(); - System::initialize(&2, &Default::default(), &Default::default(), &Default::default()); + initialize_block(2, Default::default()); // signaling a pause now should fail assert!(Grandpa::schedule_pause(1).is_err()); @@ -239,7 +249,7 @@ fn schedule_pause_only_when_live() { #[test] fn schedule_resume_only_when_paused() { new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { - System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); + initialize_block(1, Default::default()); // the set is currently live, resuming it is an error assert!(Grandpa::schedule_resume(1).is_err()); @@ -260,16 +270,16 @@ fn schedule_resume_only_when_paused() { ); // we schedule the set to go back live in 2 blocks - System::initialize(&2, &Default::default(), &Default::default(), &Default::default()); + initialize_block(2, Default::default()); Grandpa::schedule_resume(2).unwrap(); Grandpa::on_finalize(2); let _ = System::finalize(); - System::initialize(&3, &Default::default(), &Default::default(), &Default::default()); + initialize_block(3, Default::default()); Grandpa::on_finalize(3); let _ = System::finalize(); - System::initialize(&4, &Default::default(), &Default::default(), &Default::default()); + initialize_block(4, Default::default()); Grandpa::on_finalize(4); let _ = System::finalize(); diff --git a/frame/randomness-collective-flip/src/lib.rs b/frame/randomness-collective-flip/src/lib.rs index 69c1680659387..c7f7bb0db78b8 100644 --- a/frame/randomness-collective-flip/src/lib.rs +++ b/frame/randomness-collective-flip/src/lib.rs @@ -210,7 +210,13 @@ mod tests { let mut parent_hash = System::parent_hash(); for i in 1 .. (blocks + 1) { - System::initialize(&i, &parent_hash, &Default::default(), &Default::default()); + System::initialize( + &i, + &parent_hash, + &Default::default(), + &Default::default(), + frame_system::InitKind::Full, + ); CollectiveFlip::on_initialize(i); let header = System::finalize(); diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 7cc60253b40d6..4bb4e1dfc483b 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -546,6 +546,12 @@ pub enum InitKind { Full, } +impl Default for InitKind { + fn default() -> Self { + InitKind::Full + } +} + impl Module { /// Deposits an event into this block's event record. pub fn deposit_event(event: impl Into) { From 021f586af1a4b4b8b763810a100f1a78a8e725a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 8 Jan 2020 16:03:56 +0100 Subject: [PATCH 25/30] Review suggestions. --- bin/node/executor/tests/basic.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index ba7b229dca2a3..504b6853e1df2 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -46,7 +46,6 @@ use node_testing::keyring::*; mod common; use self::common::{*, sign}; - /// The wasm runtime binary which hasn't undergone the compacting process. /// /// The idea here is to pass it as the current runtime code to the executor so the executor will From 3ac00ffd1080969d1a940ec4cdb5342224e54298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 8 Jan 2020 16:37:59 +0100 Subject: [PATCH 26/30] Remove redundant comment. --- frame/system/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 4bb4e1dfc483b..4541e973c11b4 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -649,9 +649,6 @@ impl Module { } /// Start the execution of a particular block. - /// - /// Setting `is_for_inspection` flag will not cause the events - /// to be removed from the state. pub fn initialize( number: &T::BlockNumber, parent_hash: &T::Hash, From 7f38d4c04b389459b70db697adc21da70a87d15e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 8 Jan 2020 16:44:15 +0100 Subject: [PATCH 27/30] Make sure to use correct block number of authoring. --- bin/node/runtime/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 2e761bcacab8c..519a3c5e188d5 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -508,8 +508,10 @@ impl frame_system::offchain::CreateTransaction for .unwrap_or(2) as u64; let current_block = System::block_number() .saturated_into::() - // make sure we actually construct on top of the parent block. - .saturating_sub(1); + // The `System::block_number` is initialized with `n+1`, so the actual block number is + // `n`, but in offchain worker context this block does not have correct `parent_hash` set, + // so we need to fall back to `n-1`. + .saturating_sub(2); let tip = 0; let extra: SignedExtra = ( frame_system::CheckVersion::::new(), From afb99f9b528025d5a13cf16639c9088b6098fb5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 9 Jan 2020 13:37:59 +0100 Subject: [PATCH 28/30] Change the runtime API. --- bin/node-template/runtime/src/lib.rs | 6 ++-- bin/node/runtime/src/lib.rs | 6 ++-- client/offchain/src/lib.rs | 23 +++++++++----- client/service/src/builder.rs | 10 +++--- frame/executive/Cargo.toml | 16 +++++----- frame/executive/src/lib.rs | 42 +++++++++++++++++-------- primitives/offchain/src/lib.rs | 6 ++++ primitives/transaction-pool/src/pool.rs | 24 +++++++++++--- test-utils/runtime/src/lib.rs | 8 ++--- 9 files changed, 95 insertions(+), 46 deletions(-) diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 8580bfd895102..1643c10af257f 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -15,7 +15,7 @@ use sp_runtime::{ impl_opaque_keys, MultiSignature }; use sp_runtime::traits::{ - NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify, ConvertInto, IdentifyAccount + BlakeTwo256, Block as BlockT, StaticLookup, Verify, ConvertInto, IdentifyAccount }; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; @@ -339,8 +339,8 @@ impl_runtime_apis! { } impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(number: NumberFor) { - Executive::offchain_worker(number) + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) } } diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 519a3c5e188d5..dfa18556b9c92 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -33,7 +33,7 @@ use sp_runtime::{Permill, Perbill, ApplyExtrinsicResult, impl_opaque_keys, gener use sp_runtime::curve::PiecewiseLinear; use sp_runtime::transaction_validity::TransactionValidity; use sp_runtime::traits::{ - self, BlakeTwo256, Block as BlockT, NumberFor, StaticLookup, SaturatedConversion, + self, BlakeTwo256, Block as BlockT, StaticLookup, SaturatedConversion, OpaqueKeys, }; use sp_version::RuntimeVersion; @@ -645,8 +645,8 @@ impl_runtime_apis! { } impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(number: NumberFor) { - Executive::offchain_worker(number) + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) } } diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index 2beb8fe6c9332..63d0b678daf00 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -42,7 +42,7 @@ use futures::future::Future; use log::{debug, warn}; use sc_network::NetworkStateInfo; use sp_core::{offchain::{self, OffchainStorage}, ExecutionContext}; -use sp_runtime::{generic::BlockId, traits::{self, ProvideRuntimeApi}}; +use sp_runtime::{generic::BlockId, traits::{self, ProvideRuntimeApi, Header}}; mod api; @@ -92,12 +92,13 @@ impl OffchainWorkers< #[must_use] pub fn on_block_imported( &self, - number: &::Number, + header: &Block::Header, network_state: Arc, is_validator: bool, ) -> impl Future { let runtime = self.client.runtime_api(); - let at = BlockId::number(*number); + let at = BlockId::number(*header.number()); + // TODO [ToDr] Use has_api_with to support old versions too let has_api = runtime.has_api::>(&at); debug!("Checking offchain workers at {:?}: {:?}", at, has_api); @@ -108,7 +109,7 @@ impl OffchainWorkers< is_validator, ); debug!("Spawning offchain workers at {:?}", at); - let number = *number; + let header = header.clone(); let client = self.client.clone(); self.spawn_worker(move || { let runtime = client.runtime_api(); @@ -117,7 +118,7 @@ impl OffchainWorkers< let run = runtime.offchain_worker_with_context( &at, ExecutionContext::OffchainCall(Some((api, offchain::Capabilities::all()))), - number, + &header, ); if let Err(e) = run { log::error!("Error running offchain workers at {:?}: {:?}", at, e); @@ -150,6 +151,7 @@ mod tests { use substrate_test_runtime_client::runtime::Block; use sc_transaction_pool::{BasicPool, FullChainApi}; use sp_transaction_pool::{TransactionPool, InPoolTransaction}; + use sp_runtime::{generic::Header, traits::Header as _}; struct MockNetworkStateInfo(); @@ -169,7 +171,7 @@ mod tests { fn submit_at( &self, at: &BlockId, - extrinsic: ::Extrinsic, + extrinsic: ::Extrinsic, ) -> Result<(), ()> { futures::executor::block_on(self.0.submit_one(&at, extrinsic)) .map(|_| ()) @@ -187,10 +189,17 @@ mod tests { .register_transaction_pool(Arc::downgrade(&pool.clone()) as _); let db = sc_client_db::offchain::LocalStorage::new_test(); let network_state = Arc::new(MockNetworkStateInfo()); + let header = Header::new( + 0u64, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ); // when let offchain = OffchainWorkers::new(client, db); - futures::executor::block_on(offchain.on_block_imported(&0u64, network_state, false)); + futures::executor::block_on(offchain.on_block_imported(&header, network_state, false)); // then assert_eq!(pool.0.status().ready, 1); diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 8a4d760acc712..1608a51febbfd 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -43,7 +43,7 @@ use sc_rpc; use sp_api::ConstructRuntimeApi; use sp_runtime::generic::BlockId; use sp_runtime::traits::{ - Block as BlockT, ProvideRuntimeApi, NumberFor, Header, SaturatedConversion, + Block as BlockT, ProvideRuntimeApi, NumberFor, SaturatedConversion, }; use sc_executor::{NativeExecutor, NativeExecutionDispatch}; use std::{ @@ -867,7 +867,6 @@ ServiceBuilder< let events = client.import_notification_stream() .map(|v| Ok::<_, ()>(v)).compat() .for_each(move |notification| { - let number = *notification.header.number(); let txpool = txpool.upgrade(); if let Some(txpool) = txpool.as_ref() { @@ -880,8 +879,11 @@ ServiceBuilder< let offchain = offchain.as_ref().and_then(|o| o.upgrade()); if let Some(offchain) = offchain { - let future = offchain.on_block_imported(&number, network_state_info.clone(), is_validator) - .map(|()| Ok(())); + let future = offchain.on_block_imported( + ¬ification.header, + network_state_info.clone(), + is_validator + ).map(|()| Ok(())); let _ = to_spawn_tx_.unbounded_send(Box::new(Compat::new(future))); } diff --git a/frame/executive/Cargo.toml b/frame/executive/Cargo.toml index b58d4a6790730..b2b30edd1cc58 100644 --- a/frame/executive/Cargo.toml +++ b/frame/executive/Cargo.toml @@ -5,13 +5,13 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } -sp-io ={ path = "../../primitives/io", default-features = false } -sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } frame-support = { version = "2.0.0", default-features = false, path = "../support" } frame-system = { version = "2.0.0", default-features = false, path = "../system" } +serde = { version = "1.0.101", optional = true } +sp-io ={ path = "../../primitives/io", default-features = false } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } [dev-dependencies] hex-literal = "0.2.1" @@ -23,11 +23,11 @@ pallet-transaction-payment = { version = "2.0.0", path = "../transaction-payment [features] default = ["std"] std = [ - "sp-std/std", + "codec/std", "frame-support/std", + "frame-system/std", "serde", - "codec/std", - "sp-runtime/std", "sp-io/std", - "frame-system/std", + "sp-runtime/std", + "sp-std/std", ] diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index cecc2911bae21..005178a33c57e 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -82,7 +82,7 @@ use sp_runtime::{ generic::Digest, ApplyExtrinsicResult, traits::{ self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize, OnInitialize, - NumberFor, Block as BlockT, OffchainWorker, Dispatchable, + NumberFor, Block as BlockT, OffchainWorker, Dispatchable, Saturating, }, transaction_validity::TransactionValidity, }; @@ -154,9 +154,23 @@ where { /// Start the execution of a particular block. pub fn initialize_block(header: &System::Header) { - let mut digests = >::default(); - header.digest().logs().iter().for_each(|d| if d.as_pre_runtime().is_some() { digests.push(d.clone()) }); - Self::initialize_block_impl(header.number(), header.parent_hash(), header.extrinsics_root(), &digests); + let digests = Self::extract_pre_digest(&header); + Self::initialize_block_impl( + header.number(), + header.parent_hash(), + header.extrinsics_root(), + &digests + ); + } + + fn extract_pre_digest(header: &System::Header) -> DigestOf { + let mut digest = >::default(); + header.digest().logs() + .iter() + .for_each(|d| if d.as_pre_runtime().is_some() { + digest.push(d.clone()) + }); + digest } fn initialize_block_impl( @@ -316,22 +330,24 @@ where } /// Start an offchain worker and generate extrinsics. - pub fn offchain_worker(n: System::BlockNumber) { + pub fn offchain_worker(header: &System::Header) { // We need to keep events available for offchain workers, // hence we initialize the block manually. // OffchainWorker RuntimeApi should skip initialization. - let parent_hash = Default::default(); - let txs_root = Default::default(); - let digest = Default::default(); + let digests = Self::extract_pre_digest(header); >::initialize( - &(n + 1.into()), - &parent_hash, - &txs_root, - &digest, + header.number(), + header.parent_hash(), + header.extrinsics_root(), + &digests, frame_system::InitKind::Inspection, ); - >::offchain_worker(n) + >::offchain_worker( + // to maintain backward compatibility we call module offchain workers + // with parent block number. + header.number().saturating_sub(1.into()) + ) } } diff --git a/primitives/offchain/src/lib.rs b/primitives/offchain/src/lib.rs index d975e99a788bc..1dd20db0dd846 100644 --- a/primitives/offchain/src/lib.rs +++ b/primitives/offchain/src/lib.rs @@ -26,9 +26,15 @@ pub const STORAGE_PREFIX: &[u8] = b"storage"; sp_api::decl_runtime_apis! { /// The offchain worker api. + #[api_version(2)] pub trait OffchainWorkerApi { /// Starts the off-chain task for given block number. #[skip_initialize_block] + #[changed_in(2)] fn offchain_worker(number: NumberFor); + + /// Starts the off-chain task for given block header. + #[skip_initialize_block] + fn offchain_worker(header: &Block::Header); } } diff --git a/primitives/transaction-pool/src/pool.rs b/primitives/transaction-pool/src/pool.rs index 6e68574dde75e..e67a9890755d7 100644 --- a/primitives/transaction-pool/src/pool.rs +++ b/primitives/transaction-pool/src/pool.rs @@ -163,6 +163,8 @@ pub trait TransactionPool: Send + Sync { /// Error type. type Error: From + crate::error::IntoPoolError; + // Networking + /// Returns a future that imports a bunch of unverified transactions to the pool. fn submit_at( &self, @@ -183,6 +185,8 @@ pub trait TransactionPool: Send + Sync { Self::Error >> + Send + Unpin>; + // RPC + /// Returns a future that import a single transaction and starts to watch their progress in the pool. fn submit_and_watch( &self, @@ -190,23 +194,35 @@ pub trait TransactionPool: Send + Sync { xt: TransactionFor, ) -> Box>, Self::Error>> + Send + Unpin>; + + // Block production / Networking + + /// Get an iterator for ready transactions ordered by priority + fn ready(&self) -> Box>>; + + + // Block production + /// Remove transactions identified by given hashes (and dependent transactions) from the pool. fn remove_invalid(&self, hashes: &[TxHash]) -> Vec>; + // logging + /// Returns pool status. fn status(&self) -> PoolStatus; - /// Get an iterator for ready transactions ordered by priority - fn ready(&self) -> Box>>; + // logging / RPC / networking /// Return an event stream of transactions imported to the pool. fn import_notification_stream(&self) -> ImportNotificationStream; - /// Returns transaction hash - fn hash_of(&self, xt: &TransactionFor) -> TxHash; + // networking /// Notify the pool about transactions broadcast. fn on_broadcasted(&self, propagations: HashMap, Vec>); + + /// Returns transaction hash + fn hash_of(&self, xt: &TransactionFor) -> TxHash; } /// An abstraction for transaction pool. diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index cf1d76f390834..3fa2fc62461c6 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -623,8 +623,8 @@ cfg_if! { } impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(block: u64) { - let ex = Extrinsic::IncludeData(block.encode()); + fn offchain_worker(header: &::Header) { + let ex = Extrinsic::IncludeData(header.number.encode()); sp_io::offchain::submit_transaction(ex.encode()).unwrap(); } } @@ -839,8 +839,8 @@ cfg_if! { } impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(block: u64) { - let ex = Extrinsic::IncludeData(block.encode()); + fn offchain_worker(header: &::Header) { + let ex = Extrinsic::IncludeData(header.number.encode()); sp_io::offchain::submit_transaction(ex.encode()).unwrap() } } From a945a8c39b961e179d2592c04931e7f9030a1d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 9 Jan 2020 16:48:47 +0100 Subject: [PATCH 29/30] Support both versions. --- bin/node/executor/tests/submit_transaction.rs | 4 +-- bin/node/runtime/src/lib.rs | 7 ++-- client/offchain/src/lib.rs | 33 +++++++++++++------ 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index b8fa389b8693d..43702dbb3565c 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -15,7 +15,7 @@ // along with Substrate. If not, see . use node_runtime::{ - Call, Executive, Indices, Balances, Runtime, SubmitTransaction, UncheckedExtrinsic, + Call, Executive, Indices, Runtime, SubmitTransaction, UncheckedExtrinsic, }; use sp_application_crypto::AppKey; use sp_core::testing::KeyStore; @@ -139,7 +139,7 @@ fn submitted_transaction_should_be_valid() { use codec::Encode; use frame_support::storage::StorageMap; use sp_runtime::transaction_validity::ValidTransaction; - use sp_runtime::traits::{Dispatchable, StaticLookup}; + use sp_runtime::traits::StaticLookup; let mut t = new_test_ext(COMPACT_CODE, false); let (pool, state) = TestTransactionPoolExt::new(); diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index dfa18556b9c92..1ad5bb6961c03 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -508,10 +508,9 @@ impl frame_system::offchain::CreateTransaction for .unwrap_or(2) as u64; let current_block = System::block_number() .saturated_into::() - // The `System::block_number` is initialized with `n+1`, so the actual block number is - // `n`, but in offchain worker context this block does not have correct `parent_hash` set, - // so we need to fall back to `n-1`. - .saturating_sub(2); + // The `System::block_number` is initialized with `n+1`, + // so the actual block number is `n`. + .saturating_sub(1); let tip = 0; let extra: SignedExtra = ( frame_system::CheckVersion::::new(), diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index 63d0b678daf00..e4bf1bb75c7a2 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -98,11 +98,18 @@ impl OffchainWorkers< ) -> impl Future { let runtime = self.client.runtime_api(); let at = BlockId::number(*header.number()); - // TODO [ToDr] Use has_api_with to support old versions too - let has_api = runtime.has_api::>(&at); - debug!("Checking offchain workers at {:?}: {:?}", at, has_api); - - if has_api.unwrap_or(false) { + let has_api_v1 = runtime.has_api_with::, _>( + &at, |v| v == 1 + ); + let has_api_v2 = runtime.has_api::>(&at); + let version = match (has_api_v1, has_api_v2) { + (_, Ok(true)) => 2, + (Ok(true), _) => 1, + _ => 0, + }; + + debug!("Checking offchain workers at {:?}: version:{}", at, version); + if version > 0 { let (api, runner) = api::AsyncApi::new( self.db.clone(), network_state.clone(), @@ -115,11 +122,17 @@ impl OffchainWorkers< let runtime = client.runtime_api(); let api = Box::new(api); debug!("Running offchain workers at {:?}", at); - let run = runtime.offchain_worker_with_context( - &at, - ExecutionContext::OffchainCall(Some((api, offchain::Capabilities::all()))), - &header, - ); + let context = ExecutionContext::OffchainCall(Some( + (api, offchain::Capabilities::all()) + )); + let run = if version == 2 { + runtime.offchain_worker_with_context(&at, context, &header) + } else { + #[allow(deprecated)] + runtime.offchain_worker_before_version_2_with_context( + &at, context, *header.number() + ) + }; if let Err(e) = run { log::error!("Error running offchain workers at {:?}: {:?}", at, e); } From 4c66a45b59c188689958769139e13cc6b1475aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 9 Jan 2020 17:00:17 +0100 Subject: [PATCH 30/30] Bump spec version, fix RPC test. --- bin/node/runtime/src/lib.rs | 4 ++-- client/rpc/src/state/tests.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 1ad5bb6961c03..c503559ae8fd0 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -78,8 +78,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 199, - impl_version: 199, + spec_version: 200, + impl_version: 200, apis: RUNTIME_API_VERSIONS, }; diff --git a/client/rpc/src/state/tests.rs b/client/rpc/src/state/tests.rs index b1b7e059e3cbe..6101724a832ff 100644 --- a/client/rpc/src/state/tests.rs +++ b/client/rpc/src/state/tests.rs @@ -400,7 +400,7 @@ fn should_return_runtime_version() { \"specVersion\":1,\"implVersion\":1,\"apis\":[[\"0xdf6acb689907609b\",2],\ [\"0x37e397fc7c91f5e4\",1],[\"0xd2bc9897eed08f15\",1],[\"0x40fe3ad401f8959a\",4],\ [\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",1],\ - [\"0xf78b278be53f454c\",1],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]]}"; + [\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]]}"; let runtime_version = api.runtime_version(None.into()).wait().unwrap(); let serialized = serde_json::to_string(&runtime_version).unwrap();