Files
pezkuwi-subxt/polkadot/node/test/polkadot-simnet/common/src/lib.rs
T
Andrew Jones 4c7539cab5 Companion for #8615: enrich metadata with type information (#3336)
* Use beefy branch with scale-info

* Add patches

* Sprinkle some TypeInfo derives

* Add some TypeInfo deriv

* Cargo.lock

* Derive TypeInfo and skip type params for Xcm types

* Cargo.lock

* Fix up scale_info bounds attributes

* Fix up dependencies

* Use my own beefy-primitives branch

* Bump BEEFY

* Update patches

* Add some scale-info dependencies and TypeInfo derives

* More TypeInfo decoration

* Update scale-info

* Some TypeInfos and remove more Event pallet::metadata

* Moar TypeInfos

* TypeInfos galore, fix up metadata runtime API

* TypeInfo

* TypeInfos, update other runtime metadata APIs

* Fix up Kusama, comment out some `usize` QueueSize parameter types

* Remove local diener patches

* Cargo.lock

* Cargo.lock

* Update to scale-info crates.io release

* Update primitive-types branch

* Update pallet-beefy to use custom branch

* Update other parity-common deps

* Update parity-common patches

* bump a bunch of deps in parity-common

* Remove parity-common patches

* Bump finality-grandpa version

* Cargo.lock

* Update scale-info to 0.9.1

* Add recursion_limit for runtime-parachains

* Add some scale_info attributes

* Cargo.lock

* Revert finality-grandpa bump

* Cargo.lock, scale-info update

* cargo update

* Make sure using patched version of finality-grandpa

* Use patched scale-info

* Update to scale-info 0.10.0

* Update finality-grandpa

* Cargo.lock

* Update beefy deps

* Update beefy deps again

* Add scale-info dependency

* Remove deprecated pallet::metadata attributes.

* Add some missing scale-info deps and derives

* Use some variant struct call syntax

* Add missing TypeInfo impl

* Add some more TypeInfo impls

* Convert some call enum struct variant constructors

* More scale-info deps and derives

* Call enum struct variants

* TypeInfo derives

* Call enum variant structs

* scale-info deps and derives

* Call enum variant struct constructors

* Use beefy-primitives scale-info feature

* Use grandpa-bridge-gadget master branch

* Remove finality-grandpa patch

* Add missing scale_info dependency and derive

* Fix up some call variant constructors

* Add missing scale_info dependency

* Fix some test errors

* More TypeInfo derives

* More call variant structs

* Call variant structs in tests

* Cargo.lock

* Fmt

* Fix more call struct variants

* Another call struct variant

* add scale-info/std features explicitly

* More call struct variants

* Add missing scale-info dependency

* Fmt

* review: activate scale-info/std where missing

* Remove some duplicate std feature activation

* review: add scale_info bounds() attr

* More call variant structs

* Remove recursion limit

* Update beefy-primitives

* Update beefy-primitives

* Fix simnet call variant struct errors

* Fmt

* cargo update -p beefy-primitives

* Add some missing TypeInfo derives

* Fix some call variants

* Fix some call variant underscores

* Cargo.lock

* Cargo.lock

* Add missing TypeInfo derive

* Add some more missing TypeInfo derives

* Even more missing TypeInfo derives

* Add TypeInfo derives to new xcm types

* Fmt

* Cargo.lock

* Add missing TypeInfo impls

* Cargo.lock

* More missing TypeInfos

* Fixes

* Cargo.lock

* Cargo.lock

* Add TypeInfo impls to xcm v2

* Update to scale-info 1.0

* Update finality-grandpa 0.14.4, patch for now

* Update beefy

* Remove patched finality-grandpa

* Add TypeInfo impl to Outcome

* Fixes

* Call variant struct

* Call variant struct

* Fix test

* Add TypeInfo impl

* Cargo.lock

* Cargo.lock

* Cargo.lock

* git checkout master Cargo.lock

* update Substrate

* Add missing scale-info features for beefy-primitives

* Fmt

* Remove check for now

* Update beefy-primitives, removes scale-info feature

* Update beefy-primitives again

Co-authored-by: adoerr <0xad@gmx.net>
Co-authored-by: Andronik Ordian <write@reusable.software>
Co-authored-by: thiolliere <gui.thiolliere@gmail.com>
Co-authored-by: parity-processbot <>
Co-authored-by: Bastian Köcher <info@kchr.de>
2021-09-15 15:38:45 -05:00

436 lines
13 KiB
Rust

// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
#![deny(unused_extern_crates, missing_docs)]
//! Utilities for End to end runtime tests
use codec::Encode;
use democracy::{AccountVote, Conviction, Vote};
use grandpa::GrandpaBlockImport;
use polkadot_runtime::{
CouncilCollective, Event, FastTrackVotingPeriod, Runtime, RuntimeApi, TechnicalCollective,
};
use polkadot_runtime_common::claims;
use sc_consensus_babe::BabeBlockImport;
use sc_consensus_manual_seal::consensus::babe::SlotTimestampProvider;
use sc_executor::NativeElseWasmExecutor;
use sc_service::{TFullBackend, TFullClient};
use sp_runtime::{app_crypto::sp_core::H256, generic::Era, AccountId32};
use std::{error::Error, future::Future, str::FromStr};
use support::weights::Weight;
use test_runner::{
build_runtime, client_parts, ChainInfo, ConfigOrChainSpec, Node, SignatureVerificationOverride,
};
type BlockImport<B, BE, C, SC> = BabeBlockImport<B, C, GrandpaBlockImport<BE, B, C, SC>>;
type Block = polkadot_primitives::v1::Block;
type SelectChain = sc_consensus::LongestChain<TFullBackend<Block>, Block>;
/// Declare an instance of the native executor named `ExecutorDispatch`. Include the wasm binary as the
/// equivalent wasm code.
pub struct ExecutorDispatch;
impl sc_executor::NativeExecutionDispatch for ExecutorDispatch {
type ExtendHostFunctions =
(benchmarking::benchmarking::HostFunctions, SignatureVerificationOverride);
fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> {
polkadot_runtime::api::dispatch(method, data)
}
fn native_version() -> sc_executor::NativeVersion {
polkadot_runtime::native_version()
}
}
/// `ChainInfo` implementation.
pub struct PolkadotChainInfo;
impl ChainInfo for PolkadotChainInfo {
type Block = Block;
type ExecutorDispatch = ExecutorDispatch;
type Runtime = Runtime;
type RuntimeApi = RuntimeApi;
type SelectChain = SelectChain;
type BlockImport = BlockImport<
Self::Block,
TFullBackend<Self::Block>,
TFullClient<Self::Block, RuntimeApi, NativeElseWasmExecutor<ExecutorDispatch>>,
Self::SelectChain,
>;
type SignedExtras = polkadot_runtime::SignedExtra;
type InherentDataProviders =
(SlotTimestampProvider, sp_consensus_babe::inherents::InherentDataProvider);
fn signed_extras(from: <Runtime as system::Config>::AccountId) -> Self::SignedExtras {
(
system::CheckSpecVersion::<Runtime>::new(),
system::CheckTxVersion::<Runtime>::new(),
system::CheckGenesis::<Runtime>::new(),
system::CheckMortality::<Runtime>::from(Era::Immortal),
system::CheckNonce::<Runtime>::from(system::Pallet::<Runtime>::account_nonce(from)),
system::CheckWeight::<Runtime>::new(),
transaction_payment::ChargeTransactionPayment::<Runtime>::from(0),
claims::PrevalidateAttests::<Runtime>::new(),
)
}
}
/// Dispatch with root origin, via pallet-democracy
pub async fn dispatch_with_root<T>(
call: impl Into<<T::Runtime as system::Config>::Call>,
node: &Node<T>,
) -> Result<(), Box<dyn Error>>
where
T: ChainInfo<
Block = Block,
ExecutorDispatch = ExecutorDispatch,
Runtime = Runtime,
RuntimeApi = RuntimeApi,
SelectChain = SelectChain,
BlockImport = BlockImport<
Block,
TFullBackend<Block>,
TFullClient<Block, RuntimeApi, NativeElseWasmExecutor<ExecutorDispatch>>,
SelectChain,
>,
SignedExtras = polkadot_runtime::SignedExtra,
>,
{
type DemocracyCall = democracy::Call<Runtime>;
type CouncilCollectiveEvent = collective::Event<Runtime, CouncilCollective>;
type CouncilCollectiveCall = collective::Call<Runtime, CouncilCollective>;
type TechnicalCollectiveCall = collective::Call<Runtime, TechnicalCollective>;
type TechnicalCollectiveEvent = collective::Event<Runtime, TechnicalCollective>;
// here lies a black mirror esque copy of on chain whales.
let whales = vec![
"1rvXMZpAj9nKLQkPFCymyH7Fg3ZyKJhJbrc7UtHbTVhJm1A",
"15j4dg5GzsL1bw2U2AWgeyAk6QTxq43V7ZPbXdAmbVLjvDCK",
]
.into_iter()
.map(|account| AccountId32::from_str(account).unwrap())
.collect::<Vec<_>>();
// and these
let (technical_collective, council_collective) = node.with_state(|| {
(
collective::Members::<Runtime, TechnicalCollective>::get(),
collective::Members::<Runtime, CouncilCollective>::get(),
)
});
// hash of the proposal in democracy
let proposal_hash = {
// note the call (pre-image?) of the call.
node.submit_extrinsic(
DemocracyCall::note_preimage { encoded_proposal: call.into().encode() },
Some(whales[0].clone()),
)
.await?;
node.seal_blocks(1).await;
// fetch proposal hash from event emitted by the runtime
let events = node.events();
events
.iter()
.filter_map(|event| match event.event {
Event::Democracy(democracy::Event::PreimageNoted(ref proposal_hash, _, _)) =>
Some(proposal_hash.clone()),
_ => None,
})
.next()
.ok_or_else(|| {
format!("democracy::Event::PreimageNoted not found in events: {:#?}", events)
})?
};
// submit external_propose call through council collective
{
let external_propose = DemocracyCall::external_propose_majority {
proposal_hash: proposal_hash.clone().into(),
};
let length = external_propose.using_encoded(|x| x.len()) as u32 + 1;
let weight = Weight::MAX / 100_000_000;
let proposal = CouncilCollectiveCall::propose {
threshold: council_collective.len() as u32,
proposal: Box::new(external_propose.clone().into()),
length_bound: length,
};
node.submit_extrinsic(proposal.clone(), Some(council_collective[0].clone()))
.await?;
node.seal_blocks(1).await;
// fetch proposal index from event emitted by the runtime
let events = node.events();
let (index, hash): (u32, H256) = events
.iter()
.filter_map(|event| match event.event {
Event::Council(CouncilCollectiveEvent::Proposed(_, index, ref hash, _)) =>
Some((index, hash.clone())),
_ => None,
})
.next()
.ok_or_else(|| {
format!("CouncilCollectiveEvent::Proposed not found in events: {:#?}", events)
})?;
// vote
for member in &council_collective[1..] {
let call = CouncilCollectiveCall::vote { proposal: hash.clone(), index, approve: true };
node.submit_extrinsic(call, Some(member.clone())).await?;
}
node.seal_blocks(1).await;
// close vote
let call = CouncilCollectiveCall::close {
proposal_hash: hash,
index,
proposal_weight_bound: weight,
length_bound: length,
};
node.submit_extrinsic(call, Some(council_collective[0].clone())).await?;
node.seal_blocks(1).await;
// assert that proposal has been passed on chain
let events = node
.events()
.into_iter()
.filter(|event| match event.event {
Event::Council(CouncilCollectiveEvent::Closed(_hash, _, _)) if hash == _hash =>
true,
Event::Council(CouncilCollectiveEvent::Approved(_hash)) if hash == _hash => true,
Event::Council(CouncilCollectiveEvent::Executed(_hash, Ok(())))
if hash == _hash =>
true,
_ => false,
})
.collect::<Vec<_>>();
// make sure all 3 events are in state
assert_eq!(
events.len(),
3,
"CouncilCollectiveEvent::{{Closed, Approved, Executed}} not found in events: {:#?}",
node.events(),
);
}
// next technical collective must fast track the proposal.
{
let fast_track = DemocracyCall::fast_track {
proposal_hash: proposal_hash.into(),
voting_period: FastTrackVotingPeriod::get(),
delay: 0,
};
let weight = Weight::MAX / 100_000_000;
let length = fast_track.using_encoded(|x| x.len()) as u32 + 1;
let proposal = TechnicalCollectiveCall::propose {
threshold: technical_collective.len() as u32,
proposal: Box::new(fast_track.into()),
length_bound: length,
};
node.submit_extrinsic(proposal, Some(technical_collective[0].clone())).await?;
node.seal_blocks(1).await;
let events = node.events();
let (index, hash) = events
.iter()
.filter_map(|event| match event.event {
Event::TechnicalCommittee(TechnicalCollectiveEvent::Proposed(
_,
index,
ref hash,
_,
)) => Some((index, hash.clone())),
_ => None,
})
.next()
.ok_or_else(|| {
format!("TechnicalCollectiveEvent::Proposed not found in events: {:#?}", events)
})?;
// vote
for member in &technical_collective[1..] {
let call =
TechnicalCollectiveCall::vote { proposal: hash.clone(), index, approve: true };
node.submit_extrinsic(call, Some(member.clone())).await?;
}
node.seal_blocks(1).await;
// close vote
let call = CouncilCollectiveCall::close {
proposal_hash: hash,
index,
proposal_weight_bound: weight,
length_bound: length,
};
node.submit_extrinsic(call, Some(technical_collective[0].clone())).await?;
node.seal_blocks(1).await;
// assert that fast-track proposal has been passed on chain
let events = node
.events()
.into_iter()
.filter(|event| match event.event {
Event::TechnicalCommittee(TechnicalCollectiveEvent::Closed(_hash, _, _))
if hash == _hash =>
true,
Event::TechnicalCommittee(TechnicalCollectiveEvent::Approved(_hash))
if hash == _hash =>
true,
Event::TechnicalCommittee(TechnicalCollectiveEvent::Executed(_hash, Ok(())))
if hash == _hash =>
true,
_ => false,
})
.collect::<Vec<_>>();
// make sure all 3 events are in state
assert_eq!(
events.len(),
3,
"TechnicalCollectiveEvent::{{Closed, Approved, Executed}} not found in events: {:#?}",
node.events(),
);
}
// now runtime upgrade proposal is a fast-tracked referendum we can vote for.
let ref_index = node
.events()
.into_iter()
.filter_map(|event| match event.event {
Event::Democracy(democracy::Event::Started(index, _)) => Some(index),
_ => None,
})
.next()
.ok_or_else(|| {
format!("democracy::Event::Started not found in events: {:#?}", node.events())
})?;
let call = DemocracyCall::vote {
ref_index,
vote: AccountVote::Standard {
vote: Vote { aye: true, conviction: Conviction::Locked1x },
// 10 DOTS
balance: 10_000_000_000_000,
},
};
for whale in whales {
node.submit_extrinsic(call.clone(), Some(whale)).await?;
}
// wait for fast track period.
node.seal_blocks(FastTrackVotingPeriod::get() as usize).await;
// assert that the proposal is passed by looking at events
let events = node
.events()
.into_iter()
.filter(|event| match event.event {
Event::Democracy(democracy::Event::Passed(_index)) if _index == ref_index => true,
Event::Democracy(democracy::Event::PreimageUsed(_hash, _, _))
if _hash == proposal_hash =>
true,
Event::Democracy(democracy::Event::Executed(_index, Ok(()))) if _index == ref_index =>
true,
_ => false,
})
.collect::<Vec<_>>();
// make sure all events were emitted
assert_eq!(
events.len(),
3,
"democracy::Event::{{Passed, PreimageUsed, Executed}} not found in events: {:#?}",
node.events(),
);
Ok(())
}
/// Runs the test-runner as a binary.
pub fn run<F, Fut>(callback: F) -> Result<(), Box<dyn Error>>
where
F: FnOnce(Node<PolkadotChainInfo>) -> Fut,
Fut: Future<Output = Result<(), Box<dyn Error>>>,
{
use sc_cli::{CliConfiguration, SubstrateCli};
use structopt::StructOpt;
let tokio_runtime = build_runtime()?;
// parse cli args
let cmd = <polkadot_cli::Cli as StructOpt>::from_args();
// set up logging
let filters = cmd.run.base.log_filters()?;
let logger = sc_tracing::logging::LoggerBuilder::new(filters);
logger.init()?;
// set up the test-runner
let config = cmd.create_configuration(&cmd.run.base, tokio_runtime.handle().clone())?;
sc_cli::print_node_infos::<polkadot_cli::Cli>(&config);
let (rpc, task_manager, client, pool, command_sink, backend) =
client_parts::<PolkadotChainInfo>(ConfigOrChainSpec::Config(config))?;
let node =
Node::<PolkadotChainInfo>::new(rpc, task_manager, client, pool, command_sink, backend);
// hand off node.
tokio_runtime.block_on(callback(node))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use polkadot_service::chain_spec::polkadot_development_config;
use sp_keyring::sr25519::Keyring::Alice;
use sp_runtime::{traits::IdentifyAccount, MultiSigner};
#[test]
fn test_runner() {
let runtime = build_runtime().unwrap();
let (rpc, task_manager, client, pool, command_sink, backend) =
client_parts::<PolkadotChainInfo>(ConfigOrChainSpec::ChainSpec(
Box::new(polkadot_development_config().unwrap()),
runtime.handle().clone(),
))
.unwrap();
let node =
Node::<PolkadotChainInfo>::new(rpc, task_manager, client, pool, command_sink, backend);
runtime.block_on(async {
// seals blocks
node.seal_blocks(1).await;
// submit extrinsics
let alice = MultiSigner::from(Alice.public()).into_account();
node.submit_extrinsic(
system::Call::remark { remark: (b"hello world").to_vec() },
Some(alice),
)
.await
.unwrap();
// look ma, I can read state.
let _events = node.with_state(|| system::Pallet::<Runtime>::events());
// get access to the underlying client.
let _client = node.client();
});
}
}