substrate-test-runtime migrated to "pure" frame runtime (#13737)

* substrate-test-runtime migrated to pure-frame based

* test block builder: helpers added

* simple renaming

* basic_authorship test adjusted

* block_building storage_proof test adjusted

* babe: tests: should_panic expected added

* babe: tests adjusted

ConsensusLog::NextEpochData is now added by pallet_babe as
pallet_babe::SameAuthoritiesForever trigger is used in runtime config.

* beefy: tests adjusted

test-substrate-runtime is now using frame::executive to finalize the
block. during finalization the digests stored during block execution are
checked against header digests:
https://github.com/paritytech/substrate/blob/91bb2d29ca905599098a5b35eaf24867c4fbd60a/frame/executive/src/lib.rs#L585-L591

It makes impossible to directly manipulate header's digets, w/o
depositing logs into system pallet storage `Digest<T: Config>`.

Instead of this dedicated extrinsic allowing to store logs items
(MmrRoot / AuthoritiesChange) is used.

* grandpa: tests adjusted

test-substrate-runtime is now using frame::executive to finalize the
block. during finalization the digest logs stored during block execution are
checked against header digest logs:
https://github.com/paritytech/substrate/blob/91bb2d29ca905599098a5b35eaf24867c4fbd60a/frame/executive/src/lib.rs#L585-L591

It makes impossible to directly manipulate header's digets, w/o
depositing logs into system pallet storage `Digest<T: Config>`.

Instead of this dedicated extrinsic allowing to store logs items
(ScheduledChange / ForcedChange and DigestItem::Other) is used.

* network:bitswap: test adjusted

The size of unchecked extrinsic was increased. The pattern used in test will
be placed at the end of scale-encoded buffer.

* runtime apis versions adjusted

* storage keys used in runtime adjusted

* wasm vs native tests removed

* rpc tests: adjusted

Transfer transaction processing was slightly improved, test was
adjusted.

* tests: sizes adjusted

Runtime extrinsic size was increased. Size of data read during block
execution was also increased due to usage of new pallets in runtime.

Sizes were adjusted in tests.

* cargo.lock update

cargo update -p substrate-test-runtime -p substrate-test-runtime-client

* warnings fixed

* builders cleanup: includes / std

* extrinsic validation cleanup

* txpool: benches performance fixed

* fmt

* spelling

* Apply suggestions from code review

Co-authored-by: Davide Galassi <davxy@datawok.net>

* Apply code review suggestions

* Apply code review suggestions

* get rid of 1063 const

* renaming: UncheckedExtrinsic -> Extrinsic

* test-utils-runtime: further step to pure-frame

* basic-authorship: tests OK

* CheckSubstrateCall added + tests fixes

* test::Transfer call removed

* priority / propagate / no sudo+root-testing

* fixing warnings + format

* cleanup: build2/nonce + format

* final tests fixes

all tests are passing

* logs/comments removal

* should_not_accept_old_signatures test removed

* make txpool benches work again

* Cargo.lock reset

* format

* sudo hack removed

* txpool benches fix+cleanup

* .gitignore reverted

* rebase fixing + unsigned cleanup

* Cargo.toml/Cargo.lock cleanup

* force-debug feature removed

* mmr tests fixed

* make cargo-clippy happy

* network sync test uses unsigned extrinsic

* cleanup

* ".git/.scripts/commands/fmt/fmt.sh"

* push_storage_change signed call remove

* GenesisConfig cleanup

* fix

* fix

* GenesisConfig simplified

* storage_keys_works: reworked

* storage_keys_works: expected keys in vec

* storage keys list moved to substrate-test-runtime

* substrate-test: some sanity tests + GenesisConfigBuilder rework

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <git@kchr.de>

* Apply suggestions from code review

* Review suggestions

* fix

* fix

* beefy: generate_blocks_and_sync block_num sync with actaul value

* Apply suggestions from code review

Co-authored-by: Davide Galassi <davxy@datawok.net>

* Update test-utils/runtime/src/genesismap.rs

Co-authored-by: Davide Galassi <davxy@datawok.net>

* cargo update -p sc-rpc -p sc-transaction-pool

* Review suggestions

* fix

* doc added

* slot_duration adjusted for Babe::slot_duration

* small doc fixes

* array_bytes::hex used instead of hex

* tiny -> medium name fix

* Apply suggestions from code review

Co-authored-by: Sebastian Kunert <skunert49@gmail.com>

* TransferData::try_from_unchecked_extrinsic -> try_from

* Update Cargo.lock

---------

Co-authored-by: parity-processbot <>
Co-authored-by: Davide Galassi <davxy@datawok.net>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: Sebastian Kunert <skunert49@gmail.com>
This commit is contained in:
Michal Kucharczyk
2023-05-04 18:20:22 +02:00
committed by GitHub
parent 3a90728de0
commit 6a295e7c28
34 changed files with 1943 additions and 2356 deletions
+46 -34
View File
@@ -291,6 +291,12 @@ version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6"
[[package]]
name = "array-bytes"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd"
[[package]]
name = "arrayref"
version = "0.3.6"
@@ -597,7 +603,7 @@ dependencies = [
name = "binary-merkle-tree"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"env_logger 0.9.3",
"hash-db",
"log",
@@ -2327,7 +2333,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
name = "frame-benchmarking"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"frame-support",
"frame-support-procedural",
"frame-system",
@@ -2355,7 +2361,7 @@ name = "frame-benchmarking-cli"
version = "4.0.0-dev"
dependencies = [
"Inflector",
"array-bytes",
"array-bytes 4.2.0",
"chrono",
"clap 4.2.5",
"comfy-table",
@@ -2465,7 +2471,7 @@ dependencies = [
name = "frame-executive"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"frame-support",
"frame-system",
"frame-try-runtime",
@@ -5043,7 +5049,7 @@ dependencies = [
name = "node-bench"
version = "0.9.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"clap 4.2.5",
"derive_more",
"fs_extra",
@@ -5079,7 +5085,7 @@ dependencies = [
name = "node-cli"
version = "3.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"assert_cmd",
"clap 4.2.5",
"clap_complete",
@@ -5641,7 +5647,7 @@ dependencies = [
name = "pallet-alliance"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"frame-benchmarking",
"frame-support",
"frame-system",
@@ -5898,7 +5904,7 @@ dependencies = [
name = "pallet-beefy-mmr"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"binary-merkle-tree",
"frame-support",
"frame-system",
@@ -5975,7 +5981,7 @@ dependencies = [
name = "pallet-contracts"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"assert_matches",
"bitflags",
"env_logger 0.9.3",
@@ -6420,7 +6426,7 @@ dependencies = [
name = "pallet-mmr"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"env_logger 0.9.3",
"frame-benchmarking",
"frame-support",
@@ -7128,7 +7134,7 @@ dependencies = [
name = "pallet-transaction-storage"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"frame-benchmarking",
"frame-support",
"frame-system",
@@ -8584,7 +8590,7 @@ dependencies = [
name = "sc-cli"
version = "0.10.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"chrono",
"clap 4.2.5",
"fdlimit",
@@ -8655,7 +8661,7 @@ dependencies = [
name = "sc-client-db"
version = "0.10.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"criterion",
"hash-db",
"kitchensink-runtime",
@@ -8822,7 +8828,7 @@ dependencies = [
name = "sc-consensus-beefy"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"async-trait",
"fnv",
"futures",
@@ -8899,7 +8905,7 @@ name = "sc-consensus-grandpa"
version = "0.10.0-dev"
dependencies = [
"ahash 0.8.3",
"array-bytes",
"array-bytes 4.2.0",
"assert_matches",
"async-trait",
"dyn-clone",
@@ -9053,7 +9059,7 @@ dependencies = [
name = "sc-executor"
version = "0.10.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"assert_matches",
"criterion",
"env_logger 0.9.3",
@@ -9156,7 +9162,7 @@ dependencies = [
name = "sc-keystore"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"async-trait",
"parking_lot 0.12.1",
"serde_json",
@@ -9171,7 +9177,7 @@ dependencies = [
name = "sc-network"
version = "0.10.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"assert_matches",
"async-channel",
"async-trait",
@@ -9253,7 +9259,7 @@ dependencies = [
name = "sc-network-common"
version = "0.10.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"async-trait",
"bitflags",
"bytes",
@@ -9302,7 +9308,7 @@ dependencies = [
name = "sc-network-light"
version = "0.10.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"futures",
"libp2p",
"log",
@@ -9323,7 +9329,7 @@ dependencies = [
name = "sc-network-statement"
version = "0.10.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"async-channel",
"futures",
"libp2p",
@@ -9343,7 +9349,7 @@ dependencies = [
name = "sc-network-sync"
version = "0.10.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"async-trait",
"fork-tree",
"futures",
@@ -9413,7 +9419,7 @@ dependencies = [
name = "sc-network-transactions"
version = "0.10.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"futures",
"libp2p",
"log",
@@ -9432,7 +9438,7 @@ dependencies = [
name = "sc-offchain"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"bytes",
"fnv",
"futures",
@@ -9562,7 +9568,7 @@ dependencies = [
name = "sc-rpc-spec-v2"
version = "0.10.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"assert_matches",
"futures",
"futures-util",
@@ -9676,7 +9682,7 @@ dependencies = [
name = "sc-service-test"
version = "2.0.0"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"async-channel",
"fdlimit",
"futures",
@@ -9858,7 +9864,7 @@ dependencies = [
name = "sc-transaction-pool"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"assert_matches",
"async-trait",
"criterion",
@@ -10585,7 +10591,7 @@ dependencies = [
name = "sp-consensus-beefy"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"lazy_static",
"parity-scale-codec",
"scale-info",
@@ -10644,7 +10650,7 @@ dependencies = [
name = "sp-core"
version = "7.0.0"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"bitflags",
"blake2",
"bounded-collections",
@@ -10824,7 +10830,7 @@ dependencies = [
name = "sp-mmr-primitives"
version = "4.0.0-dev"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"ckb-merkle-mountain-range",
"log",
"parity-scale-codec",
@@ -11031,7 +11037,7 @@ dependencies = [
name = "sp-state-machine"
version = "0.13.0"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"assert_matches",
"hash-db",
"log",
@@ -11149,7 +11155,7 @@ name = "sp-trie"
version = "7.0.0"
dependencies = [
"ahash 0.8.3",
"array-bytes",
"array-bytes 4.2.0",
"criterion",
"hash-db",
"hashbrown 0.13.2",
@@ -11501,7 +11507,7 @@ dependencies = [
name = "substrate-test-client"
version = "2.0.1"
dependencies = [
"array-bytes",
"array-bytes 4.2.0",
"async-trait",
"futures",
"parity-scale-codec",
@@ -11526,7 +11532,8 @@ dependencies = [
name = "substrate-test-runtime"
version = "2.0.0"
dependencies = [
"cfg-if",
"array-bytes 6.1.0",
"frame-executive",
"frame-support",
"frame-system",
"frame-system-rpc-runtime-api",
@@ -11534,7 +11541,10 @@ dependencies = [
"log",
"memory-db",
"pallet-babe",
"pallet-balances",
"pallet-beefy-mmr",
"pallet-root-testing",
"pallet-sudo",
"pallet-timestamp",
"parity-scale-codec",
"sc-block-builder",
@@ -11551,6 +11561,7 @@ dependencies = [
"sp-consensus-beefy",
"sp-consensus-grandpa",
"sp-core",
"sp-debug-derive",
"sp-externalities",
"sp-inherents",
"sp-io",
@@ -11561,6 +11572,7 @@ dependencies = [
"sp-session",
"sp-state-machine",
"sp-std",
"sp-tracing",
"sp-transaction-pool",
"sp-trie",
"sp-version",
@@ -547,37 +547,29 @@ mod tests {
use sp_api::Core;
use sp_blockchain::HeaderBackend;
use sp_consensus::{BlockOrigin, Environment, Proposer};
use sp_core::Pair;
use sp_runtime::{generic::BlockId, traits::NumberFor};
use sp_runtime::{generic::BlockId, traits::NumberFor, Perbill};
use substrate_test_runtime_client::{
prelude::*,
runtime::{Extrinsic, Transfer},
runtime::{Block as TestBlock, Extrinsic, ExtrinsicBuilder, Transfer},
TestClientBuilder, TestClientBuilderExt,
};
const SOURCE: TransactionSource = TransactionSource::External;
fn extrinsic(nonce: u64) -> Extrinsic {
Transfer {
amount: Default::default(),
nonce,
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Bob.into(),
}
.into_signed_tx()
}
// Note:
// Maximum normal extrinsic size for `substrate_test_runtime` is ~65% of max_block (refer to
// `substrate_test_runtime::RuntimeBlockWeights` for details).
// This extrinsic sizing allows for:
// - one huge xts + a lot of tiny dust
// - one huge, no medium,
// - two medium xts
// This is widely exploited in following tests.
const HUGE: u32 = 649000000;
const MEDIUM: u32 = 250000000;
const TINY: u32 = 1000;
fn exhausts_resources_extrinsic_from(who: usize) -> Extrinsic {
let pair = AccountKeyring::numeric(who);
let transfer = Transfer {
// increase the amount to bump priority
amount: 1,
nonce: 0,
from: pair.public(),
to: AccountKeyring::Bob.into(),
};
let signature = pair.sign(&transfer.encode()).into();
Extrinsic::Transfer { transfer, signature, exhaust_resources_when_not_first: true }
fn extrinsic(nonce: u64) -> Extrinsic {
ExtrinsicBuilder::new_fill_block(Perbill::from_parts(TINY)).nonce(nonce).build()
}
fn chain_event<B: BlockT>(header: B::Header) -> ChainEvent<B>
@@ -738,7 +730,7 @@ mod tests {
#[test]
fn should_not_remove_invalid_transactions_from_the_same_sender_after_one_was_invalid() {
// given
let mut client = Arc::new(substrate_test_runtime_client::new());
let client = Arc::new(substrate_test_runtime_client::new());
let spawner = sp_core::testing::TaskExecutor::new();
let txpool = BasicPool::new_full(
Default::default(),
@@ -748,38 +740,29 @@ mod tests {
client.clone(),
);
let medium = |nonce| {
ExtrinsicBuilder::new_fill_block(Perbill::from_parts(MEDIUM))
.nonce(nonce)
.build()
};
let huge = |nonce| {
ExtrinsicBuilder::new_fill_block(Perbill::from_parts(HUGE)).nonce(nonce).build()
};
block_on(txpool.submit_at(
&BlockId::number(0),
SOURCE,
vec![
extrinsic(0),
extrinsic(1),
Transfer {
amount: Default::default(),
nonce: 2,
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Bob.into(),
}.into_resources_exhausting_tx(),
extrinsic(3),
Transfer {
amount: Default::default(),
nonce: 4,
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Bob.into(),
}.into_resources_exhausting_tx(),
extrinsic(5),
extrinsic(6),
],
vec![medium(0), medium(1), huge(2), medium(3), huge(4), medium(5), medium(6)],
))
.unwrap();
let mut proposer_factory =
ProposerFactory::new(spawner.clone(), client.clone(), txpool.clone(), None, None);
let mut propose_block = |client: &TestClient,
number,
parent_number,
expected_block_extrinsics,
expected_pool_transactions| {
let hash = client.expect_block_hash_from_id(&BlockId::Number(number)).unwrap();
let hash = client.expect_block_hash_from_id(&BlockId::Number(parent_number)).unwrap();
let proposer = proposer_factory.init_with_now(
&client.expect_header(hash).unwrap(),
Box::new(move || time::Instant::now()),
@@ -794,12 +777,30 @@ mod tests {
// then
// block should have some extrinsics although we have some more in the pool.
assert_eq!(txpool.ready().count(), expected_pool_transactions);
assert_eq!(block.extrinsics().len(), expected_block_extrinsics);
assert_eq!(
txpool.ready().count(),
expected_pool_transactions,
"at block: {}",
block.header.number
);
assert_eq!(
block.extrinsics().len(),
expected_block_extrinsics,
"at block: {}",
block.header.number
);
block
};
let import_and_maintain = |mut client: Arc<TestClient>, block: TestBlock| {
let hash = block.hash();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
block_on(txpool.maintain(chain_event(
client.expect_header(hash).expect("there should be header"),
)));
};
block_on(
txpool.maintain(chain_event(
client
@@ -811,19 +812,28 @@ mod tests {
// let's create one block and import it
let block = propose_block(&client, 0, 2, 7);
let hashof1 = block.hash();
block_on(client.import(BlockOrigin::Own, block)).unwrap();
block_on(
txpool.maintain(chain_event(
client.expect_header(hashof1).expect("there should be header"),
)),
);
import_and_maintain(client.clone(), block);
assert_eq!(txpool.ready().count(), 5);
// now let's make sure that we can still make some progress
let block = propose_block(&client, 1, 2, 5);
block_on(client.import(BlockOrigin::Own, block)).unwrap();
let block = propose_block(&client, 1, 1, 5);
import_and_maintain(client.clone(), block);
assert_eq!(txpool.ready().count(), 4);
// again let's make sure that we can still make some progress
let block = propose_block(&client, 2, 1, 4);
import_and_maintain(client.clone(), block);
assert_eq!(txpool.ready().count(), 3);
// again let's make sure that we can still make some progress
let block = propose_block(&client, 3, 1, 3);
import_and_maintain(client.clone(), block);
assert_eq!(txpool.ready().count(), 2);
// again let's make sure that we can still make some progress
let block = propose_block(&client, 4, 2, 2);
import_and_maintain(client.clone(), block);
assert_eq!(txpool.ready().count(), 0);
}
#[test]
@@ -849,9 +859,9 @@ mod tests {
amount: 100,
nonce: 0,
}
.into_signed_tx(),
.into_unchecked_extrinsic(),
)
.chain((0..extrinsics_num - 1).map(|v| Extrinsic::IncludeData(vec![v as u8; 10])))
.chain((1..extrinsics_num as u64).map(extrinsic))
.collect::<Vec<_>>();
let block_limit = genesis_header.encoded_size() +
@@ -862,7 +872,7 @@ mod tests {
.sum::<usize>() +
Vec::<Extrinsic>::new().encoded_size();
block_on(txpool.submit_at(&BlockId::number(0), SOURCE, extrinsics)).unwrap();
block_on(txpool.submit_at(&BlockId::number(0), SOURCE, extrinsics.clone())).unwrap();
block_on(txpool.maintain(chain_event(genesis_header.clone())));
@@ -905,7 +915,13 @@ mod tests {
let proposer = block_on(proposer_factory.init(&genesis_header)).unwrap();
// Give it enough time
// Exact block_limit, which includes:
// 99 (header_size) + 718 (proof@initialize_block) + 246 (one Transfer extrinsic)
let block_limit = {
let builder =
client.new_block_at(genesis_header.hash(), Default::default(), true).unwrap();
builder.estimate_block_size(true) + extrinsics[0].encoded_size()
};
let block = block_on(proposer.propose(
Default::default(),
Default::default(),
@@ -915,7 +931,7 @@ mod tests {
.map(|r| r.block)
.unwrap();
// The block limit didn't changed, but we now include the proof in the estimation of the
// The block limit was increased, but we now include the proof in the estimation of the
// block size and thus, only the `Transfer` will fit into the block. It reads more data
// than we have reserved in the block limit.
assert_eq!(block.extrinsics().len(), 1);
@@ -934,6 +950,15 @@ mod tests {
client.clone(),
);
let tiny = |nonce| {
ExtrinsicBuilder::new_fill_block(Perbill::from_parts(TINY)).nonce(nonce).build()
};
let huge = |who| {
ExtrinsicBuilder::new_fill_block(Perbill::from_parts(HUGE))
.signer(AccountKeyring::numeric(who))
.build()
};
block_on(
txpool.submit_at(
&BlockId::number(0),
@@ -941,9 +966,9 @@ mod tests {
// add 2 * MAX_SKIPPED_TRANSACTIONS that exhaust resources
(0..MAX_SKIPPED_TRANSACTIONS * 2)
.into_iter()
.map(|i| exhausts_resources_extrinsic_from(i))
.map(huge)
// and some transactions that are okay.
.chain((0..MAX_SKIPPED_TRANSACTIONS).into_iter().map(|i| extrinsic(i as _)))
.chain((0..MAX_SKIPPED_TRANSACTIONS as u64).into_iter().map(tiny))
.collect(),
),
)
@@ -997,15 +1022,27 @@ mod tests {
client.clone(),
);
let tiny = |who| {
ExtrinsicBuilder::new_fill_block(Perbill::from_parts(TINY))
.signer(AccountKeyring::numeric(who))
.nonce(1)
.build()
};
let huge = |who| {
ExtrinsicBuilder::new_fill_block(Perbill::from_parts(HUGE))
.signer(AccountKeyring::numeric(who))
.build()
};
block_on(
txpool.submit_at(
&BlockId::number(0),
SOURCE,
(0..MAX_SKIPPED_TRANSACTIONS + 2)
.into_iter()
.map(|i| exhausts_resources_extrinsic_from(i))
.map(huge)
// and some transactions that are okay.
.chain((0..MAX_SKIPPED_TRANSACTIONS).into_iter().map(|i| extrinsic(i as _)))
.chain((0..MAX_SKIPPED_TRANSACTIONS + 2).into_iter().map(tiny))
.collect(),
),
)
@@ -1018,7 +1055,7 @@ mod tests {
.expect("there should be header"),
)),
);
assert_eq!(txpool.ready().count(), MAX_SKIPPED_TRANSACTIONS * 2 + 2);
assert_eq!(txpool.ready().count(), MAX_SKIPPED_TRANSACTIONS * 2 + 4);
let mut proposer_factory =
ProposerFactory::new(spawner.clone(), client.clone(), txpool.clone(), None, None);
@@ -1049,8 +1086,13 @@ mod tests {
.map(|r| r.block)
.unwrap();
// then the block should have no transactions despite some in the pool
assert_eq!(block.extrinsics().len(), 1);
// then the block should have one or two transactions. This maybe random as they are
// processed in parallel. The same signer and consecutive nonces for huge and tiny
// transactions guarantees that max two transactions will get to the block.
assert!(
(1..3).contains(&block.extrinsics().len()),
"Block shall contain one or two extrinsics."
);
assert!(
cell2.lock().0 > MAX_SKIPPED_TRANSACTIONS,
"Not enough calls to current time, which indicates the test might have ended because of deadline, not soft deadline"
+1 -1
View File
@@ -26,7 +26,7 @@
//! # use sp_runtime::generic::BlockId;
//! # use std::{sync::Arc, time::Duration};
//! # use substrate_test_runtime_client::{
//! # runtime::{Extrinsic, Transfer}, AccountKeyring,
//! # runtime::Transfer, AccountKeyring,
//! # DefaultTestClientBuilderExt, TestClientBuilderExt,
//! # };
//! # use sc_transaction_pool::{BasicPool, FullChainApi};
+10 -9
View File
@@ -313,7 +313,7 @@ mod tests {
use sp_core::Blake2Hasher;
use sp_state_machine::Backend;
use substrate_test_runtime_client::{
runtime::Extrinsic, DefaultTestClientBuilderExt, TestClientBuilderExt,
runtime::ExtrinsicBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt,
};
#[test]
@@ -322,9 +322,11 @@ mod tests {
let backend = builder.backend();
let client = builder.build();
let genesis_hash = client.info().best_hash;
let block = BlockBuilder::new(
&client,
client.info().best_hash,
genesis_hash,
client.info().best_number,
RecordProof::Yes,
Default::default(),
@@ -335,12 +337,11 @@ mod tests {
.unwrap();
let proof = block.proof.expect("Proof is build on request");
let genesis_state_root = client.header(genesis_hash).unwrap().unwrap().state_root;
let backend = sp_state_machine::create_proof_check_backend::<Blake2Hasher>(
block.storage_changes.transaction_storage_root,
proof,
)
.unwrap();
let backend =
sp_state_machine::create_proof_check_backend::<Blake2Hasher>(genesis_state_root, proof)
.unwrap();
assert!(backend
.storage(&sp_core::storage::well_known_keys::CODE)
@@ -364,7 +365,7 @@ mod tests {
)
.unwrap();
block_builder.push(Extrinsic::ReadAndPanic(8)).unwrap_err();
block_builder.push(ExtrinsicBuilder::new_read_and_panic(8).build()).unwrap_err();
let block = block_builder.build().unwrap();
@@ -380,7 +381,7 @@ mod tests {
)
.unwrap();
block_builder.push(Extrinsic::Read(8)).unwrap();
block_builder.push(ExtrinsicBuilder::new_read(8).build()).unwrap();
let block = block_builder.build().unwrap();
+5 -56
View File
@@ -71,15 +71,12 @@ const SLOT_DURATION_MS: u64 = 1000;
struct DummyFactory {
client: Arc<TestClient>,
epoch_changes: SharedEpochChanges<TestBlock, Epoch>,
config: BabeConfiguration,
mutator: Mutator,
}
struct DummyProposer {
factory: DummyFactory,
parent_hash: Hash,
parent_number: u64,
parent_slot: Slot,
}
impl Environment<TestBlock> for DummyFactory {
@@ -88,15 +85,9 @@ impl Environment<TestBlock> for DummyFactory {
type Error = Error;
fn init(&mut self, parent_header: &<TestBlock as BlockT>::Header) -> Self::CreateProposer {
let parent_slot = crate::find_pre_digest::<TestBlock>(parent_header)
.expect("parent header has a pre-digest")
.slot();
future::ready(Ok(DummyProposer {
factory: self.clone(),
parent_hash: parent_header.hash(),
parent_number: *parent_header.number(),
parent_slot,
}))
}
}
@@ -123,39 +114,6 @@ impl DummyProposer {
Err(e) => return future::ready(Err(e)),
};
let this_slot = crate::find_pre_digest::<TestBlock>(block.header())
.expect("baked block has valid pre-digest")
.slot();
// figure out if we should add a consensus digest, since the test runtime
// doesn't.
let epoch_changes = self.factory.epoch_changes.shared_data();
let epoch = epoch_changes
.epoch_data_for_child_of(
descendent_query(&*self.factory.client),
&self.parent_hash,
self.parent_number,
this_slot,
|slot| Epoch::genesis(&self.factory.config, slot),
)
.expect("client has data to find epoch")
.expect("can compute epoch for baked block");
let first_in_epoch = self.parent_slot < epoch.start_slot;
if first_in_epoch {
// push a `Consensus` digest signalling next change.
// we just reuse the same randomness and authorities as the prior
// epoch. this will break when we add light client support, since
// that will re-check the randomness logic off-chain.
let digest_data = ConsensusLog::NextEpochData(NextEpochDescriptor {
authorities: epoch.authorities.clone(),
randomness: epoch.randomness,
})
.encode();
let digest = DigestItem::Consensus(BABE_ENGINE_ID, digest_data);
block.header.digest_mut().push(digest)
}
// mutate the block header according to the mutator.
(self.factory.mutator)(&mut block.header, Stage::PreSeal);
@@ -351,7 +309,7 @@ impl TestNetFactory for BabeTestNet {
}
#[tokio::test]
#[should_panic]
#[should_panic(expected = "No BABE pre-runtime digest found")]
async fn rejects_empty_block() {
sp_tracing::try_init_simple();
let mut net = BabeTestNet::new(3);
@@ -398,7 +356,6 @@ async fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + '
let environ = DummyFactory {
client: client.clone(),
config: data.link.config.clone(),
epoch_changes: data.link.epoch_changes.clone(),
mutator: mutator.clone(),
};
@@ -483,7 +440,7 @@ async fn authoring_blocks() {
}
#[tokio::test]
#[should_panic]
#[should_panic(expected = "valid babe headers must contain a predigest")]
async fn rejects_missing_inherent_digest() {
run_one_test(|header: &mut TestHeader, stage| {
let v = std::mem::take(&mut header.digest_mut().logs);
@@ -496,7 +453,7 @@ async fn rejects_missing_inherent_digest() {
}
#[tokio::test]
#[should_panic]
#[should_panic(expected = "has a bad seal")]
async fn rejects_missing_seals() {
run_one_test(|header: &mut TestHeader, stage| {
let v = std::mem::take(&mut header.digest_mut().logs);
@@ -509,7 +466,7 @@ async fn rejects_missing_seals() {
}
#[tokio::test]
#[should_panic]
#[should_panic(expected = "Expected epoch change to happen")]
async fn rejects_missing_consensus_digests() {
run_one_test(|header: &mut TestHeader, stage| {
let v = std::mem::take(&mut header.digest_mut().logs);
@@ -770,7 +727,6 @@ async fn importing_block_one_sets_genesis_epoch() {
let mut proposer_factory = DummyFactory {
client: client.clone(),
config: data.link.config.clone(),
epoch_changes: data.link.epoch_changes.clone(),
mutator: Arc::new(|_, _| ()),
};
@@ -814,7 +770,6 @@ async fn revert_prunes_epoch_changes_and_removes_weights() {
let mut proposer_factory = DummyFactory {
client: client.clone(),
config: data.link.config.clone(),
epoch_changes: data.link.epoch_changes.clone(),
mutator: Arc::new(|_, _| ()),
};
@@ -902,7 +857,6 @@ async fn revert_not_allowed_for_finalized() {
let mut proposer_factory = DummyFactory {
client: client.clone(),
config: data.link.config.clone(),
epoch_changes: data.link.epoch_changes.clone(),
mutator: Arc::new(|_, _| ()),
};
@@ -943,7 +897,6 @@ async fn importing_epoch_change_block_prunes_tree() {
let mut proposer_factory = DummyFactory {
client: client.clone(),
config: data.link.config.clone(),
epoch_changes: data.link.epoch_changes.clone(),
mutator: Arc::new(|_, _| ()),
};
@@ -1030,7 +983,7 @@ async fn importing_epoch_change_block_prunes_tree() {
}
#[tokio::test]
#[should_panic]
#[should_panic(expected = "Slot number must increase: parent slot: 999, this slot: 999")]
async fn verify_slots_are_strictly_increasing() {
let mut net = BabeTestNet::new(1);
@@ -1042,7 +995,6 @@ async fn verify_slots_are_strictly_increasing() {
let mut proposer_factory = DummyFactory {
client: client.clone(),
config: data.link.config.clone(),
epoch_changes: data.link.epoch_changes.clone(),
mutator: Arc::new(|_, _| ()),
};
@@ -1082,7 +1034,6 @@ async fn obsolete_blocks_aux_data_cleanup() {
let mut proposer_factory = DummyFactory {
client: client.clone(),
config: data.link.config.clone(),
epoch_changes: data.link.epoch_changes.clone(),
mutator: Arc::new(|_, _| ()),
};
@@ -1168,7 +1119,6 @@ async fn allows_skipping_epochs() {
let mut proposer_factory = DummyFactory {
client: client.clone(),
config: data.link.config.clone(),
epoch_changes: data.link.epoch_changes.clone(),
mutator: Arc::new(|_, _| ()),
};
@@ -1298,7 +1248,6 @@ async fn allows_skipping_epochs_on_some_forks() {
let mut proposer_factory = DummyFactory {
client: client.clone(),
config: data.link.config.clone(),
epoch_changes: data.link.epoch_changes.clone(),
mutator: Arc::new(|_, _| ()),
};
+24 -18
View File
@@ -66,7 +66,7 @@ use sp_runtime::{
BuildStorage, DigestItem, EncodedJustification, Justifications, Storage,
};
use std::{marker::PhantomData, sync::Arc, task::Poll};
use substrate_test_runtime_client::{runtime::Header, ClientExt};
use substrate_test_runtime_client::{BlockBuilderExt, ClientExt};
use tokio::time::Duration;
const GENESIS_HASH: H256 = H256::zero();
@@ -165,20 +165,22 @@ impl BeefyTestNet {
// push genesis to make indexing human readable (index equals to block number)
all_hashes.push(self.peer(0).client().info().genesis_hash);
let built_hashes = self.peer(0).generate_blocks(count, BlockOrigin::File, |builder| {
let mut block = builder.build().unwrap().block;
let mut block_num: NumberFor<Block> = self.peer(0).client().info().best_number;
let built_hashes = self.peer(0).generate_blocks(count, BlockOrigin::File, |mut builder| {
block_num = block_num.saturating_add(1).try_into().unwrap();
if include_mmr_digest {
let block_num = *block.header.number();
let num_byte = block_num.to_le_bytes().into_iter().next().unwrap();
let mmr_root = MmrRootHash::repeat_byte(num_byte);
add_mmr_digest(&mut block.header, mmr_root);
add_mmr_digest(&mut builder, mmr_root);
}
if *block.header.number() % session_length == 0 {
add_auth_change_digest(&mut block.header, validator_set.clone());
if block_num % session_length == 0 {
add_auth_change_digest(&mut builder, validator_set.clone());
}
let block = builder.build().unwrap().block;
assert_eq!(block.header.number, block_num);
block
});
all_hashes.extend(built_hashes);
@@ -325,18 +327,22 @@ sp_api::mock_impl_runtime_apis! {
}
}
fn add_mmr_digest(header: &mut Header, mmr_hash: MmrRootHash) {
header.digest_mut().push(DigestItem::Consensus(
BEEFY_ENGINE_ID,
ConsensusLog::<AuthorityId>::MmrRoot(mmr_hash).encode(),
));
fn add_mmr_digest(builder: &mut impl BlockBuilderExt, mmr_hash: MmrRootHash) {
builder
.push_deposit_log_digest_item(DigestItem::Consensus(
BEEFY_ENGINE_ID,
ConsensusLog::<AuthorityId>::MmrRoot(mmr_hash).encode(),
))
.unwrap();
}
fn add_auth_change_digest(header: &mut Header, new_auth_set: BeefyValidatorSet) {
header.digest_mut().push(DigestItem::Consensus(
BEEFY_ENGINE_ID,
ConsensusLog::<AuthorityId>::AuthoritiesChange(new_auth_set).encode(),
));
fn add_auth_change_digest(builder: &mut impl BlockBuilderExt, new_auth_set: BeefyValidatorSet) {
builder
.push_deposit_log_digest_item(DigestItem::Consensus(
BEEFY_ENGINE_ID,
ConsensusLog::<AuthorityId>::AuthoritiesChange(new_auth_set).encode(),
))
.unwrap();
}
pub(crate) fn make_beefy_ids(keys: &[BeefyKeyring]) -> Vec<AuthorityId> {
+56 -61
View File
@@ -48,7 +48,7 @@ use sp_runtime::{
Justifications,
};
use std::{collections::HashSet, pin::Pin};
use substrate_test_runtime_client::runtime::BlockNumber;
use substrate_test_runtime_client::{runtime::BlockNumber, BlockBuilderExt};
use tokio::runtime::Handle;
use authorities::AuthoritySet;
@@ -399,22 +399,27 @@ async fn run_to_completion(
run_to_completion_with(blocks, net, peers, |_| None).await
}
fn add_scheduled_change(block: &mut Block, change: ScheduledChange<BlockNumber>) {
block.header.digest_mut().push(DigestItem::Consensus(
GRANDPA_ENGINE_ID,
sp_consensus_grandpa::ConsensusLog::ScheduledChange(change).encode(),
));
fn add_scheduled_change(builder: &mut impl BlockBuilderExt, change: ScheduledChange<BlockNumber>) {
builder
.push_deposit_log_digest_item(DigestItem::Consensus(
GRANDPA_ENGINE_ID,
sp_consensus_grandpa::ConsensusLog::ScheduledChange(change).encode(),
))
.unwrap();
}
fn add_forced_change(
block: &mut Block,
builder: &mut impl BlockBuilderExt,
median_last_finalized: BlockNumber,
change: ScheduledChange<BlockNumber>,
) {
block.header.digest_mut().push(DigestItem::Consensus(
GRANDPA_ENGINE_ID,
sp_consensus_grandpa::ConsensusLog::ForcedChange(median_last_finalized, change).encode(),
));
builder
.push_deposit_log_digest_item(DigestItem::Consensus(
GRANDPA_ENGINE_ID,
sp_consensus_grandpa::ConsensusLog::ForcedChange(median_last_finalized, change)
.encode(),
))
.unwrap();
}
#[tokio::test]
@@ -605,28 +610,24 @@ async fn transition_3_voters_twice_1_full_observer() {
},
14 => {
// generate transition at block 15, applied at 20.
net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
let mut block = builder.build().unwrap().block;
net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |mut builder| {
add_scheduled_change(
&mut block,
&mut builder,
ScheduledChange { next_authorities: make_ids(peers_b), delay: 4 },
);
block
builder.build().unwrap().block
});
net.lock().peer(0).push_blocks(5, false);
},
20 => {
// at block 21 we do another transition, but this time instant.
// add more until we have 30.
net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
let mut block = builder.build().unwrap().block;
net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |mut builder| {
add_scheduled_change(
&mut block,
&mut builder,
ScheduledChange { next_authorities: make_ids(&peers_c), delay: 0 },
);
block
builder.build().unwrap().block
});
net.lock().peer(0).push_blocks(9, false);
},
@@ -708,13 +709,12 @@ async fn sync_justifications_on_change_blocks() {
// at block 21 we do add a transition which is instant
let hashof21 = net
.peer(0)
.generate_blocks(1, BlockOrigin::File, |builder| {
let mut block = builder.build().unwrap().block;
.generate_blocks(1, BlockOrigin::File, |mut builder| {
add_scheduled_change(
&mut block,
&mut builder,
ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 },
);
block
builder.build().unwrap().block
})
.pop()
.unwrap();
@@ -778,26 +778,24 @@ async fn finalizes_multiple_pending_changes_in_order() {
net.peer(0).push_blocks(20, false);
// at block 21 we do add a transition which is instant
net.peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
let mut block = builder.build().unwrap().block;
net.peer(0).generate_blocks(1, BlockOrigin::File, |mut builder| {
add_scheduled_change(
&mut block,
&mut builder,
ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 },
);
block
builder.build().unwrap().block
});
// add more blocks on top of it (until we have 25)
net.peer(0).push_blocks(4, false);
// at block 26 we add another which is enacted at block 30
net.peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
let mut block = builder.build().unwrap().block;
net.peer(0).generate_blocks(1, BlockOrigin::File, |mut builder| {
add_scheduled_change(
&mut block,
&mut builder,
ScheduledChange { next_authorities: make_ids(peers_c), delay: 4 },
);
block
builder.build().unwrap().block
});
// add more blocks on top of it (until we have 30)
@@ -833,23 +831,21 @@ async fn force_change_to_new_set() {
let voters_future = initialize_grandpa(&mut net, peers_a);
let net = Arc::new(Mutex::new(net));
net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
let mut block = builder.build().unwrap().block;
net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |mut builder| {
// add a forced transition at block 12.
add_forced_change(
&mut block,
&mut builder,
0,
ScheduledChange { next_authorities: voters.clone(), delay: 10 },
);
// add a normal transition too to ensure that forced changes take priority.
add_scheduled_change(
&mut block,
&mut builder,
ScheduledChange { next_authorities: make_ids(genesis_authorities), delay: 5 },
);
block
builder.build().unwrap().block
});
net.lock().peer(0).push_blocks(25, false);
@@ -885,14 +881,15 @@ async fn allows_reimporting_change_blocks() {
let (mut block_import, ..) = net.make_block_import(client.clone());
let full_client = client.as_client();
let builder = full_client
let mut builder = full_client
.new_block_at(full_client.chain_info().genesis_hash, Default::default(), false)
.unwrap();
let mut block = builder.build().unwrap().block;
add_scheduled_change(
&mut block,
&mut builder,
ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 },
);
let block = builder.build().unwrap().block;
let block = || {
let block = block.clone();
@@ -929,16 +926,17 @@ async fn test_bad_justification() {
let (mut block_import, ..) = net.make_block_import(client.clone());
let full_client = client.as_client();
let builder = full_client
let mut builder = full_client
.new_block_at(full_client.chain_info().genesis_hash, Default::default(), false)
.unwrap();
let mut block = builder.build().unwrap().block;
add_scheduled_change(
&mut block,
&mut builder,
ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 },
);
let block = builder.build().unwrap().block;
let block = || {
let block = block.clone();
let mut import = BlockImportParams::new(BlockOrigin::File, block.header);
@@ -1629,6 +1627,7 @@ async fn grandpa_environment_passes_actual_best_block_to_voting_rules() {
#[tokio::test]
async fn grandpa_environment_checks_if_best_block_is_descendent_of_finality_target() {
sp_tracing::try_init_simple();
use finality_grandpa::voter::Environment;
let peers = &[Ed25519Keyring::Alice];
@@ -1662,10 +1661,9 @@ async fn grandpa_environment_checks_if_best_block_is_descendent_of_finality_targ
BlockId::Number(4),
6,
BlockOrigin::File,
|builder| {
let mut block = builder.build().unwrap().block;
block.header.digest_mut().push(DigestItem::Other(vec![1]));
block
|mut builder| {
builder.push_deposit_log_digest_item(DigestItem::Other(vec![1])).unwrap();
builder.build().unwrap().block
},
false,
false,
@@ -1999,13 +1997,12 @@ async fn revert_prunes_authority_changes() {
type TestBlockBuilder<'a> =
BlockBuilder<'a, Block, PeersFullClient, substrate_test_runtime_client::Backend>;
let edit_block = |builder: TestBlockBuilder| {
let mut block = builder.build().unwrap().block;
let edit_block = |mut builder: TestBlockBuilder| {
add_scheduled_change(
&mut block,
&mut builder,
ScheduledChange { next_authorities: make_ids(peers), delay: 0 },
);
block
builder.build().unwrap().block
};
let api = TestApi::new(make_ids(peers));
@@ -2047,10 +2044,9 @@ async fn revert_prunes_authority_changes() {
BlockId::Number(23),
3,
BlockOrigin::File,
|builder| {
let mut block = builder.build().unwrap().block;
block.header.digest_mut().push(DigestItem::Other(vec![1]));
block
|mut builder| {
builder.push_deposit_log_digest_item(DigestItem::Other(vec![1])).unwrap();
builder.build().unwrap().block
},
false,
false,
@@ -2079,10 +2075,9 @@ async fn revert_prunes_authority_changes() {
BlockId::Number(25),
3,
BlockOrigin::File,
|builder| {
let mut block = builder.build().unwrap().block;
block.header.digest_mut().push(DigestItem::Other(vec![2]));
block
|mut builder| {
builder.push_deposit_log_digest_item(DigestItem::Other(vec![2])).unwrap();
builder.build().unwrap().block
},
false,
false,
@@ -326,11 +326,10 @@ mod tests {
use sp_consensus::BlockOrigin;
use sp_consensus_grandpa::GRANDPA_ENGINE_ID;
use sp_keyring::Ed25519Keyring;
use sp_runtime::traits::Header as _;
use std::sync::Arc;
use substrate_test_runtime_client::{
ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, TestClientBuilder,
TestClientBuilderExt,
BlockBuilderExt, ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt,
TestClientBuilder, TestClientBuilderExt,
};
#[test]
@@ -348,8 +347,7 @@ mod tests {
let mut authority_set_changes = Vec::new();
for n in 1..=100 {
let mut block = client.new_block(Default::default()).unwrap().build().unwrap().block;
let mut builder = client.new_block(Default::default()).unwrap();
let mut new_authorities = None;
// we will trigger an authority set change every 10 blocks
@@ -376,9 +374,11 @@ mod tests {
.encode(),
);
block.header.digest_mut().logs.push(digest);
builder.push_deposit_log_digest_item(digest).unwrap();
}
let block = builder.build().unwrap().block;
futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
if let Some(new_authorities) = new_authorities {
+5 -3
View File
@@ -297,7 +297,7 @@ mod tests {
};
use sp_consensus::BlockOrigin;
use sp_runtime::codec::Encode;
use substrate_test_runtime::Extrinsic;
use substrate_test_runtime::ExtrinsicBuilder;
use substrate_test_runtime_client::{self, prelude::*, TestClientBuilder};
#[tokio::test]
@@ -470,7 +470,9 @@ mod tests {
let mut client = TestClientBuilder::with_tx_storage(u32::MAX).build();
let mut block_builder = client.new_block(Default::default()).unwrap();
let ext = Extrinsic::Store(vec![0x13, 0x37, 0x13, 0x38]);
// encoded extrinsic: [161, .. , 2, 6, 16, 19, 55, 19, 56]
let ext = ExtrinsicBuilder::new_indexed_call(vec![0x13, 0x37, 0x13, 0x38]).build();
let pattern_index = ext.encoded_size() - 4;
block_builder.push(ext.clone()).unwrap();
let block = block_builder.build().unwrap().block;
@@ -494,7 +496,7 @@ mod tests {
0x70,
cid::multihash::Multihash::wrap(
u64::from(cid::multihash::Code::Blake2b256),
&sp_core::hashing::blake2_256(&ext.encode()[2..]),
&sp_core::hashing::blake2_256(&ext.encode()[pattern_index..]),
)
.unwrap(),
)
+2 -14
View File
@@ -88,13 +88,11 @@ use sp_runtime::{
};
use substrate_test_runtime_client::AccountKeyring;
pub use substrate_test_runtime_client::{
runtime::{Block, Extrinsic, Hash, Header, Transfer},
runtime::{Block, ExtrinsicBuilder, Hash, Header, Transfer},
TestClient, TestClientBuilder, TestClientBuilderExt,
};
use tokio::time::timeout;
type AuthorityId = sp_consensus_babe::AuthorityId;
/// A Verifier that accepts all blocks and passes them on with the configured
/// finality to be imported.
#[derive(Clone)]
@@ -474,7 +472,7 @@ where
amount: 1,
nonce,
};
builder.push(transfer.into_signed_tx()).unwrap();
builder.push(transfer.into_unchecked_extrinsic()).unwrap();
nonce += 1;
builder.build().unwrap().block
},
@@ -497,16 +495,6 @@ where
}
}
pub fn push_authorities_change_block(
&mut self,
new_authorities: Vec<AuthorityId>,
) -> Vec<H256> {
self.generate_blocks(1, BlockOrigin::File, |mut builder| {
builder.push(Extrinsic::AuthoritiesChange(new_authorities.clone())).unwrap();
builder.build().unwrap().block
})
}
/// Get a reference to the client.
pub fn client(&self) -> &PeersClient {
&self.client
+5 -3
View File
@@ -1183,7 +1183,7 @@ async fn syncs_indexed_blocks() {
64,
BlockOrigin::Own,
|mut builder| {
let ex = Extrinsic::Store(n.to_le_bytes().to_vec());
let ex = ExtrinsicBuilder::new_indexed_call(n.to_le_bytes().to_vec()).nonce(n).build();
n += 1;
builder.push(ex).unwrap();
builder.build().unwrap().block
@@ -1305,11 +1305,13 @@ async fn syncs_huge_blocks() {
builder.build().unwrap().block
});
let mut nonce = 0;
net.peer(0).generate_blocks(32, BlockOrigin::Own, |mut builder| {
// Add 32 extrinsics 32k each = 1MiB total
for _ in 0..32 {
let ex = Extrinsic::IncludeData([42u8; 32 * 1024].to_vec());
for _ in 0..32u64 {
let ex = ExtrinsicBuilder::new_include_data(vec![42u8; 32 * 1024]).nonce(nonce).build();
builder.push(ex).unwrap();
nonce += 1;
}
builder.build().unwrap().block
});
+12 -14
View File
@@ -254,8 +254,10 @@ mod tests {
use sp_runtime::generic::BlockId;
use std::{collections::HashSet, sync::Arc};
use substrate_test_runtime_client::{
runtime::Block, ClientBlockImportExt, DefaultTestClientBuilderExt, TestClient,
TestClientBuilderExt,
runtime::{
substrate_test_pallet::pallet::Call as PalletCall, Block, ExtrinsicBuilder, RuntimeCall,
},
ClientBlockImportExt, DefaultTestClientBuilderExt, TestClient, TestClientBuilderExt,
};
struct TestNetwork();
@@ -385,7 +387,10 @@ mod tests {
// then
assert_eq!(pool.0.status().ready, 1);
assert_eq!(pool.0.ready().next().unwrap().is_propagable(), false);
assert!(matches!(
pool.0.ready().next().unwrap().data().function,
RuntimeCall::SubstrateTest(PalletCall::storage_change { .. })
));
}
#[test]
@@ -403,12 +408,8 @@ mod tests {
let key = &b"hello"[..];
let value = &b"world"[..];
let mut block_builder = client.new_block(Default::default()).unwrap();
block_builder
.push(substrate_test_runtime_client::runtime::Extrinsic::OffchainIndexSet(
key.to_vec(),
value.to_vec(),
))
.unwrap();
let ext = ExtrinsicBuilder::new_offchain_index_set(key.to_vec(), value.to_vec()).build();
block_builder.push(ext).unwrap();
let block = block_builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, block)).unwrap();
@@ -416,11 +417,8 @@ mod tests {
assert_eq!(value, &offchain_db.get(sp_offchain::STORAGE_PREFIX, &key).unwrap());
let mut block_builder = client.new_block(Default::default()).unwrap();
block_builder
.push(substrate_test_runtime_client::runtime::Extrinsic::OffchainIndexClear(
key.to_vec(),
))
.unwrap();
let ext = ExtrinsicBuilder::new_offchain_index_clear(key.to_vec()).nonce(1).build();
block_builder.push(ext).unwrap();
let block = block_builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, block)).unwrap();
@@ -184,12 +184,13 @@ async fn follow_with_runtime() {
// Initialized must always be reported first.
let event: FollowEvent<String> = get_next_event(&mut sub).await;
// it is basically json-encoded substrate_test_runtime_client::runtime::VERSION
let runtime_str = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\
\"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\
[\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\
[\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",2],\
[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]],\
\"transactionVersion\":1,\"stateVersion\":1}";
[\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\
[\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\
[\"0xed99c5acb25eedf5\",3]],\"transactionVersion\":1,\"stateVersion\":1}";
let runtime: RuntimeVersion = serde_json::from_str(runtime_str).unwrap();
let finalized_block_runtime =
+17 -14
View File
@@ -37,10 +37,11 @@ use sp_core::{
H256,
};
use sp_keystore::{testing::MemoryKeystore, Keystore};
use sp_runtime::Perbill;
use std::sync::Arc;
use substrate_test_runtime_client::{
self,
runtime::{Block, Extrinsic, SessionKeys, Transfer},
runtime::{Block, Extrinsic, ExtrinsicBuilder, SessionKeys, Transfer},
AccountKeyring, Backend, Client, DefaultTestClientBuilderExt, TestClientBuilderExt,
};
@@ -51,7 +52,7 @@ fn uxt(sender: AccountKeyring, nonce: u64) -> Extrinsic {
from: sender.into(),
to: AccountKeyring::Bob.into(),
};
tx.into_signed_tx()
ExtrinsicBuilder::new_transfer(tx).build()
}
type FullTransactionPool = BasicPool<FullChainApi<Client<Backend>, Block>, Block>;
@@ -111,7 +112,13 @@ async fn author_submit_transaction_should_not_cause_error() {
#[tokio::test]
async fn author_should_watch_extrinsic() {
let api = TestSetup::into_rpc();
let xt = to_hex(&uxt(AccountKeyring::Alice, 0).encode(), true);
let xt = to_hex(
&ExtrinsicBuilder::new_call_with_priority(0)
.signer(AccountKeyring::Alice.into())
.build()
.encode(),
true,
);
let mut sub = api.subscribe("author_submitAndWatchExtrinsic", [xt]).await.unwrap();
let (tx, sub_id) = timeout_secs(10, sub.next::<TransactionStatus<H256, Block>>())
@@ -125,15 +132,11 @@ async fn author_should_watch_extrinsic() {
// Replace the extrinsic and observe the subscription is notified.
let (xt_replacement, xt_hash) = {
let tx = Transfer {
amount: 5,
nonce: 0,
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Bob.into(),
};
let tx = tx.into_signed_tx().encode();
let tx = ExtrinsicBuilder::new_call_with_priority(1)
.signer(AccountKeyring::Alice.into())
.build()
.encode();
let hash = blake2_256(&tx);
(to_hex(&tx, true), hash)
};
@@ -152,10 +155,10 @@ async fn author_should_watch_extrinsic() {
async fn author_should_return_watch_validation_error() {
const METHOD: &'static str = "author_submitAndWatchExtrinsic";
let invalid_xt = ExtrinsicBuilder::new_fill_block(Perbill::from_percent(100)).build();
let api = TestSetup::into_rpc();
let failed_sub = api
.subscribe(METHOD, [to_hex(&uxt(AccountKeyring::Alice, 179).encode(), true)])
.await;
let failed_sub = api.subscribe(METHOD, [to_hex(&invalid_xt.encode(), true)]).await;
assert_matches!(
failed_sub,
+19 -3
View File
@@ -28,6 +28,22 @@ async fn block_stats_work() {
let api = <Dev<Block, _>>::new(client.clone(), DenyUnsafe::No).into_rpc();
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
let (expected_witness_len, expected_witness_compact_len, expected_block_len) = {
let genesis_hash = client.chain_info().genesis_hash;
let mut runtime_api = client.runtime_api();
runtime_api.record_proof();
runtime_api.execute_block(genesis_hash, block.clone()).unwrap();
let witness = runtime_api.extract_proof().unwrap();
let pre_root = *client.header(genesis_hash).unwrap().unwrap().state_root();
(
witness.clone().encoded_size() as u64,
witness.into_compact_proof::<HasherOf<Block>>(pre_root).unwrap().encoded_size() as u64,
block.encoded_size() as u64,
)
};
client.import(BlockOrigin::Own, block).await.unwrap();
// Can't gather stats for a block without a parent.
@@ -43,9 +59,9 @@ async fn block_stats_work() {
.await
.unwrap(),
Some(BlockStats {
witness_len: 630,
witness_compact_len: 534,
block_len: 99,
witness_len: expected_witness_len,
witness_compact_len: expected_witness_compact_len,
block_len: expected_block_len,
num_extrinsics: 0,
}),
);
+47 -16
View File
@@ -29,9 +29,11 @@ use sc_block_builder::BlockBuilderProvider;
use sc_rpc_api::DenyUnsafe;
use sp_consensus::BlockOrigin;
use sp_core::{hash::H256, storage::ChildInfo};
use sp_io::hashing::blake2_256;
use std::sync::Arc;
use substrate_test_runtime_client::{prelude::*, runtime};
use substrate_test_runtime_client::{
prelude::*,
runtime::{ExtrinsicBuilder, Transfer},
};
const STORAGE_KEY: &[u8] = b"child";
@@ -218,7 +220,7 @@ async fn should_notify_about_storage_changes() {
// Cause a change:
let mut builder = client.new_block(Default::default()).unwrap();
builder
.push_transfer(runtime::Transfer {
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 42,
@@ -244,18 +246,26 @@ async fn should_send_initial_storage_changes_and_notifications() {
let mut client = Arc::new(substrate_test_runtime_client::new());
let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No);
let alice_balance_key =
blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into()));
let alice_balance_key = [
sp_core::hashing::twox_128(b"System"),
sp_core::hashing::twox_128(b"Account"),
sp_core::hashing::blake2_128(&AccountKeyring::Alice.public()),
]
.concat()
.iter()
.chain(AccountKeyring::Alice.public().0.iter())
.cloned()
.collect::<Vec<u8>>();
let api_rpc = api.into_rpc();
let sub = api_rpc
.subscribe("state_subscribeStorage", [[StorageKey(alice_balance_key.to_vec())]])
.subscribe("state_subscribeStorage", [[StorageKey(alice_balance_key)]])
.await
.unwrap();
let mut builder = client.new_block(Default::default()).unwrap();
builder
.push_transfer(runtime::Transfer {
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 42,
@@ -280,22 +290,42 @@ async fn should_query_storage() {
async fn run_tests(mut client: Arc<TestClient>) {
let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No);
let mut add_block = |nonce| {
let mut add_block = |index| {
let mut builder = client.new_block(Default::default()).unwrap();
// fake change: None -> None -> None
builder.push_storage_change(vec![1], None).unwrap();
builder
.push(ExtrinsicBuilder::new_storage_change(vec![1], None).build())
.unwrap();
// fake change: None -> Some(value) -> Some(value)
builder.push_storage_change(vec![2], Some(vec![2])).unwrap();
builder
.push(ExtrinsicBuilder::new_storage_change(vec![2], Some(vec![2])).build())
.unwrap();
// actual change: None -> Some(value) -> None
builder
.push_storage_change(vec![3], if nonce == 0 { Some(vec![3]) } else { None })
.push(
ExtrinsicBuilder::new_storage_change(
vec![3],
if index == 0 { Some(vec![3]) } else { None },
)
.build(),
)
.unwrap();
// actual change: None -> Some(value)
builder
.push_storage_change(vec![4], if nonce == 0 { None } else { Some(vec![4]) })
.push(
ExtrinsicBuilder::new_storage_change(
vec![4],
if index == 0 { None } else { Some(vec![4]) },
)
.build(),
)
.unwrap();
// actual change: Some(value1) -> Some(value2)
builder.push_storage_change(vec![5], Some(vec![nonce as u8])).unwrap();
builder
.push(
ExtrinsicBuilder::new_storage_change(vec![5], Some(vec![index as u8])).build(),
)
.unwrap();
let block = builder.build().unwrap().block;
let hash = block.header.hash();
executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
@@ -482,12 +512,13 @@ async fn should_return_runtime_version() {
let client = Arc::new(substrate_test_runtime_client::new());
let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No);
// it is basically json-encoded substrate_test_runtime_client::runtime::VERSION
let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\
\"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\
[\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\
[\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",2],\
[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]],\
\"transactionVersion\":1,\"stateVersion\":1}";
[\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\
[\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\
[\"0xed99c5acb25eedf5\",3]],\"transactionVersion\":1,\"stateVersion\":1}";
let runtime_version = api.runtime_version(None.into()).unwrap();
let serialized = serde_json::to_string(&runtime_version).unwrap();
+4 -7
View File
@@ -520,10 +520,9 @@ mod tests {
use futures::executor::block_on;
use sc_transaction_pool::BasicPool;
use sp_consensus::SelectChain;
use sp_runtime::traits::BlindCheckable;
use substrate_test_runtime_client::{
prelude::*,
runtime::{Extrinsic, Transfer},
runtime::{ExtrinsicBuilder, Transfer, TransferData},
};
#[test]
@@ -542,13 +541,13 @@ mod tests {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Bob.into(),
}
.into_signed_tx();
.into_unchecked_extrinsic();
block_on(pool.submit_one(&BlockId::hash(best.hash()), source, transaction.clone()))
.unwrap();
block_on(pool.submit_one(
&BlockId::hash(best.hash()),
source,
Extrinsic::IncludeData(vec![1]),
ExtrinsicBuilder::new_call_do_not_propagate().nonce(1).build(),
))
.unwrap();
assert_eq!(pool.status().ready, 2);
@@ -558,8 +557,6 @@ mod tests {
// then
assert_eq!(transactions.len(), 1);
assert!(transactions[0].1.clone().check().is_ok());
// this should not panic
let _ = transactions[0].1.transfer();
assert!(TransferData::try_from(&transactions[0].1).is_ok());
}
}
+83 -142
View File
@@ -48,7 +48,8 @@ use substrate_test_runtime_client::{
new_native_or_wasm_executor,
prelude::*,
runtime::{
genesismap::{insert_genesis_block, GenesisConfig},
currency::DOLLARS,
genesismap::{insert_genesis_block, GenesisStorageBuilder},
Block, BlockNumber, Digest, Hash, Header, RuntimeApi, Transfer,
},
AccountKeyring, BlockBuilderExt, ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt,
@@ -66,7 +67,7 @@ fn construct_block(
state_root: Hash,
txs: Vec<Transfer>,
) -> (Vec<u8>, Hash) {
let transactions = txs.into_iter().map(|tx| tx.into_signed_tx()).collect::<Vec<_>>();
let transactions = txs.into_iter().map(|tx| tx.into_unchecked_extrinsic()).collect::<Vec<_>>();
let iter = transactions.iter().map(Encode::encode);
let extrinsics_root = LayoutV0::<BlakeTwo256>::ordered_trie_root(iter).into();
@@ -137,9 +138,9 @@ fn block1(genesis_hash: Hash, backend: &InMemoryBackend<BlakeTwo256>) -> (Vec<u8
"25e5b37074063ab75c889326246640729b40d0c86932edc527bc80db0e04fe5c",
),
vec![Transfer {
from: AccountKeyring::One.into(),
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Two.into(),
amount: 69,
amount: 69 * DOLLARS,
nonce: 0,
}],
)
@@ -167,14 +168,12 @@ fn finality_notification_check(
#[test]
fn construct_genesis_should_work_with_native() {
let mut storage = GenesisConfig::new(
let mut storage = GenesisStorageBuilder::new(
vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()],
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
1000,
None,
Default::default(),
1000 * DOLLARS,
)
.genesis_map();
.build_storage();
let genesis_hash = insert_genesis_block(&mut storage);
let backend = InMemoryBackend::from((storage, StateVersion::default()));
@@ -200,14 +199,12 @@ fn construct_genesis_should_work_with_native() {
#[test]
fn construct_genesis_should_work_with_wasm() {
let mut storage = GenesisConfig::new(
let mut storage = GenesisStorageBuilder::new(
vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()],
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
1000,
None,
Default::default(),
1000 * DOLLARS,
)
.genesis_map();
.build_storage();
let genesis_hash = insert_genesis_block(&mut storage);
let backend = InMemoryBackend::from((storage, StateVersion::default()));
@@ -231,39 +228,6 @@ fn construct_genesis_should_work_with_wasm() {
.unwrap();
}
#[test]
fn construct_genesis_with_bad_transaction_should_panic() {
let mut storage = GenesisConfig::new(
vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()],
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
68,
None,
Default::default(),
)
.genesis_map();
let genesis_hash = insert_genesis_block(&mut storage);
let backend = InMemoryBackend::from((storage, StateVersion::default()));
let (b1data, _b1hash) = block1(genesis_hash, &backend);
let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&backend);
let runtime_code = backend_runtime_code.runtime_code().expect("Code is part of the backend");
let mut overlay = OverlayedChanges::default();
let r = StateMachine::new(
&backend,
&mut overlay,
&new_native_or_wasm_executor(),
"Core_execute_block",
&b1data,
Default::default(),
&runtime_code,
CallContext::Onchain,
)
.execute(ExecutionStrategy::NativeElseWasm);
assert!(r.is_err());
}
#[test]
fn client_initializes_from_genesis_ok() {
let client = substrate_test_runtime_client::new();
@@ -273,14 +237,14 @@ fn client_initializes_from_genesis_ok() {
.runtime_api()
.balance_of(client.chain_info().best_hash, AccountKeyring::Alice.into())
.unwrap(),
1000
1000 * DOLLARS
);
assert_eq!(
client
.runtime_api()
.balance_of(client.chain_info().best_hash, AccountKeyring::Ferdie.into())
.unwrap(),
0
0 * DOLLARS
);
}
@@ -305,7 +269,7 @@ fn block_builder_works_with_transactions() {
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 42,
amount: 42 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -340,42 +304,43 @@ fn block_builder_works_with_transactions() {
.runtime_api()
.balance_of(client.chain_info().best_hash, AccountKeyring::Alice.into())
.unwrap(),
958
958 * DOLLARS
);
assert_eq!(
client
.runtime_api()
.balance_of(client.chain_info().best_hash, AccountKeyring::Ferdie.into())
.unwrap(),
42
42 * DOLLARS
);
}
#[test]
fn block_builder_does_not_include_invalid() {
let mut client = substrate_test_runtime_client::new();
let mut builder = client.new_block(Default::default()).unwrap();
builder
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 42,
amount: 42 * DOLLARS,
nonce: 0,
})
.unwrap();
assert!(builder
.push_transfer(Transfer {
from: AccountKeyring::Eve.into(),
to: AccountKeyring::Alice.into(),
amount: 42,
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 30 * DOLLARS,
nonce: 0,
})
.is_err());
let block = builder.build().unwrap().block;
//transfer from Eve should not be included
assert_eq!(block.extrinsics.len(), 1);
block_on(client.import(BlockOrigin::Own, block)).unwrap();
let hashof0 = client
@@ -491,7 +456,7 @@ fn uncles_with_multiple_forks() {
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 41,
amount: 41 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -523,7 +488,7 @@ fn uncles_with_multiple_forks() {
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
amount: 1 * DOLLARS,
nonce: 1,
})
.unwrap();
@@ -537,7 +502,7 @@ fn uncles_with_multiple_forks() {
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
amount: 1 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -646,7 +611,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() {
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 41,
amount: 41 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -678,7 +643,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() {
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
amount: 1 * DOLLARS,
nonce: 1,
})
.unwrap();
@@ -692,7 +657,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() {
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
amount: 1 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -868,7 +833,7 @@ fn finality_target_with_best_not_on_longest_chain() {
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 41,
amount: 41 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -993,7 +958,7 @@ fn importing_diverged_finalized_block_should_trigger_reorg() {
b1.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
amount: 1 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -1048,7 +1013,7 @@ fn finalizing_diverged_block_should_trigger_reorg() {
b1.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
amount: 1 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -1166,7 +1131,7 @@ fn finality_notifications_content() {
c1.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 2,
amount: 2 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -1178,7 +1143,7 @@ fn finality_notifications_content() {
d3.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 2,
amount: 2 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -1252,7 +1217,7 @@ fn state_reverted_on_reorg() {
a1.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Bob.into(),
amount: 10,
amount: 10 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -1265,7 +1230,7 @@ fn state_reverted_on_reorg() {
b1.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 50,
amount: 50 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -1273,19 +1238,19 @@ fn state_reverted_on_reorg() {
// Reorg to B1
block_on(client.import_as_best(BlockOrigin::Own, b1.clone())).unwrap();
assert_eq!(950, current_balance(&client));
assert_eq!(950 * DOLLARS, current_balance(&client));
let mut a2 = client.new_block_at(a1.hash(), Default::default(), false).unwrap();
a2.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Charlie.into(),
amount: 10,
amount: 10 * DOLLARS,
nonce: 1,
})
.unwrap();
let a2 = a2.build().unwrap().block;
// Re-org to A2
block_on(client.import_as_best(BlockOrigin::Own, a2)).unwrap();
assert_eq!(980, current_balance(&client));
assert_eq!(980 * DOLLARS, current_balance(&client));
}
#[test]
@@ -1342,7 +1307,7 @@ fn doesnt_import_blocks_that_revert_finality() {
b1.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
amount: 1 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -1386,7 +1351,7 @@ fn doesnt_import_blocks_that_revert_finality() {
c1.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 2,
amount: 2 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -1579,7 +1544,7 @@ fn returns_status_for_pruned_blocks() {
b1.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
amount: 1 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -1718,8 +1683,9 @@ fn storage_keys_prefix_and_start_key_works() {
res,
[
child_root.clone(),
array_bytes::hex2bytes_unchecked("3a636f6465"),
array_bytes::hex2bytes_unchecked("3a686561707061676573"),
array_bytes::hex2bytes_unchecked("3a636f6465"), //":code"
array_bytes::hex2bytes_unchecked("3a65787472696e7369635f696e646578"), //":extrinsic_index"
array_bytes::hex2bytes_unchecked("3a686561707061676573"), //":heappages"
]
);
@@ -1732,7 +1698,13 @@ fn storage_keys_prefix_and_start_key_works() {
.unwrap()
.map(|x| x.0)
.collect();
assert_eq!(res, [array_bytes::hex2bytes_unchecked("3a686561707061676573")]);
assert_eq!(
res,
[
array_bytes::hex2bytes_unchecked("3a65787472696e7369635f696e646578"),
array_bytes::hex2bytes_unchecked("3a686561707061676573")
]
);
let res: Vec<_> = client
.storage_keys(
@@ -1762,54 +1734,32 @@ fn storage_keys_prefix_and_start_key_works() {
#[test]
fn storage_keys_works() {
sp_tracing::try_init_simple();
let expected_keys =
substrate_test_runtime::storage_key_generator::get_expected_storage_hashed_keys();
let client = substrate_test_runtime_client::new();
let block_hash = client.info().best_hash;
let prefix = StorageKey(array_bytes::hex2bytes_unchecked(""));
let res: Vec<_> = client
.storage_keys(block_hash, Some(&prefix), None)
.unwrap()
.take(9)
.take(19)
.map(|x| array_bytes::bytes2hex("", &x.0))
.collect();
assert_eq!(
res,
[
"00c232cf4e70a5e343317016dc805bf80a6a8cd8ad39958d56f99891b07851e0",
"085b2407916e53a86efeb8b72dbe338c4b341dab135252f96b6ed8022209b6cb",
"0befda6e1ca4ef40219d588a727f1271",
"1a560ecfd2a62c2b8521ef149d0804eb621050e3988ed97dca55f0d7c3e6aa34",
"1d66850d32002979d67dd29dc583af5b2ae2a1f71c1f35ad90fff122be7a3824",
"237498b98d8803334286e9f0483ef513098dd3c1c22ca21c4dc155b4ef6cc204",
"26aa394eea5630e07c48ae0c9558cef75e0621c4869aa60c02be9adcc98a0d1d",
"29b9db10ec5bf7907d8f74b5e60aa8140c4fbdd8127a1ee5600cb98e5ec01729",
"3a636f6465",
]
);
assert_eq!(res, expected_keys[0..19],);
// Starting at an empty key nothing gets skipped.
let res: Vec<_> = client
.storage_keys(block_hash, Some(&prefix), Some(&StorageKey("".into())))
.unwrap()
.take(9)
.take(19)
.map(|x| array_bytes::bytes2hex("", &x.0))
.collect();
assert_eq!(
res,
[
"00c232cf4e70a5e343317016dc805bf80a6a8cd8ad39958d56f99891b07851e0",
"085b2407916e53a86efeb8b72dbe338c4b341dab135252f96b6ed8022209b6cb",
"0befda6e1ca4ef40219d588a727f1271",
"1a560ecfd2a62c2b8521ef149d0804eb621050e3988ed97dca55f0d7c3e6aa34",
"1d66850d32002979d67dd29dc583af5b2ae2a1f71c1f35ad90fff122be7a3824",
"237498b98d8803334286e9f0483ef513098dd3c1c22ca21c4dc155b4ef6cc204",
"26aa394eea5630e07c48ae0c9558cef75e0621c4869aa60c02be9adcc98a0d1d",
"29b9db10ec5bf7907d8f74b5e60aa8140c4fbdd8127a1ee5600cb98e5ec01729",
"3a636f6465",
]
);
assert_eq!(res, expected_keys[0..19],);
// Starting at an incomplete key nothing gets skipped.
let res: Vec<_> = client
@@ -1824,16 +1774,12 @@ fn storage_keys_works() {
.collect();
assert_eq!(
res,
[
"3a636f6465",
"3a686561707061676573",
"52008686cc27f6e5ed83a216929942f8bcd32a396f09664a5698f81371934b56",
"5348d72ac6cc66e5d8cbecc27b0e0677503b845fe2382d819f83001781788fd5",
"5c2d5fda66373dabf970e4fb13d277ce91c5233473321129d32b5a8085fa8133",
"6644b9b8bc315888ac8e41a7968dc2b4141a5403c58acdf70b7e8f7e07bf5081",
"66484000ed3f75c95fc7b03f39c20ca1e1011e5999278247d3b2f5e3c3273808",
"7d5007603a7f5dd729d51d93cf695d6465789443bb967c0d1fe270e388c96eaa",
]
expected_keys
.iter()
.filter(|&i| i > &"3a636f64".to_string())
.take(8)
.cloned()
.collect::<Vec<String>>()
);
// Starting at a complete key the first key is skipped.
@@ -1849,38 +1795,33 @@ fn storage_keys_works() {
.collect();
assert_eq!(
res,
[
"3a686561707061676573",
"52008686cc27f6e5ed83a216929942f8bcd32a396f09664a5698f81371934b56",
"5348d72ac6cc66e5d8cbecc27b0e0677503b845fe2382d819f83001781788fd5",
"5c2d5fda66373dabf970e4fb13d277ce91c5233473321129d32b5a8085fa8133",
"6644b9b8bc315888ac8e41a7968dc2b4141a5403c58acdf70b7e8f7e07bf5081",
"66484000ed3f75c95fc7b03f39c20ca1e1011e5999278247d3b2f5e3c3273808",
"7d5007603a7f5dd729d51d93cf695d6465789443bb967c0d1fe270e388c96eaa",
]
expected_keys
.iter()
.filter(|&i| i > &"3a636f6465".to_string())
.take(8)
.cloned()
.collect::<Vec<String>>()
);
const SOME_BALANCE_KEY : &str = "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e2c1dc507e2035edbbd8776c440d870460c57f0008067cc01c5ff9eb2e2f9b3a94299a915a91198bd1021a6c55596f57";
let res: Vec<_> = client
.storage_keys(
block_hash,
Some(&prefix),
Some(&StorageKey(array_bytes::hex2bytes_unchecked(
"7d5007603a7f5dd729d51d93cf695d6465789443bb967c0d1fe270e388c96eaa",
))),
Some(&StorageKey(array_bytes::hex2bytes_unchecked(SOME_BALANCE_KEY))),
)
.unwrap()
.take(5)
.take(8)
.map(|x| array_bytes::bytes2hex("", &x.0))
.collect();
assert_eq!(
res,
[
"811ecfaadcf5f2ee1d67393247e2f71a1662d433e8ce7ff89fb0d4aa9561820b",
"a93d74caa7ec34ea1b04ce1e5c090245f867d333f0f88278a451e45299654dc5",
"a9ee1403384afbfc13f13be91ff70bfac057436212e53b9733914382ac942892",
"cf722c0832b5231d35e29f319ff27389f5032bfc7bfc3ba5ed7839f2042fb99f",
"e3b47b6c84c0493481f97c5197d2554f",
]
expected_keys
.iter()
.filter(|&i| i > &SOME_BALANCE_KEY.to_string())
.take(8)
.cloned()
.collect::<Vec<String>>()
);
}
@@ -1999,7 +1940,7 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi
b1.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
amount: 1 * DOLLARS,
nonce: 0,
})
.unwrap();
@@ -18,7 +18,7 @@
use criterion::{criterion_group, criterion_main, Criterion};
use codec::{Decode, Encode};
use codec::Encode;
use futures::{
executor::block_on,
future::{ready, Ready},
@@ -33,7 +33,7 @@ use sp_runtime::{
ValidTransaction,
},
};
use substrate_test_runtime::{AccountId, Block, Extrinsic, Transfer, H256};
use substrate_test_runtime::{AccountId, Block, Extrinsic, ExtrinsicBuilder, TransferData, H256};
#[derive(Clone, Debug, Default)]
struct TestApi {
@@ -65,8 +65,10 @@ impl ChainApi for TestApi {
_source: TransactionSource,
uxt: <Self::Block as BlockT>::Extrinsic,
) -> Self::ValidationFuture {
let nonce = uxt.transfer().nonce;
let from = uxt.transfer().from;
let transfer = TransferData::try_from(&uxt)
.expect("uxt is expected to be bench_call (carrying TransferData)");
let nonce = transfer.nonce;
let from = transfer.from;
match self.block_id_to_number(at) {
Ok(Some(num)) if num > 5 => return ready(Ok(Err(InvalidTransaction::Stale.into()))),
@@ -131,13 +133,8 @@ impl ChainApi for TestApi {
}
}
fn uxt(transfer: Transfer) -> Extrinsic {
Extrinsic::Transfer {
transfer,
signature: Decode::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
.expect("infinite input; no dead input space; qed"),
exhaust_resources_when_not_first: false,
}
fn uxt(transfer: TransferData) -> Extrinsic {
ExtrinsicBuilder::new_bench_call(transfer).build()
}
fn bench_configured(pool: Pool<TestApi>, number: u64) {
@@ -146,7 +143,7 @@ fn bench_configured(pool: Pool<TestApi>, number: u64) {
let mut tags = Vec::new();
for nonce in 1..=number {
let xt = uxt(Transfer {
let xt = uxt(TransferData {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
@@ -463,7 +463,8 @@ mod tests {
use sc_transaction_pool_api::TransactionStatus;
use sp_runtime::transaction_validity::TransactionSource;
use std::{collections::HashMap, time::Instant};
use substrate_test_runtime::{AccountId, Extrinsic, Transfer, H256};
use substrate_test_runtime::{AccountId, ExtrinsicBuilder, Transfer, H256};
use substrate_test_runtime_client::AccountKeyring::{Alice, Bob};
const SOURCE: TransactionSource = TransactionSource::External;
@@ -477,7 +478,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -494,7 +495,7 @@ mod tests {
// given
let pool = pool();
let uxt = uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -520,8 +521,8 @@ mod tests {
TestApi::default().into(),
);
// after validation `IncludeData` will be set to non-propagable
let uxt = Extrinsic::IncludeData(vec![42]);
// after validation `IncludeData` will be set to non-propagable (validate_transaction mock)
let uxt = ExtrinsicBuilder::new_include_data(vec![42]).build();
// when
let res = block_on(pool.submit_one(&BlockId::Number(0), SOURCE, uxt));
@@ -542,7 +543,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -553,7 +554,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 1,
@@ -565,7 +566,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 3,
@@ -594,7 +595,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -605,7 +606,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 1,
@@ -616,7 +617,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 3,
@@ -645,7 +646,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -659,27 +660,27 @@ mod tests {
// then
assert!(pool.validated_pool.is_banned(&hash1));
}
use codec::Encode;
#[test]
fn should_limit_futures() {
sp_tracing::try_init_simple();
let xt = uxt(Transfer {
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 1,
});
// given
let limit = Limit { count: 100, total_bytes: 200 };
let limit = Limit { count: 100, total_bytes: xt.encoded_size() };
let options = Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
let pool = Pool::new(options, true.into(), TestApi::default().into());
let hash1 = block_on(pool.submit_one(
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 1,
}),
))
.unwrap();
let hash1 = block_on(pool.submit_one(&BlockId::Number(0), SOURCE, xt)).unwrap();
assert_eq!(pool.validated_pool().status().future, 1);
// when
@@ -687,7 +688,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(2)),
from: Bob.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 10,
@@ -715,7 +716,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 1,
@@ -738,7 +739,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: INVALID_NONCE,
@@ -763,7 +764,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -795,7 +796,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -828,7 +829,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 1,
@@ -843,7 +844,7 @@ mod tests {
&BlockId::Number(0),
SOURCE,
uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -863,7 +864,7 @@ mod tests {
// given
let pool = pool();
let uxt = uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -887,7 +888,7 @@ mod tests {
// given
let pool = pool();
let uxt = uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -918,7 +919,7 @@ mod tests {
let pool = Pool::new(options, true.into(), TestApi::default().into());
let xt = uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -928,7 +929,7 @@ mod tests {
// when
let xt = uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(2)),
from: Bob.into(),
to: AccountId::from_h256(H256::from_low_u64_be(1)),
amount: 4,
nonce: 1,
@@ -952,13 +953,17 @@ mod tests {
let pool = Pool::new(options, true.into(), TestApi::default().into());
let xt = Extrinsic::IncludeData(Vec::new());
// after validation `IncludeData` will have priority set to 9001
// (validate_transaction mock)
let xt = ExtrinsicBuilder::new_include_data(Vec::new()).build();
block_on(pool.submit_one(&BlockId::Number(0), SOURCE, xt)).unwrap();
assert_eq!(pool.validated_pool().status().ready, 1);
// then
// after validation `Transfer` will have priority set to 4 (validate_transaction
// mock)
let xt = uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(2)),
from: Bob.into(),
to: AccountId::from_h256(H256::from_low_u64_be(1)),
amount: 4,
nonce: 1,
@@ -977,12 +982,16 @@ mod tests {
let pool = Pool::new(options, true.into(), TestApi::default().into());
let xt = Extrinsic::IncludeData(Vec::new());
// after validation `IncludeData` will have priority set to 9001
// (validate_transaction mock)
let xt = ExtrinsicBuilder::new_include_data(Vec::new()).build();
block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, xt)).unwrap();
assert_eq!(pool.validated_pool().status().ready, 1);
// after validation `Transfer` will have priority set to 4 (validate_transaction
// mock)
let xt = uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
@@ -992,7 +1001,9 @@ mod tests {
assert_eq!(pool.validated_pool().status().ready, 2);
// when
let xt = Extrinsic::Store(Vec::new());
// after validation `Store` will have priority set to 9001 (validate_transaction
// mock)
let xt = ExtrinsicBuilder::new_indexed_call(Vec::new()).build();
block_on(pool.submit_one(&BlockId::Number(1), SOURCE, xt)).unwrap();
assert_eq!(pool.validated_pool().status().ready, 2);
@@ -1014,7 +1025,7 @@ mod tests {
// when
let xt = uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 1,
@@ -1030,7 +1041,7 @@ mod tests {
// But now before the previous one is imported we import
// the one that it depends on.
let xt = uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 4,
nonce: 0,
@@ -362,6 +362,7 @@ mod tests {
use sc_transaction_pool_api::TransactionSource;
use sp_runtime::generic::BlockId;
use substrate_test_runtime::{AccountId, Transfer, H256};
use substrate_test_runtime_client::AccountKeyring::Alice;
#[test]
fn revalidation_queue_works() {
@@ -370,7 +371,7 @@ mod tests {
let queue = Arc::new(RevalidationQueue::new(api.clone(), pool.clone()));
let uxt = uxt(Transfer {
from: AccountId::from_h256(H256::from_low_u64_be(1)),
from: Alice.into(),
to: AccountId::from_h256(H256::from_low_u64_be(2)),
amount: 5,
nonce: 0,
+18 -8
View File
@@ -31,7 +31,10 @@ use sp_runtime::{
},
};
use std::{collections::HashSet, sync::Arc};
use substrate_test_runtime::{Block, Extrinsic, Hashing, Transfer, H256};
use substrate_test_runtime::{
substrate_test_pallet::pallet::Call as PalletCall, BalancesCall, Block, Extrinsic,
ExtrinsicBuilder, Hashing, RuntimeCall, Transfer, TransferData, H256,
};
pub(crate) const INVALID_NONCE: u64 = 254;
@@ -70,9 +73,11 @@ impl ChainApi for TestApi {
let block_number = self.block_id_to_number(at).unwrap().unwrap();
let res = match uxt {
Extrinsic::Transfer { transfer, .. } => {
let nonce = transfer.nonce;
Extrinsic {
function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { .. }),
..
} => {
let TransferData { nonce, .. } = (&uxt).try_into().unwrap();
// This is used to control the test flow.
if nonce > 0 {
let opt = self.delay.lock().take();
@@ -115,14 +120,20 @@ impl ChainApi for TestApi {
Ok(transaction)
}
},
Extrinsic::IncludeData(_) => Ok(ValidTransaction {
Extrinsic {
function: RuntimeCall::SubstrateTest(PalletCall::include_data { .. }),
..
} => Ok(ValidTransaction {
priority: 9001,
requires: vec![],
provides: vec![vec![42]],
longevity: 9001,
propagate: false,
}),
Extrinsic::Store(_) => Ok(ValidTransaction {
Extrinsic {
function: RuntimeCall::SubstrateTest(PalletCall::indexed_call { .. }),
..
} => Ok(ValidTransaction {
priority: 9001,
requires: vec![],
provides: vec![vec![43]],
@@ -185,8 +196,7 @@ impl ChainApi for TestApi {
}
pub(crate) fn uxt(transfer: Transfer) -> Extrinsic {
let signature = TryFrom::try_from(&[0; 64][..]).unwrap();
Extrinsic::Transfer { transfer, signature, exhaust_resources_when_not_first: false }
ExtrinsicBuilder::new_transfer(transfer).build()
}
pub(crate) fn pool() -> Pool<TestApi> {
+57 -58
View File
@@ -35,11 +35,11 @@ use sp_consensus::BlockOrigin;
use sp_runtime::{
generic::BlockId,
traits::Block as _,
transaction_validity::{InvalidTransaction, TransactionSource, ValidTransaction},
transaction_validity::{TransactionSource, ValidTransaction},
};
use std::{collections::BTreeSet, pin::Pin, sync::Arc};
use substrate_test_runtime_client::{
runtime::{Block, Extrinsic, Hash, Header, Index, Transfer},
runtime::{Block, Extrinsic, ExtrinsicBuilder, Hash, Header, Index, Transfer, TransferData},
AccountKeyring::*,
ClientBlockImportExt,
};
@@ -86,7 +86,11 @@ fn submission_should_work() {
let pool = pool();
block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap();
let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect();
let pending: Vec<_> = pool
.validated_pool()
.ready()
.map(|a| TransferData::try_from(&a.data).unwrap().nonce)
.collect();
assert_eq!(pending, vec![209]);
}
@@ -96,16 +100,25 @@ fn multiple_submission_should_work() {
block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap();
block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 210))).unwrap();
let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect();
let pending: Vec<_> = pool
.validated_pool()
.ready()
.map(|a| TransferData::try_from(&a.data).unwrap().nonce)
.collect();
assert_eq!(pending, vec![209, 210]);
}
#[test]
fn early_nonce_should_be_culled() {
sp_tracing::try_init_simple();
let pool = pool();
block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 208))).unwrap();
let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect();
let pending: Vec<_> = pool
.validated_pool()
.ready()
.map(|a| TransferData::try_from(&a.data).unwrap().nonce)
.collect();
assert_eq!(pending, Vec::<Index>::new());
}
@@ -114,11 +127,19 @@ fn late_nonce_should_be_queued() {
let pool = pool();
block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 210))).unwrap();
let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect();
let pending: Vec<_> = pool
.validated_pool()
.ready()
.map(|a| TransferData::try_from(&a.data).unwrap().nonce)
.collect();
assert_eq!(pending, Vec::<Index>::new());
block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap();
let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect();
let pending: Vec<_> = pool
.validated_pool()
.ready()
.map(|a| TransferData::try_from(&a.data).unwrap().nonce)
.collect();
assert_eq!(pending, vec![209, 210]);
}
@@ -128,14 +149,22 @@ fn prune_tags_should_work() {
let hash209 = block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap();
block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 210))).unwrap();
let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect();
let pending: Vec<_> = pool
.validated_pool()
.ready()
.map(|a| TransferData::try_from(&a.data).unwrap().nonce)
.collect();
assert_eq!(pending, vec![209, 210]);
pool.validated_pool().api().push_block(1, Vec::new(), true);
block_on(pool.prune_tags(&BlockId::number(1), vec![vec![209]], vec![hash209]))
.expect("Prune tags");
let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect();
let pending: Vec<_> = pool
.validated_pool()
.ready()
.map(|a| TransferData::try_from(&a.data).unwrap().nonce)
.collect();
assert_eq!(pending, vec![210]);
}
@@ -148,7 +177,11 @@ fn should_ban_invalid_transactions() {
block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt.clone())).unwrap_err();
// when
let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect();
let pending: Vec<_> = pool
.validated_pool()
.ready()
.map(|a| TransferData::try_from(&a.data).unwrap().nonce)
.collect();
assert_eq!(pending, Vec::<Index>::new());
// then
@@ -197,7 +230,11 @@ fn should_correctly_prune_transactions_providing_more_than_one_tag() {
block_on(pool.submit_one(&BlockId::number(2), SOURCE, xt.clone())).expect("2. Imported");
assert_eq!(pool.validated_pool().status().ready, 1);
assert_eq!(pool.validated_pool().status().future, 1);
let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect();
let pending: Vec<_> = pool
.validated_pool()
.ready()
.map(|a| TransferData::try_from(&a.data).unwrap().nonce)
.collect();
assert_eq!(pending, vec![211]);
// prune it and make sure the pool is empty
@@ -472,6 +509,7 @@ fn finalization() {
#[test]
fn fork_aware_finalization() {
sp_tracing::try_init_simple();
let api = TestApi::empty();
// starting block A1 (last finalized.)
let a_header = api.push_block(1, vec![], true);
@@ -888,48 +926,6 @@ fn ready_set_should_eventually_resolve_when_block_update_arrives() {
}
}
#[test]
fn should_not_accept_old_signatures() {
let client = Arc::new(substrate_test_runtime_client::new());
let best_hash = client.info().best_hash;
let finalized_hash = client.info().finalized_hash;
let pool = Arc::new(
BasicPool::new_test(
Arc::new(FullChainApi::new(client, None, &sp_core::testing::TaskExecutor::new())),
best_hash,
finalized_hash,
)
.0,
);
let transfer = Transfer { from: Alice.into(), to: Bob.into(), nonce: 0, amount: 1 };
let _bytes: sp_core::sr25519::Signature = transfer.using_encoded(|e| Alice.sign(e)).into();
// generated with schnorrkel 0.1.1 from `_bytes`
let old_singature = sp_core::sr25519::Signature::try_from(
&array_bytes::hex2bytes(
"c427eb672e8c441c86d31f1a81b22b43102058e9ce237cabe9897ea5099ffd426\
cd1c6a1f4f2869c3df57901d36bedcb295657adb3a4355add86ed234eb83108",
)
.expect("hex invalid")[..],
)
.expect("signature construction failed");
let xt = Extrinsic::Transfer {
transfer,
signature: old_singature,
exhaust_resources_when_not_first: false,
};
assert_matches::assert_matches!(
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())),
Err(error::Error::Pool(sc_transaction_pool_api::error::Error::InvalidTransaction(
InvalidTransaction::BadProof
))),
"Should be invalid transaction with bad proof",
);
}
#[test]
fn import_notification_to_pool_maintain_works() {
let mut client = Arc::new(substrate_test_runtime_client::new());
@@ -975,7 +971,7 @@ fn import_notification_to_pool_maintain_works() {
fn pruning_a_transaction_should_remove_it_from_best_transaction() {
let (pool, api, _guard) = maintained_pool();
let xt1 = Extrinsic::IncludeData(Vec::new());
let xt1 = ExtrinsicBuilder::new_include_data(Vec::new()).build();
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt1.clone())).expect("1. Imported");
assert_eq!(pool.status().ready, 1);
@@ -1001,7 +997,7 @@ fn stale_transactions_are_pruned() {
let (pool, api, _guard) = maintained_pool();
xts.into_iter().for_each(|xt| {
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.into_signed_tx()))
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.into_unchecked_extrinsic()))
.expect("1. Imported");
});
assert_eq!(pool.status().ready, 0);
@@ -1010,9 +1006,12 @@ fn stale_transactions_are_pruned() {
// Almost the same as our initial transactions, but with some different `amount`s to make them
// generate a different hash
let xts = vec![
Transfer { from: Alice.into(), to: Bob.into(), nonce: 1, amount: 2 }.into_signed_tx(),
Transfer { from: Alice.into(), to: Bob.into(), nonce: 2, amount: 2 }.into_signed_tx(),
Transfer { from: Alice.into(), to: Bob.into(), nonce: 3, amount: 2 }.into_signed_tx(),
Transfer { from: Alice.into(), to: Bob.into(), nonce: 1, amount: 2 }
.into_unchecked_extrinsic(),
Transfer { from: Alice.into(), to: Bob.into(), nonce: 2, amount: 2 }
.into_unchecked_extrinsic(),
Transfer { from: Alice.into(), to: Bob.into(), nonce: 3, amount: 2 }
.into_unchecked_extrinsic(),
];
// Import block
@@ -60,55 +60,6 @@ fn calling_native_runtime_signature_changed_function() {
assert_eq!(runtime_api.function_signature_changed(best_hash).unwrap(), 1);
}
#[test]
fn calling_wasm_runtime_signature_changed_old_function() {
let client = TestClientBuilder::new()
.set_execution_strategy(ExecutionStrategy::AlwaysWasm)
.build();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
#[allow(deprecated)]
let res = runtime_api.function_signature_changed_before_version_2(best_hash).unwrap();
assert_eq!(&res, &[1, 2]);
}
#[test]
fn calling_with_both_strategy_and_fail_on_wasm_should_return_error() {
let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
assert!(runtime_api.fail_on_wasm(best_hash).is_err());
}
#[test]
fn calling_with_both_strategy_and_fail_on_native_should_work() {
let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
assert_eq!(runtime_api.fail_on_native(best_hash).unwrap(), 1);
}
#[test]
fn calling_with_native_else_wasm_and_fail_on_wasm_should_work() {
let client = TestClientBuilder::new()
.set_execution_strategy(ExecutionStrategy::NativeElseWasm)
.build();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
assert_eq!(runtime_api.fail_on_wasm(best_hash).unwrap(), 1);
}
#[test]
fn calling_with_native_else_wasm_and_fail_on_native_should_work() {
let client = TestClientBuilder::new()
.set_execution_strategy(ExecutionStrategy::NativeElseWasm)
.build();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
assert_eq!(runtime_api.fail_on_native(best_hash).unwrap(), 1);
}
#[test]
fn use_trie_function() {
let client = TestClientBuilder::new()
@@ -162,7 +113,7 @@ fn record_proof_works() {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Bob.into(),
}
.into_signed_tx();
.into_unchecked_extrinsic();
// Build the block and record proof
let mut builder = client
+14 -4
View File
@@ -35,6 +35,10 @@ sp-session = { version = "4.0.0-dev", default-features = false, path = "../../pr
sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" }
sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" }
pallet-babe = { version = "4.0.0-dev", default-features = false, path = "../../frame/babe" }
pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../../frame/balances" }
pallet-root-testing = { version = "1.0.0-dev", default-features = false, path = "../../frame/root-testing" }
pallet-sudo = { version = "4.0.0-dev", default-features = false, path = "../../frame/sudo" }
frame-executive = { version = "4.0.0-dev", default-features = false, path = "../../frame/executive" }
frame-system = { version = "4.0.0-dev", default-features = false, path = "../../frame/system" }
frame-system-rpc-runtime-api = { version = "4.0.0-dev", default-features = false, path = "../../frame/system/rpc/runtime-api" }
pallet-timestamp = { version = "4.0.0-dev", default-features = false, path = "../../frame/timestamp" }
@@ -45,18 +49,20 @@ trie-db = { version = "0.27.0", default-features = false }
sc-service = { version = "0.10.0-dev", default-features = false, optional = true, features = ["test-helpers"], path = "../../client/service" }
sp-state-machine = { version = "0.13.0", default-features = false, path = "../../primitives/state-machine" }
sp-externalities = { version = "0.13.0", default-features = false, path = "../../primitives/externalities" }
sp-debug-derive = { path = "../../primitives/debug-derive" }
# 3rd party
cfg-if = "1.0"
array-bytes = { version = "6.1", optional = true }
log = { version = "0.4.17", default-features = false }
serde = { version = "1.0.136", optional = true, features = ["derive"] }
[dev-dependencies]
futures = "0.3.21"
sc-block-builder = { version = "0.10.0-dev", path = "../../client/block-builder" }
sc-executor = { version = "0.10.0-dev", path = "../../client/executor" }
sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" }
substrate-test-runtime-client = { version = "2.0.0", path = "./client" }
futures = "0.3.21"
sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" }
[build-dependencies]
substrate-wasm-builder = { version = "5.0.0-dev", path = "../../utils/wasm-builder", optional = true }
@@ -66,7 +72,7 @@ default = [
"std",
]
std = [
"pallet-beefy-mmr/std",
"array-bytes",
"sp-application-crypto/std",
"sp-consensus-aura/std",
"sp-consensus-babe/std",
@@ -93,9 +99,13 @@ std = [
"sp-externalities/std",
"sp-state-machine/std",
"pallet-babe/std",
"pallet-beefy-mmr/std",
"pallet-timestamp/std",
"pallet-balances/std",
"pallet-sudo/std",
"pallet-root-testing/std",
"frame-system-rpc-runtime-api/std",
"frame-system/std",
"pallet-timestamp/std",
"sc-service",
"sp-consensus-grandpa/std",
"sp-trie/std",
@@ -21,6 +21,7 @@ use sc_client_api::backend;
use sp_api::{ApiExt, ProvideRuntimeApi};
use sc_block_builder::BlockBuilderApi;
use substrate_test_runtime::*;
/// Extension trait for test block builder.
pub trait BlockBuilderExt {
@@ -29,12 +30,19 @@ pub trait BlockBuilderExt {
&mut self,
transfer: substrate_test_runtime::Transfer,
) -> Result<(), sp_blockchain::Error>;
/// Add storage change extrinsic to the block.
/// Add unsigned storage change extrinsic to the block.
fn push_storage_change(
&mut self,
key: Vec<u8>,
value: Option<Vec<u8>>,
) -> Result<(), sp_blockchain::Error>;
/// Adds an extrinsic which pushes DigestItem to header's log
fn push_deposit_log_digest_item(
&mut self,
log: sp_runtime::generic::DigestItem,
) -> Result<(), sp_blockchain::Error>;
}
impl<'a, A, B> BlockBuilderExt
@@ -52,7 +60,7 @@ where
&mut self,
transfer: substrate_test_runtime::Transfer,
) -> Result<(), sp_blockchain::Error> {
self.push(transfer.into_signed_tx())
self.push(transfer.into_unchecked_extrinsic())
}
fn push_storage_change(
@@ -60,6 +68,13 @@ where
key: Vec<u8>,
value: Option<Vec<u8>>,
) -> Result<(), sp_blockchain::Error> {
self.push(substrate_test_runtime::Extrinsic::StorageChange(key, value))
self.push(ExtrinsicBuilder::new_storage_change(key, value).build())
}
fn push_deposit_log_digest_item(
&mut self,
log: sp_runtime::generic::DigestItem,
) -> Result<(), sp_blockchain::Error> {
self.push(ExtrinsicBuilder::new_deposit_log_digest_item(log).build())
}
}
+7 -59
View File
@@ -30,16 +30,9 @@ pub use substrate_test_runtime as runtime;
pub use self::block_builder_ext::BlockBuilderExt;
use sc_chain_spec::construct_genesis_block;
use sp_api::StateVersion;
use sp_core::{
sr25519,
storage::{ChildInfo, Storage, StorageChild},
Pair,
};
use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT};
use sp_core::storage::{ChildInfo, Storage, StorageChild};
use substrate_test_client::sc_executor::WasmExecutor;
use substrate_test_runtime::genesismap::{additional_storage_with_genesis, GenesisConfig};
use substrate_test_runtime::genesismap::GenesisStorageBuilder;
/// A prelude to import in tests.
pub mod prelude {
@@ -92,28 +85,6 @@ pub struct GenesisParameters {
}
impl GenesisParameters {
fn genesis_config(&self) -> GenesisConfig {
GenesisConfig::new(
vec![
sr25519::Public::from(Sr25519Keyring::Alice).into(),
sr25519::Public::from(Sr25519Keyring::Bob).into(),
sr25519::Public::from(Sr25519Keyring::Charlie).into(),
],
(0..16_usize)
.into_iter()
.map(|i| AccountKeyring::numeric(i).public())
.chain(vec![
AccountKeyring::Alice.into(),
AccountKeyring::Bob.into(),
AccountKeyring::Charlie.into(),
])
.collect(),
1000,
self.heap_pages_override,
self.extra_storage.clone(),
)
}
/// Set the wasm code that should be used at genesis.
pub fn set_wasm_code(&mut self, code: Vec<u8>) {
self.wasm_code = Some(code);
@@ -127,34 +98,11 @@ impl GenesisParameters {
impl GenesisInit for GenesisParameters {
fn genesis_storage(&self) -> Storage {
use codec::Encode;
let mut storage = self.genesis_config().genesis_map();
if let Some(ref code) = self.wasm_code {
storage
.top
.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code.clone());
}
let child_roots = storage.children_default.values().map(|child_content| {
let state_root =
<<<runtime::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
child_content.data.clone().into_iter().collect(),
sp_runtime::StateVersion::V1,
);
let prefixed_storage_key = child_content.child_info.prefixed_storage_key();
(prefixed_storage_key.into_inner(), state_root.encode())
});
let state_root =
<<<runtime::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
storage.top.clone().into_iter().chain(child_roots).collect(),
sp_runtime::StateVersion::V1,
);
let block: runtime::Block = construct_genesis_block(state_root, StateVersion::V1);
storage.top.extend(additional_storage_with_genesis(&block));
storage
GenesisStorageBuilder::default()
.with_heap_pages(self.heap_pages_override)
.with_wasm_code(&self.wasm_code)
.with_extra_storage(self.extra_storage.clone())
.build_storage()
}
}
@@ -0,0 +1,207 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Provides utils for building the `Extrinsic` instances used with `substrate-test-runtime`.
use crate::{
substrate_test_pallet::pallet::Call as PalletCall, AccountId, Balance, BalancesCall,
CheckSubstrateCall, Extrinsic, Index, Pair, RuntimeCall, SignedPayload, TransferData,
};
use codec::Encode;
use frame_system::{CheckNonce, CheckWeight};
use sp_core::crypto::Pair as TraitPair;
use sp_keyring::AccountKeyring;
use sp_runtime::{transaction_validity::TransactionPriority, Perbill};
use sp_std::prelude::*;
/// Transfer used in test substrate pallet. Extrinsic is created and signed using this data.
#[derive(Clone)]
pub struct Transfer {
/// Transfer sender and signer of created extrinsic
pub from: Pair,
/// The recipient of the transfer
pub to: AccountId,
/// Amount of transfer
pub amount: Balance,
/// Sender's account nonce at which transfer is valid
pub nonce: u64,
}
impl Transfer {
/// Convert into a signed unchecked extrinsic.
pub fn into_unchecked_extrinsic(self) -> Extrinsic {
ExtrinsicBuilder::new_transfer(self).build()
}
}
impl Default for TransferData {
fn default() -> Self {
Self {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Bob.into(),
amount: 0,
nonce: 0,
}
}
}
/// If feasible converts given `Extrinsic` to `TransferData`
impl TryFrom<&Extrinsic> for TransferData {
type Error = ();
fn try_from(uxt: &Extrinsic) -> Result<Self, Self::Error> {
match uxt {
Extrinsic {
function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }),
signature: Some((from, _, (CheckNonce(nonce), ..))),
} => Ok(TransferData { from: *from, to: *dest, amount: *value, nonce: *nonce }),
Extrinsic {
function: RuntimeCall::SubstrateTest(PalletCall::bench_call { transfer }),
signature: None,
} => Ok(transfer.clone()),
_ => Err(()),
}
}
}
/// Generates `Extrinsic`
pub struct ExtrinsicBuilder {
function: RuntimeCall,
signer: Option<Pair>,
nonce: Option<Index>,
}
impl ExtrinsicBuilder {
/// Create builder for given `RuntimeCall`. By default `Extrinsic` will be signed by `Alice`.
pub fn new(function: impl Into<RuntimeCall>) -> Self {
Self { function: function.into(), signer: Some(AccountKeyring::Alice.pair()), nonce: None }
}
/// Create builder for given `RuntimeCall`. `Extrinsic` will be unsigned.
pub fn new_unsigned(function: impl Into<RuntimeCall>) -> Self {
Self { function: function.into(), signer: None, nonce: None }
}
/// Create builder for `pallet_call::bench_transfer` from given `TransferData`.
pub fn new_bench_call(transfer: TransferData) -> Self {
Self::new_unsigned(PalletCall::bench_call { transfer })
}
/// Create builder for given `Transfer`. Transfer `nonce` will be used as `Extrinsic` nonce.
/// Transfer `from` will be used as Extrinsic signer.
pub fn new_transfer(transfer: Transfer) -> Self {
Self {
nonce: Some(transfer.nonce),
signer: Some(transfer.from.clone()),
..Self::new(BalancesCall::transfer_allow_death {
dest: transfer.to,
value: transfer.amount,
})
}
}
/// Create builder for `PalletCall::include_data` call using given parameters
pub fn new_include_data(data: Vec<u8>) -> Self {
Self::new(PalletCall::include_data { data })
}
/// Create builder for `PalletCall::storage_change` call using given parameters. Will
/// create unsigned Extrinsic.
pub fn new_storage_change(key: Vec<u8>, value: Option<Vec<u8>>) -> Self {
Self::new_unsigned(PalletCall::storage_change { key, value })
}
/// Create builder for `PalletCall::offchain_index_set` call using given parameters
pub fn new_offchain_index_set(key: Vec<u8>, value: Vec<u8>) -> Self {
Self::new(PalletCall::offchain_index_set { key, value })
}
/// Create builder for `PalletCall::offchain_index_clear` call using given parameters
pub fn new_offchain_index_clear(key: Vec<u8>) -> Self {
Self::new(PalletCall::offchain_index_clear { key })
}
/// Create builder for `PalletCall::indexed_call` call using given parameters
pub fn new_indexed_call(data: Vec<u8>) -> Self {
Self::new(PalletCall::indexed_call { data })
}
/// Create builder for `PalletCall::new_deposit_log_digest_item` call using given `log`
pub fn new_deposit_log_digest_item(log: sp_runtime::generic::DigestItem) -> Self {
Self::new_unsigned(PalletCall::deposit_log_digest_item { log })
}
/// Create builder for `PalletCall::Call::new_deposit_log_digest_item`
pub fn new_fill_block(ratio: Perbill) -> Self {
Self::new(PalletCall::fill_block { ratio })
}
/// Create builder for `PalletCall::call_do_not_propagate` call using given parameters
pub fn new_call_do_not_propagate() -> Self {
Self::new(PalletCall::call_do_not_propagate {})
}
/// Create builder for `PalletCall::call_with_priority` call using given parameters
pub fn new_call_with_priority(priority: TransactionPriority) -> Self {
Self::new(PalletCall::call_with_priority { priority })
}
/// Create builder for `PalletCall::read` call using given parameters
pub fn new_read(count: u32) -> Self {
Self::new_unsigned(PalletCall::read { count })
}
/// Create builder for `PalletCall::read` call using given parameters
pub fn new_read_and_panic(count: u32) -> Self {
Self::new_unsigned(PalletCall::read_and_panic { count })
}
/// Unsigned `Extrinsic` will be created
pub fn unsigned(mut self) -> Self {
self.signer = None;
self
}
/// Given `nonce` will be set in `Extrinsic`
pub fn nonce(mut self, nonce: Index) -> Self {
self.nonce = Some(nonce);
self
}
/// Extrinsic will be signed by `signer`
pub fn signer(mut self, signer: Pair) -> Self {
self.signer = Some(signer);
self
}
/// Build `Extrinsic` using embedded parameters
pub fn build(self) -> Extrinsic {
if let Some(signer) = self.signer {
let extra = (
CheckNonce::from(self.nonce.unwrap_or(0)),
CheckWeight::new(),
CheckSubstrateCall {},
);
let raw_payload =
SignedPayload::from_raw(self.function.clone(), extra.clone(), ((), (), ()));
let signature = raw_payload.using_encoded(|e| signer.sign(e));
Extrinsic::new_signed(self.function, signer.public(), signature, extra)
} else {
Extrinsic::new_unsigned(self.function)
}
}
}
+92 -54
View File
@@ -17,75 +17,119 @@
//! Tool for creating the genesis block.
use super::{system, wasm_binary_unwrap, AccountId, AuthorityId, Runtime};
use codec::{Encode, Joiner, KeyedVec};
use frame_support::traits::GenesisBuild;
use super::{
currency, substrate_test_pallet, wasm_binary_unwrap, AccountId, AuthorityId, Balance,
GenesisConfig,
};
use codec::Encode;
use sc_service::construct_genesis_block;
use sp_core::{
map,
sr25519,
storage::{well_known_keys, StateVersion, Storage},
Pair,
};
use sp_keyring::{AccountKeyring, Sr25519Keyring};
use sp_runtime::{
traits::{Block as BlockT, Hash as HashT, Header as HeaderT},
BuildStorage,
};
use sp_io::hashing::{blake2_256, twox_128};
use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT};
use std::collections::BTreeMap;
/// Configuration of a general Substrate test genesis block.
pub struct GenesisConfig {
/// Builder for generating storage from substrate-test-runtime genesis config. Default storage can
/// be extended with additional key-value pairs.
pub struct GenesisStorageBuilder {
authorities: Vec<AuthorityId>,
balances: Vec<(AccountId, u64)>,
heap_pages_override: Option<u64>,
/// Additional storage key pairs that will be added to the genesis map.
extra_storage: Storage,
wasm_code: Option<Vec<u8>>,
}
impl GenesisConfig {
impl Default for GenesisStorageBuilder {
/// Creates a builder with default settings for `substrate_test_runtime`.
fn default() -> Self {
Self::new(
vec![
sr25519::Public::from(Sr25519Keyring::Alice).into(),
sr25519::Public::from(Sr25519Keyring::Bob).into(),
sr25519::Public::from(Sr25519Keyring::Charlie).into(),
],
(0..16_usize)
.into_iter()
.map(|i| AccountKeyring::numeric(i).public())
.chain(vec![
AccountKeyring::Alice.into(),
AccountKeyring::Bob.into(),
AccountKeyring::Charlie.into(),
])
.collect(),
1000 * currency::DOLLARS,
)
}
}
impl GenesisStorageBuilder {
/// Creates a storage builder for genesis config. `substrage test runtime` `GenesisConfig` is
/// initialized with provided `authorities`, `endowed_accounts` with given balance. Key-pairs
/// from `extra_storage` will be injected into built storage. `HEAP_PAGES` key and value will
/// also be placed into storage.
pub fn new(
authorities: Vec<AuthorityId>,
endowed_accounts: Vec<AccountId>,
balance: u64,
heap_pages_override: Option<u64>,
extra_storage: Storage,
balance: Balance,
) -> Self {
GenesisConfig {
GenesisStorageBuilder {
authorities,
balances: endowed_accounts.into_iter().map(|a| (a, balance)).collect(),
heap_pages_override,
extra_storage,
heap_pages_override: None,
extra_storage: Default::default(),
wasm_code: None,
}
}
pub fn genesis_map(&self) -> Storage {
let wasm_runtime = wasm_binary_unwrap().to_vec();
let mut map: BTreeMap<Vec<u8>, Vec<u8>> = self
.balances
.iter()
.map(|&(ref account, balance)| {
(account.to_keyed_vec(b"balance:"), vec![].and(&balance))
})
.map(|(k, v)| (blake2_256(&k[..])[..].to_vec(), v.to_vec()))
.chain(
vec![
(well_known_keys::CODE.into(), wasm_runtime),
(
well_known_keys::HEAP_PAGES.into(),
vec![].and(&(self.heap_pages_override.unwrap_or(16_u64))),
),
]
.into_iter(),
)
.collect();
map.insert(twox_128(&b"sys:auth"[..])[..].to_vec(), self.authorities.encode());
// Add the extra storage entries.
map.extend(self.extra_storage.top.clone().into_iter());
/// Override default wasm code to be placed into GenesisConfig.
pub fn with_wasm_code(mut self, wasm_code: &Option<Vec<u8>>) -> Self {
self.wasm_code = wasm_code.clone();
self
}
// Assimilate the system genesis config.
let mut storage =
Storage { top: map, children_default: self.extra_storage.children_default.clone() };
<system::GenesisConfig as GenesisBuild<Runtime>>::assimilate_storage(
&system::GenesisConfig { authorities: self.authorities.clone() },
&mut storage,
)
.expect("Adding `system::GensisConfig` to the genesis");
pub fn with_heap_pages(mut self, heap_pages_override: Option<u64>) -> Self {
self.heap_pages_override = heap_pages_override;
self
}
pub fn with_extra_storage(mut self, storage: Storage) -> Self {
self.extra_storage = storage;
self
}
/// Builds the `GenesisConfig` and returns its storage.
pub fn build_storage(&mut self) -> Storage {
let genesis_config = GenesisConfig {
system: frame_system::GenesisConfig {
code: self.wasm_code.clone().unwrap_or(wasm_binary_unwrap().to_vec()),
},
babe: pallet_babe::GenesisConfig {
authorities: self.authorities.clone().into_iter().map(|x| (x, 1)).collect(),
epoch_config: Some(crate::TEST_RUNTIME_BABE_EPOCH_CONFIGURATION),
},
substrate_test: substrate_test_pallet::GenesisConfig {
authorities: self.authorities.clone(),
},
balances: pallet_balances::GenesisConfig { balances: self.balances.clone() },
};
let mut storage = genesis_config
.build_storage()
.expect("Build storage from substrate-test-runtime GenesisConfig");
storage.top.insert(
well_known_keys::HEAP_PAGES.into(),
self.heap_pages_override.unwrap_or(16_u64).encode(),
);
storage.top.extend(self.extra_storage.top.clone());
storage.children_default.extend(self.extra_storage.children_default.clone());
storage
}
@@ -108,12 +152,6 @@ pub fn insert_genesis_block(storage: &mut Storage) -> sp_core::hash::H256 {
);
let block: crate::Block = construct_genesis_block(state_root, StateVersion::V1);
let genesis_hash = block.header.hash();
storage.top.extend(additional_storage_with_genesis(&block));
genesis_hash
}
pub fn additional_storage_with_genesis(genesis_block: &crate::Block) -> BTreeMap<Vec<u8>, Vec<u8>> {
map![
twox_128(&b"latest"[..]).to_vec() => genesis_block.hash().as_fixed_bytes().to_vec()
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,244 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # substrate-test pallet
//!
//! Provides functionality used in unit-tests of numerous modules across substrate that require
//! functioning runtime. Some calls are allowed to be submitted as unsigned extrinsics, however most
//! of them requires signing. Refer to `pallet::Call` for further details.
use crate::AuthorityId;
use frame_support::{pallet_prelude::*, storage};
use sp_runtime::transaction_validity::{
InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction,
};
use sp_std::prelude::*;
pub use self::pallet::*;
const LOG_TARGET: &str = "substrate_test_pallet";
#[frame_support::pallet(dev_mode)]
pub mod pallet {
use super::*;
use crate::TransferData;
use frame_system::pallet_prelude::*;
use sp_core::storage::well_known_keys;
use sp_runtime::{transaction_validity::TransactionPriority, Perbill};
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::storage]
#[pallet::getter(fn authorities)]
pub type Authorities<T> = StorageValue<_, Vec<AuthorityId>, ValueQuery>;
#[pallet::genesis_config]
#[cfg_attr(feature = "std", derive(Default))]
pub struct GenesisConfig {
pub authorities: Vec<AuthorityId>,
}
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig {
fn build(&self) {
<Authorities<T>>::put(self.authorities.clone());
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Legacy call used in transaction pool benchmarks.
#[pallet::call_index(0)]
#[pallet::weight(100)]
pub fn bench_call(_origin: OriginFor<T>, _transfer: TransferData) -> DispatchResult {
Ok(())
}
/// Implicitly fill a block body with some data.
#[pallet::call_index(1)]
#[pallet::weight(100)]
pub fn include_data(origin: OriginFor<T>, _data: Vec<u8>) -> DispatchResult {
frame_system::ensure_signed(origin)?;
Ok(())
}
/// Put/delete some data from storage. Intended to use as an unsigned extrinsic.
#[pallet::call_index(2)]
#[pallet::weight(100)]
pub fn storage_change(
_origin: OriginFor<T>,
key: Vec<u8>,
value: Option<Vec<u8>>,
) -> DispatchResult {
match value {
Some(value) => storage::unhashed::put_raw(&key, &value),
None => storage::unhashed::kill(&key),
}
Ok(())
}
/// Write a key value pair to the offchain database.
#[pallet::call_index(3)]
#[pallet::weight(100)]
pub fn offchain_index_set(
origin: OriginFor<T>,
key: Vec<u8>,
value: Vec<u8>,
) -> DispatchResult {
frame_system::ensure_signed(origin)?;
sp_io::offchain_index::set(&key, &value);
Ok(())
}
/// Remove a key and an associated value from the offchain database.
#[pallet::call_index(4)]
#[pallet::weight(100)]
pub fn offchain_index_clear(origin: OriginFor<T>, key: Vec<u8>) -> DispatchResult {
frame_system::ensure_signed(origin)?;
sp_io::offchain_index::clear(&key);
Ok(())
}
/// Create an index for this call.
#[pallet::call_index(5)]
#[pallet::weight(100)]
pub fn indexed_call(origin: OriginFor<T>, data: Vec<u8>) -> DispatchResult {
frame_system::ensure_signed(origin)?;
let content_hash = sp_io::hashing::blake2_256(&data);
let extrinsic_index: u32 =
storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX).unwrap();
sp_io::transaction_index::index(extrinsic_index, data.len() as u32, content_hash);
Ok(())
}
/// Deposit given digest items into the system storage. They will be included in a header
/// during finalization.
#[pallet::call_index(6)]
#[pallet::weight(100)]
pub fn deposit_log_digest_item(
_origin: OriginFor<T>,
log: sp_runtime::generic::DigestItem,
) -> DispatchResult {
<frame_system::Pallet<T>>::deposit_log(log);
Ok(())
}
/// This call is validated as `ValidTransaction` with given priority.
#[pallet::call_index(7)]
#[pallet::weight(100)]
pub fn call_with_priority(
_origin: OriginFor<T>,
_priority: TransactionPriority,
) -> DispatchResult {
Ok(())
}
/// This call is validated as non-propagable `ValidTransaction`.
#[pallet::call_index(8)]
#[pallet::weight(100)]
pub fn call_do_not_propagate(_origin: OriginFor<T>) -> DispatchResult {
Ok(())
}
/// Fill the block weight up to the given ratio.
#[pallet::call_index(9)]
#[pallet::weight(*_ratio * T::BlockWeights::get().max_block)]
pub fn fill_block(origin: OriginFor<T>, _ratio: Perbill) -> DispatchResult {
ensure_signed(origin)?;
Ok(())
}
/// Read X times from the state some data.
///
/// Panics if it can not read `X` times.
#[pallet::call_index(10)]
#[pallet::weight(100)]
pub fn read(_origin: OriginFor<T>, count: u32) -> DispatchResult {
Self::execute_read(count, false)
}
/// Read X times from the state some data and then panic!
///
/// Returns `Ok` if it didn't read anything.
#[pallet::call_index(11)]
#[pallet::weight(100)]
pub fn read_and_panic(_origin: OriginFor<T>, count: u32) -> DispatchResult {
Self::execute_read(count, true)
}
}
impl<T: Config> Pallet<T> {
fn execute_read(read: u32, panic_at_end: bool) -> DispatchResult {
let mut next_key = vec![];
for _ in 0..(read as usize) {
if let Some(next) = sp_io::storage::next_key(&next_key) {
// Read the value
sp_io::storage::get(&next);
next_key = next;
} else {
if panic_at_end {
return Ok(())
} else {
panic!("Could not read {read} times from the state");
}
}
}
if panic_at_end {
panic!("BYE")
} else {
Ok(())
}
}
}
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
log::trace!(target: LOG_TARGET, "validate_unsigned {call:?}");
match call {
// Some tests do not need to be complicated with signer and nonce, some need
// reproducible block hash (call signature can't be there).
// Offchain testing requires storage_change.
Call::deposit_log_digest_item { .. } |
Call::storage_change { .. } |
Call::read { .. } |
Call::read_and_panic { .. } => Ok(Default::default()),
_ => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
}
}
}
}
pub fn validate_runtime_call<T: pallet::Config>(call: &pallet::Call<T>) -> TransactionValidity {
log::trace!(target: LOG_TARGET, "validate_runtime_call {call:?}");
match call {
Call::call_do_not_propagate {} =>
Ok(ValidTransaction { propagate: false, ..Default::default() }),
Call::call_with_priority { priority } =>
Ok(ValidTransaction { priority: *priority, ..Default::default() }),
_ => Ok(Default::default()),
}
}
-587
View File
@@ -1,587 +0,0 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code
//! and depositing logs.
use crate::{
AccountId, AuthorityId, Block, BlockNumber, Digest, Extrinsic, Header, Runtime, Transfer,
H256 as Hash,
};
use codec::{Decode, Encode, KeyedVec};
use frame_support::storage;
use sp_core::storage::well_known_keys;
use sp_io::{hashing::blake2_256, storage::root as storage_root, trie};
use sp_runtime::{
generic,
traits::Header as _,
transaction_validity::{
InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,
},
ApplyExtrinsicResult,
};
use sp_std::prelude::*;
const NONCE_OF: &[u8] = b"nonce:";
const BALANCE_OF: &[u8] = b"balance:";
pub use self::pallet::*;
#[frame_support::pallet]
mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::storage]
pub type ExtrinsicData<T> = StorageMap<_, Blake2_128Concat, u32, Vec<u8>, ValueQuery>;
// The current block number being processed. Set by `execute_block`.
#[pallet::storage]
pub type Number<T> = StorageValue<_, BlockNumber, OptionQuery>;
#[pallet::storage]
pub type ParentHash<T> = StorageValue<_, Hash, ValueQuery>;
#[pallet::storage]
pub type NewAuthorities<T> = StorageValue<_, Vec<AuthorityId>, OptionQuery>;
#[pallet::storage]
pub type StorageDigest<T> = StorageValue<_, Digest, OptionQuery>;
#[pallet::storage]
pub type Authorities<T> = StorageValue<_, Vec<AuthorityId>, ValueQuery>;
#[pallet::genesis_config]
#[cfg_attr(feature = "std", derive(Default))]
pub struct GenesisConfig {
pub authorities: Vec<AuthorityId>,
}
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig {
fn build(&self) {
<Authorities<T>>::put(self.authorities.clone());
}
}
}
pub fn balance_of_key(who: AccountId) -> Vec<u8> {
who.to_keyed_vec(BALANCE_OF)
}
pub fn balance_of(who: AccountId) -> u64 {
storage::hashed::get_or(&blake2_256, &balance_of_key(who), 0)
}
pub fn nonce_of(who: AccountId) -> u64 {
storage::hashed::get_or(&blake2_256, &who.to_keyed_vec(NONCE_OF), 0)
}
pub fn initialize_block(header: &Header) {
// populate environment.
<Number<Runtime>>::put(&header.number);
<ParentHash<Runtime>>::put(&header.parent_hash);
<StorageDigest<Runtime>>::put(header.digest());
storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &0u32);
// try to read something that depends on current header digest
// so that it'll be included in execution proof
if let Some(generic::DigestItem::Other(v)) = header.digest().logs().iter().next() {
let _: Option<u32> = storage::unhashed::get(v);
}
}
pub fn authorities() -> Vec<AuthorityId> {
<Authorities<Runtime>>::get()
}
pub fn get_block_number() -> Option<BlockNumber> {
<Number<Runtime>>::get()
}
pub fn take_block_number() -> Option<BlockNumber> {
<Number<Runtime>>::take()
}
#[derive(Copy, Clone)]
enum Mode {
Verify,
Overwrite,
}
/// Actually execute all transitioning for `block`.
pub fn polish_block(block: &mut Block) {
execute_block_with_state_root_handler(block, Mode::Overwrite);
}
pub fn execute_block(mut block: Block) -> Header {
execute_block_with_state_root_handler(&mut block, Mode::Verify)
}
fn execute_block_with_state_root_handler(block: &mut Block, mode: Mode) -> Header {
let header = &mut block.header;
initialize_block(header);
// execute transactions
block.extrinsics.iter().for_each(|e| {
let _ = execute_transaction(e.clone()).unwrap_or_else(|_| panic!("Invalid transaction"));
});
let new_header = finalize_block();
if let Mode::Overwrite = mode {
header.state_root = new_header.state_root;
} else {
info_expect_equal_hash(&new_header.state_root, &header.state_root);
assert_eq!(
new_header.state_root, header.state_root,
"Storage root must match that calculated.",
);
}
if let Mode::Overwrite = mode {
header.extrinsics_root = new_header.extrinsics_root;
} else {
info_expect_equal_hash(&new_header.extrinsics_root, &header.extrinsics_root);
assert_eq!(
new_header.extrinsics_root, header.extrinsics_root,
"Transaction trie root must be valid.",
);
}
new_header
}
/// The block executor.
pub struct BlockExecutor;
impl frame_support::traits::ExecuteBlock<Block> for BlockExecutor {
fn execute_block(block: Block) {
execute_block(block);
}
}
/// Execute a transaction outside of the block execution function.
/// This doesn't attempt to validate anything regarding the block.
pub fn validate_transaction(utx: Extrinsic) -> TransactionValidity {
if check_signature(&utx).is_err() {
return InvalidTransaction::BadProof.into()
}
let tx = utx.transfer();
let nonce_key = tx.from.to_keyed_vec(NONCE_OF);
let expected_nonce: u64 = storage::hashed::get_or(&blake2_256, &nonce_key, 0);
if tx.nonce < expected_nonce {
return InvalidTransaction::Stale.into()
}
if tx.nonce > expected_nonce + 64 {
return InvalidTransaction::Future.into()
}
let encode = |from: &AccountId, nonce: u64| (from, nonce).encode();
let requires = if tx.nonce != expected_nonce && tx.nonce > 0 {
vec![encode(&tx.from, tx.nonce - 1)]
} else {
vec![]
};
let provides = vec![encode(&tx.from, tx.nonce)];
Ok(ValidTransaction { priority: tx.amount, requires, provides, longevity: 64, propagate: true })
}
/// Execute a transaction outside of the block execution function.
/// This doesn't attempt to validate anything regarding the block.
pub fn execute_transaction(utx: Extrinsic) -> ApplyExtrinsicResult {
let extrinsic_index: u32 =
storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX).unwrap_or_default();
let result = execute_transaction_backend(&utx, extrinsic_index);
<ExtrinsicData<Runtime>>::insert(extrinsic_index, utx.encode());
storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &(extrinsic_index + 1));
result
}
/// Finalize the block.
pub fn finalize_block() -> Header {
use sp_core::storage::StateVersion;
let extrinsic_index: u32 = storage::unhashed::take(well_known_keys::EXTRINSIC_INDEX).unwrap();
let txs: Vec<_> = (0..extrinsic_index).map(<ExtrinsicData<Runtime>>::take).collect();
let extrinsics_root = trie::blake2_256_ordered_root(txs, StateVersion::V0);
let number = <Number<Runtime>>::take().expect("Number is set by `initialize_block`");
let parent_hash = <ParentHash<Runtime>>::take();
let mut digest =
<StorageDigest<Runtime>>::take().expect("StorageDigest is set by `initialize_block`");
let o_new_authorities = <NewAuthorities<Runtime>>::take();
// This MUST come after all changes to storage are done. Otherwise we will fail the
// “Storage root does not match that calculated” assertion.
let storage_root = Hash::decode(&mut &storage_root(StateVersion::V1)[..])
.expect("`storage_root` is a valid hash");
if let Some(new_authorities) = o_new_authorities {
digest.push(generic::DigestItem::Consensus(*b"aura", new_authorities.encode()));
digest.push(generic::DigestItem::Consensus(*b"babe", new_authorities.encode()));
}
Header { number, extrinsics_root, state_root: storage_root, parent_hash, digest }
}
#[inline(always)]
fn check_signature(utx: &Extrinsic) -> Result<(), TransactionValidityError> {
use sp_runtime::traits::BlindCheckable;
utx.clone().check().map_err(|_| InvalidTransaction::BadProof.into()).map(|_| ())
}
fn execute_transaction_backend(utx: &Extrinsic, extrinsic_index: u32) -> ApplyExtrinsicResult {
check_signature(utx)?;
match utx {
Extrinsic::Transfer { exhaust_resources_when_not_first: true, .. }
if extrinsic_index != 0 =>
Err(InvalidTransaction::ExhaustsResources.into()),
Extrinsic::Transfer { ref transfer, .. } => execute_transfer_backend(transfer),
Extrinsic::AuthoritiesChange(ref new_auth) => execute_new_authorities_backend(new_auth),
Extrinsic::IncludeData(_) => Ok(Ok(())),
Extrinsic::StorageChange(key, value) =>
execute_storage_change(key, value.as_ref().map(|v| &**v)),
Extrinsic::OffchainIndexSet(key, value) => {
sp_io::offchain_index::set(key, value);
Ok(Ok(()))
},
Extrinsic::OffchainIndexClear(key) => {
sp_io::offchain_index::clear(key);
Ok(Ok(()))
},
Extrinsic::Store(data) => execute_store(data.clone()),
Extrinsic::ReadAndPanic(i) => execute_read(*i, true),
Extrinsic::Read(i) => execute_read(*i, false),
}
}
fn execute_read(read: u32, panic_at_end: bool) -> ApplyExtrinsicResult {
let mut next_key = vec![];
for _ in 0..(read as usize) {
if let Some(next) = sp_io::storage::next_key(&next_key) {
// Read the value
sp_io::storage::get(&next);
next_key = next;
} else {
if panic_at_end {
return Ok(Ok(()))
} else {
panic!("Could not read {read} times from the state");
}
}
}
if panic_at_end {
panic!("BYE")
} else {
Ok(Ok(()))
}
}
fn execute_transfer_backend(tx: &Transfer) -> ApplyExtrinsicResult {
// check nonce
let nonce_key = tx.from.to_keyed_vec(NONCE_OF);
let expected_nonce: u64 = storage::hashed::get_or(&blake2_256, &nonce_key, 0);
if tx.nonce != expected_nonce {
return Err(InvalidTransaction::Stale.into())
}
// increment nonce in storage
storage::hashed::put(&blake2_256, &nonce_key, &(expected_nonce + 1));
// check sender balance
let from_balance_key = tx.from.to_keyed_vec(BALANCE_OF);
let from_balance: u64 = storage::hashed::get_or(&blake2_256, &from_balance_key, 0);
// enact transfer
if tx.amount > from_balance {
return Err(InvalidTransaction::Payment.into())
}
let to_balance_key = tx.to.to_keyed_vec(BALANCE_OF);
let to_balance: u64 = storage::hashed::get_or(&blake2_256, &to_balance_key, 0);
storage::hashed::put(&blake2_256, &from_balance_key, &(from_balance - tx.amount));
storage::hashed::put(&blake2_256, &to_balance_key, &(to_balance + tx.amount));
Ok(Ok(()))
}
fn execute_store(data: Vec<u8>) -> ApplyExtrinsicResult {
let content_hash = sp_io::hashing::blake2_256(&data);
let extrinsic_index: u32 = storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX).unwrap();
sp_io::transaction_index::index(extrinsic_index, data.len() as u32, content_hash);
Ok(Ok(()))
}
fn execute_new_authorities_backend(new_authorities: &[AuthorityId]) -> ApplyExtrinsicResult {
<NewAuthorities<Runtime>>::put(new_authorities.to_vec());
Ok(Ok(()))
}
fn execute_storage_change(key: &[u8], value: Option<&[u8]>) -> ApplyExtrinsicResult {
match value {
Some(value) => storage::unhashed::put_raw(key, value),
None => storage::unhashed::kill(key),
}
Ok(Ok(()))
}
#[cfg(feature = "std")]
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
use sp_core::hexdisplay::HexDisplay;
if given != expected {
println!(
"Hash: given={}, expected={}",
HexDisplay::from(given.as_fixed_bytes()),
HexDisplay::from(expected.as_fixed_bytes()),
);
}
}
#[cfg(not(feature = "std"))]
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
if given != expected {
sp_runtime::print("Hash not equal");
sp_runtime::print(given.as_bytes());
sp_runtime::print(expected.as_bytes());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{wasm_binary_unwrap, Header, Transfer};
use sc_executor::{NativeElseWasmExecutor, WasmExecutor};
use sp_core::{
map,
traits::{CallContext, CodeExecutor, RuntimeCode},
};
use sp_io::{hashing::twox_128, TestExternalities};
use substrate_test_runtime_client::{AccountKeyring, Sr25519Keyring};
// Declare an instance of the native executor dispatch for the test runtime.
pub struct NativeDispatch;
impl sc_executor::NativeExecutionDispatch for NativeDispatch {
type ExtendHostFunctions = ();
fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> {
crate::api::dispatch(method, data)
}
fn native_version() -> sc_executor::NativeVersion {
crate::native_version()
}
}
fn executor() -> NativeElseWasmExecutor<NativeDispatch> {
NativeElseWasmExecutor::new_with_wasm_executor(WasmExecutor::builder().build())
}
fn new_test_ext() -> TestExternalities {
let authorities = vec![
Sr25519Keyring::Alice.to_raw_public(),
Sr25519Keyring::Bob.to_raw_public(),
Sr25519Keyring::Charlie.to_raw_public(),
];
TestExternalities::new_with_code(
wasm_binary_unwrap(),
sp_core::storage::Storage {
top: map![
twox_128(b"latest").to_vec() => vec![69u8; 32],
twox_128(b"sys:auth").to_vec() => authorities.encode(),
blake2_256(&AccountKeyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => {
vec![111u8, 0, 0, 0, 0, 0, 0, 0]
},
],
children_default: map![],
},
)
}
fn block_import_works<F>(block_executor: F)
where
F: Fn(Block, &mut TestExternalities),
{
let h = Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: Default::default(),
extrinsics_root: Default::default(),
digest: Default::default(),
};
let mut b = Block { header: h, extrinsics: vec![] };
new_test_ext().execute_with(|| polish_block(&mut b));
block_executor(b, &mut new_test_ext());
}
#[test]
fn block_import_works_native() {
block_import_works(|b, ext| {
ext.execute_with(|| {
execute_block(b);
})
});
}
#[test]
fn block_import_works_wasm() {
block_import_works(|b, ext| {
let mut ext = ext.ext();
let runtime_code = RuntimeCode {
code_fetcher: &sp_core::traits::WrappedRuntimeCode(wasm_binary_unwrap().into()),
hash: Vec::new(),
heap_pages: None,
};
executor()
.call(
&mut ext,
&runtime_code,
"Core_execute_block",
&b.encode(),
false,
CallContext::Offchain,
)
.0
.unwrap();
})
}
fn block_import_with_transaction_works<F>(block_executor: F)
where
F: Fn(Block, &mut TestExternalities),
{
let mut b1 = Block {
header: Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: Default::default(),
extrinsics_root: Default::default(),
digest: Default::default(),
},
extrinsics: vec![Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Bob.into(),
amount: 69,
nonce: 0,
}
.into_signed_tx()],
};
let mut dummy_ext = new_test_ext();
dummy_ext.execute_with(|| polish_block(&mut b1));
let mut b2 = Block {
header: Header {
parent_hash: b1.header.hash(),
number: 2,
state_root: Default::default(),
extrinsics_root: Default::default(),
digest: Default::default(),
},
extrinsics: vec![
Transfer {
from: AccountKeyring::Bob.into(),
to: AccountKeyring::Alice.into(),
amount: 27,
nonce: 0,
}
.into_signed_tx(),
Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Charlie.into(),
amount: 69,
nonce: 1,
}
.into_signed_tx(),
],
};
dummy_ext.execute_with(|| polish_block(&mut b2));
drop(dummy_ext);
let mut t = new_test_ext();
t.execute_with(|| {
assert_eq!(balance_of(AccountKeyring::Alice.into()), 111);
assert_eq!(balance_of(AccountKeyring::Bob.into()), 0);
});
block_executor(b1, &mut t);
t.execute_with(|| {
assert_eq!(balance_of(AccountKeyring::Alice.into()), 42);
assert_eq!(balance_of(AccountKeyring::Bob.into()), 69);
});
block_executor(b2, &mut t);
t.execute_with(|| {
assert_eq!(balance_of(AccountKeyring::Alice.into()), 0);
assert_eq!(balance_of(AccountKeyring::Bob.into()), 42);
assert_eq!(balance_of(AccountKeyring::Charlie.into()), 69);
});
}
#[test]
fn block_import_with_transaction_works_native() {
block_import_with_transaction_works(|b, ext| {
ext.execute_with(|| {
execute_block(b);
})
});
}
#[test]
fn block_import_with_transaction_works_wasm() {
block_import_with_transaction_works(|b, ext| {
let mut ext = ext.ext();
let runtime_code = RuntimeCode {
code_fetcher: &sp_core::traits::WrappedRuntimeCode(wasm_binary_unwrap().into()),
hash: Vec::new(),
heap_pages: None,
};
executor()
.call(
&mut ext,
&runtime_code,
"Core_execute_block",
&b.encode(),
false,
CallContext::Offchain,
)
.0
.unwrap();
})
}
}
@@ -35,7 +35,10 @@ use sp_runtime::{
};
use std::collections::{BTreeMap, HashMap, HashSet};
use substrate_test_runtime_client::{
runtime::{AccountId, Block, BlockNumber, Extrinsic, Hash, Header, Index, Transfer},
runtime::{
AccountId, Block, BlockNumber, Extrinsic, ExtrinsicBuilder, Hash, Header, Index, Transfer,
TransferData,
},
AccountKeyring::{self, *},
};
@@ -276,7 +279,7 @@ impl sc_transaction_pool::ChainApi for TestApi {
Err(e) => return ready(Err(e)),
}
let (requires, provides) = if let Some(transfer) = uxt.try_transfer() {
let (requires, provides) = if let Ok(transfer) = TransferData::try_from(&uxt) {
let chain_nonce = self.chain.read().nonces.get(&transfer.from).cloned().unwrap_or(0);
let requires =
if chain_nonce == transfer.nonce { vec![] } else { vec![vec![chain_nonce as u8]] };
@@ -377,6 +380,5 @@ impl sp_blockchain::HeaderMetadata<Block> for TestApi {
pub fn uxt(who: AccountKeyring, nonce: Index) -> Extrinsic {
let dummy = codec::Decode::decode(&mut TrailingZeroInput::zeroes()).unwrap();
let transfer = Transfer { from: who.into(), to: dummy, nonce, amount: 1 };
let signature = transfer.using_encoded(|e| who.sign(e));
Extrinsic::Transfer { transfer, signature, exhaust_resources_when_not_first: false }
ExtrinsicBuilder::new_transfer(transfer).build()
}
+4 -4
View File
@@ -243,7 +243,7 @@ mod tests {
amount: 5,
nonce,
};
t.into_signed_tx()
t.into_unchecked_extrinsic()
};
// Populate the pool
let ext0 = new_transaction(0);
@@ -297,7 +297,7 @@ mod tests {
amount: 5,
nonce: 0,
}
.into_signed_tx();
.into_unchecked_extrinsic();
// when
let bytes = accounts.dry_run(tx.encode().into(), None).await.expect("Call is successful");
@@ -325,13 +325,13 @@ mod tests {
amount: 5,
nonce: 100,
}
.into_signed_tx();
.into_unchecked_extrinsic();
// when
let bytes = accounts.dry_run(tx.encode().into(), None).await.expect("Call is successful");
// then
let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap();
assert_eq!(apply_res, Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)));
assert_eq!(apply_res, Err(TransactionValidityError::Invalid(InvalidTransaction::Future)));
}
}