Reorganising the repository - external renames and moves (#4074)

* Adding first rough ouline of the repository structure

* Remove old CI stuff

* add title

* formatting fixes

* move node-exits job's script to scripts dir

* Move docs into subdir

* move to bin

* move maintainence scripts, configs and helpers into its own dir

* add .local to ignore

* move core->client

* start up 'test' area

* move test client

* move test runtime

* make test move compile

* Add dependencies rule enforcement.

* Fix indexing.

* Update docs to reflect latest changes

* Moving /srml->/paint

* update docs

* move client/sr-* -> primitives/

* clean old readme

* remove old broken code in rhd

* update lock

* Step 1.

* starting to untangle client

* Fix after merge.

* start splitting out client interfaces

* move children and blockchain interfaces

* Move trie and state-machine to primitives.

* Fix WASM builds.

* fixing broken imports

* more interface moves

* move backend and light to interfaces

* move CallExecutor

* move cli off client

* moving around more interfaces

* re-add consensus crates into the mix

* fix subkey path

* relieve client from executor

* starting to pull out client from grandpa

* move is_decendent_of out of client

* grandpa still depends on client directly

* lemme tests pass

* rename srml->paint

* Make it compile.

* rename interfaces->client-api

* Move keyring to primitives.

* fixup libp2p dep

* fix broken use

* allow dependency enforcement to fail

* move fork-tree

* Moving wasm-builder

* make env

* move build-script-utils

* fixup broken crate depdencies and names

* fix imports for authority discovery

* fix typo

* update cargo.lock

* fixing imports

* Fix paths and add missing crates

* re-add missing crates
This commit is contained in:
Benjamin Kampmann
2019-11-14 21:51:17 +01:00
committed by Bastian Köcher
parent becc3b0a4f
commit 60e5011c72
809 changed files with 7801 additions and 6464 deletions
+41
View File
@@ -0,0 +1,41 @@
[package]
name = "node-template"
version = "2.0.0"
authors = ["Anonymous"]
build = "build.rs"
edition = "2018"
[[bin]]
name = "node-template"
path = "src/main.rs"
[dependencies]
derive_more = "0.15.0"
futures = "0.1.29"
ctrlc = { version = "3.1.3", features = ["termination"] }
log = "0.4.8"
tokio = "0.1.22"
exit-future = "0.1.4"
parking_lot = "0.9.0"
codec = { package = "parity-scale-codec", version = "1.0.0" }
trie-root = "0.15.2"
sr-io = { path = "../../primitives/sr-io" }
substrate-cli = { path = "../../client/cli" }
primitives = { package = "substrate-primitives", path = "../../primitives/core" }
substrate-executor = { path = "../../client/executor" }
substrate-service = { path = "../../client/service" }
inherents = { package = "substrate-inherents", path = "../../primitives/inherents" }
transaction-pool = { package = "substrate-transaction-pool", path = "../../client/transaction-pool" }
network = { package = "substrate-network", path = "../../client/network" }
aura = { package = "substrate-consensus-aura", path = "../../client/consensus/aura" }
aura-primitives = { package = "substrate-consensus-aura-primitives", path = "../../primitives/consensus/aura" }
grandpa = { package = "substrate-finality-grandpa", path = "../../client/finality-grandpa" }
grandpa-primitives = { package = "substrate-finality-grandpa-primitives", path = "../../primitives/finality-grandpa" }
substrate-client = { path = "../../client/" }
runtime = { package = "node-template-runtime", path = "runtime" }
sr-primitives = { path = "../../primitives/sr-primitives" }
basic-authorship = { package = "substrate-basic-authorship", path = "../../client/basic-authorship"}
[build-dependencies]
vergen = "3.0.4"
build-script-utils = { package = "substrate-build-script-utils", path = "../../utils/build-script-utils" }
+24
View File
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
+76
View File
@@ -0,0 +1,76 @@
# Substrate Node Template
A new SRML-based Substrate node, ready for hacking.
## Build
Install Rust:
```bash
curl https://sh.rustup.rs -sSf | sh
```
Initialize your Wasm Build environment:
```bash
./scripts/init.sh
```
Build Wasm and native code:
```bash
cargo build --release
```
## Run
### Single node development chain
Purge any existing developer chain state:
```bash
./target/release/node-template purge-chain --dev
```
Start a development chain with:
```bash
./target/release/node-template --dev
```
Detailed logs may be shown by running the node with the following environment variables set: `RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`.
### Multi-node local testnet
If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two validator nodes for Alice and Bob, who are the initial authorities of the genesis chain that have been endowed with testnet units.
Optionally, give each node a name and expose them so they are listed on the Polkadot [telemetry site](https://telemetry.polkadot.io/#/Local%20Testnet).
You'll need two terminal windows open.
We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at `/tmp/alice`. The bootnode ID of her node is `QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR`, which is generated from the `--node-key` value that we specify below:
```bash
cargo run -- \
--base-path /tmp/alice \
--chain=local \
--alice \
--node-key 0000000000000000000000000000000000000000000000000000000000000001 \
--telemetry-url ws://telemetry.polkadot.io:1024 \
--validator
```
In the second terminal, we'll start Bob's substrate node on a different TCP port of 30334, and with his chain database stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's bootnode ID on TCP port 30333:
```bash
cargo run -- \
--base-path /tmp/bob \
--bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR \
--chain=local \
--bob \
--port 30334 \
--telemetry-url ws://telemetry.polkadot.io:1024 \
--validator
```
Additional CLI usage options are available and may be shown by running `cargo run -- --help`.
+9
View File
@@ -0,0 +1,9 @@
use vergen::{ConstantsFlags, generate_cargo_keys};
const ERROR_MSG: &str = "Failed to generate metadata files";
fn main() {
generate_cargo_keys(ConstantsFlags::SHA_SHORT).expect(ERROR_MSG);
build_script_utils::rerun_if_git_head_changed();
}
@@ -0,0 +1,67 @@
[package]
name = "node-template-runtime"
version = "2.0.0"
authors = ["Anonymous"]
edition = "2018"
[dependencies]
aura = { package = "paint-aura", path = "../../../paint/aura", default-features = false }
aura-primitives = { package = "substrate-consensus-aura-primitives", path = "../../../primitives/consensus/aura", default-features = false }
balances = { package = "paint-balances", path = "../../../paint/balances", default-features = false }
block-builder-api = { package = "substrate-block-builder-runtime-api", path = "../../../primitives/block-builder/runtime-api", default-features = false}
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] }
executive = { package = "paint-executive", path = "../../../paint/executive", default-features = false }
grandpa = { package = "paint-grandpa", path = "../../../paint/grandpa", default-features = false }
indices = { package = "paint-indices", path = "../../../paint/indices", default-features = false }
inherents = { package = "substrate-inherents", path = "../../../primitives/inherents", default-features = false}
offchain-primitives = { package = "substrate-offchain-primitives", path = "../../../primitives/offchain", default-features = false }
primitives = { package = "substrate-primitives", path = "../../../primitives/core", default-features = false }
randomness-collective-flip = { package = "paint-randomness-collective-flip", path = "../../../paint/randomness-collective-flip", default-features = false }
rstd = { package = "sr-std", path = "../../../primitives/sr-std", default-features = false }
runtime-io = { package = "sr-io", path = "../../../primitives/sr-io", default-features = false }
safe-mix = { version = "1.0.0", default-features = false }
serde = { version = "1.0.101", optional = true, features = ["derive"] }
sr-api = { path = "../../../primitives/sr-api", default-features = false }
sr-primitives = { path = "../../../primitives/sr-primitives", default-features = false }
substrate-session = { path = "../../../primitives/session", default-features = false }
sudo = { package = "paint-sudo", path = "../../../paint/sudo", default-features = false }
support = { package = "paint-support", path = "../../../paint/support", default-features = false }
system = { package = "paint-system", path = "../../../paint/system", default-features = false }
timestamp = { package = "paint-timestamp", path = "../../../paint/timestamp", default-features = false }
transaction-payment = { package = "paint-transaction-payment", path = "../../../paint/transaction-payment", default-features = false }
tx-pool-api = { package = "substrate-transaction-pool-runtime-api", path = "../../../primitives/transaction-pool/runtime-api", default-features = false }
version = { package = "sr-version", path = "../../../primitives/sr-version", default-features = false }
[build-dependencies]
wasm-builder-runner = { package = "substrate-wasm-builder-runner", path = "../../../client/utils/wasm-builder-runner", version = "1.0.4" }
[features]
default = ["std"]
std = [
"aura-primitives/std",
"aura/std",
"balances/std",
"block-builder-api/std",
"codec/std",
"executive/std",
"grandpa/std",
"indices/std",
"inherents/std",
"offchain-primitives/std",
"primitives/std",
"randomness-collective-flip/std",
"rstd/std",
"runtime-io/std",
"safe-mix/std",
"serde",
"sr-api/std",
"sr-primitives/std",
"substrate-session/std",
"sudo/std",
"support/std",
"system/std",
"timestamp/std",
"transaction-payment/std",
"tx-pool-api/std",
"version/std",
]
@@ -0,0 +1,27 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource};
fn main() {
build_current_project_with_rustflags(
"wasm_binary.rs",
WasmBuilderSource::Crates("1.0.8"),
// This instructs LLD to export __heap_base as a global variable, which is used by the
// external memory allocator.
"-Clink-arg=--export=__heap_base",
);
}
@@ -0,0 +1,361 @@
//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
#![recursion_limit="256"]
// Make the WASM binary available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
use rstd::prelude::*;
use primitives::OpaqueMetadata;
use sr_primitives::{
ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str,
impl_opaque_keys, MultiSignature
};
use sr_primitives::traits::{
NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify, ConvertInto, IdentifyAccount
};
use sr_primitives::weights::Weight;
use sr_api::impl_runtime_apis;
use aura_primitives::sr25519::AuthorityId as AuraId;
use grandpa::AuthorityList as GrandpaAuthorityList;
use grandpa::fg_primitives;
use version::RuntimeVersion;
#[cfg(feature = "std")]
use version::NativeVersion;
// A few exports that help ease life for downstream crates.
#[cfg(any(feature = "std", test))]
pub use sr_primitives::BuildStorage;
pub use timestamp::Call as TimestampCall;
pub use balances::Call as BalancesCall;
pub use sr_primitives::{Permill, Perbill};
pub use support::{StorageValue, construct_runtime, parameter_types, traits::Randomness};
/// An index to a block.
pub type BlockNumber = u32;
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = MultiSignature;
/// Some way of identifying an account on the chain. We intentionally make it equivalent
/// to the public key of our transaction signing scheme.
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
/// The type for looking up accounts. We don't expect more than 4 billion of them, but you
/// never know...
pub type AccountIndex = u32;
/// Balance of an account.
pub type Balance = u128;
/// Index of a transaction in the chain.
pub type Index = u32;
/// A hash of some data used by the chain.
pub type Hash = primitives::H256;
/// Digest item type.
pub type DigestItem = generic::DigestItem<Hash>;
/// Used for the module template in `./template.rs`
mod template;
/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
/// the specifics of the runtime. They can then be made to be agnostic over specific formats
/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
/// to even the core datastructures.
pub mod opaque {
use super::*;
pub use sr_primitives::OpaqueExtrinsic as UncheckedExtrinsic;
/// Opaque block header type.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Opaque block type.
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// Opaque block identifier type.
pub type BlockId = generic::BlockId<Block>;
impl_opaque_keys! {
pub struct SessionKeys {
pub aura: Aura,
pub grandpa: Grandpa,
}
}
}
/// This runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node-template"),
impl_name: create_runtime_str!("node-template"),
authoring_version: 1,
spec_version: 1,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
};
pub const MILLISECS_PER_BLOCK: u64 = 6000;
pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
// These time units are defined in number of blocks.
pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
pub const HOURS: BlockNumber = MINUTES * 60;
pub const DAYS: BlockNumber = HOURS * 24;
/// The version infromation used to identify this runtime when compiled natively.
#[cfg(feature = "std")]
pub fn native_version() -> NativeVersion {
NativeVersion {
runtime_version: VERSION,
can_author_with: Default::default(),
}
}
parameter_types! {
pub const BlockHashCount: BlockNumber = 250;
pub const MaximumBlockWeight: Weight = 1_000_000;
pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);
pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;
pub const Version: RuntimeVersion = VERSION;
}
impl system::Trait for Runtime {
/// The identifier used to distinguish between accounts.
type AccountId = AccountId;
/// The aggregated dispatch type that is available for extrinsics.
type Call = Call;
/// The lookup mechanism to get account ID from whatever is passed in dispatchers.
type Lookup = Indices;
/// The index type for storing how many extrinsics an account has signed.
type Index = Index;
/// The index type for blocks.
type BlockNumber = BlockNumber;
/// The type for hashing blocks and tries.
type Hash = Hash;
/// The hashing algorithm used.
type Hashing = BlakeTwo256;
/// The header type.
type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// The ubiquitous event type.
type Event = Event;
/// The ubiquitous origin type.
type Origin = Origin;
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
type BlockHashCount = BlockHashCount;
/// Maximum weight of each block.
type MaximumBlockWeight = MaximumBlockWeight;
/// Maximum size of all encoded transactions (in bytes) that are allowed in one block.
type MaximumBlockLength = MaximumBlockLength;
/// Portion of the block weight that is available to all normal transactions.
type AvailableBlockRatio = AvailableBlockRatio;
/// Version of the runtime.
type Version = Version;
}
impl aura::Trait for Runtime {
type AuthorityId = AuraId;
}
impl grandpa::Trait for Runtime {
type Event = Event;
}
impl indices::Trait for Runtime {
/// The type for recording indexing into the account enumeration. If this ever overflows, there
/// will be problems!
type AccountIndex = AccountIndex;
/// Use the standard means of resolving an index hint from an id.
type ResolveHint = indices::SimpleResolveHint<Self::AccountId, Self::AccountIndex>;
/// Determine whether an account is dead.
type IsDeadAccount = Balances;
/// The ubiquitous event type.
type Event = Event;
}
parameter_types! {
pub const MinimumPeriod: u64 = SLOT_DURATION / 2;
}
impl timestamp::Trait for Runtime {
/// A timestamp: milliseconds since the unix epoch.
type Moment = u64;
type OnTimestampSet = Aura;
type MinimumPeriod = MinimumPeriod;
}
parameter_types! {
pub const ExistentialDeposit: u128 = 500;
pub const TransferFee: u128 = 0;
pub const CreationFee: u128 = 0;
}
impl balances::Trait for Runtime {
/// The type for recording an account's balance.
type Balance = Balance;
/// What to do if an account's free balance gets zeroed.
type OnFreeBalanceZero = ();
/// What to do if a new account is created.
type OnNewAccount = Indices;
/// The ubiquitous event type.
type Event = Event;
type DustRemoval = ();
type TransferPayment = ();
type ExistentialDeposit = ExistentialDeposit;
type TransferFee = TransferFee;
type CreationFee = CreationFee;
}
parameter_types! {
pub const TransactionBaseFee: Balance = 0;
pub const TransactionByteFee: Balance = 1;
}
impl transaction_payment::Trait for Runtime {
type Currency = balances::Module<Runtime>;
type OnTransactionPayment = ();
type TransactionBaseFee = TransactionBaseFee;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = ConvertInto;
type FeeMultiplierUpdate = ();
}
impl sudo::Trait for Runtime {
type Event = Event;
type Proposal = Call;
}
/// Used for the module template in `./template.rs`
impl template::Trait for Runtime {
type Event = Event;
}
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: system::{Module, Call, Storage, Config, Event},
Timestamp: timestamp::{Module, Call, Storage, Inherent},
Aura: aura::{Module, Config<T>, Inherent(Timestamp)},
Grandpa: grandpa::{Module, Call, Storage, Config, Event},
Indices: indices::{default, Config<T>},
Balances: balances::{default, Error},
TransactionPayment: transaction_payment::{Module, Storage},
Sudo: sudo,
// Used for the module template in `./template.rs`
TemplateModule: template::{Module, Call, Storage, Event<T>},
RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage},
}
);
/// The address format for describing accounts.
pub type Address = <Indices as StaticLookup>::Source;
/// Block header type as expected by this runtime.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Block type as expected by this runtime.
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// A Block signed with a Justification
pub type SignedBlock = generic::SignedBlock<Block>;
/// BlockId type as expected by this runtime.
pub type BlockId = generic::BlockId<Block>;
/// The SignedExtension to the basic transaction logic.
pub type SignedExtra = (
system::CheckVersion<Runtime>,
system::CheckGenesis<Runtime>,
system::CheckEra<Runtime>,
system::CheckNonce<Runtime>,
system::CheckWeight<Runtime>,
transaction_payment::ChargeTransactionPayment<Runtime>
);
/// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
/// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Call, SignedExtra>;
/// Executive: handles dispatch to the various modules.
pub type Executive = executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Runtime, AllModules>;
impl_runtime_apis! {
impl sr_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion {
VERSION
}
fn execute_block(block: Block) {
Executive::execute_block(block)
}
fn initialize_block(header: &<Block as BlockT>::Header) {
Executive::initialize_block(header)
}
}
impl sr_api::Metadata<Block> for Runtime {
fn metadata() -> OpaqueMetadata {
Runtime::metadata().into()
}
}
impl block_builder_api::BlockBuilder<Block> for Runtime {
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyResult {
Executive::apply_extrinsic(extrinsic)
}
fn finalize_block() -> <Block as BlockT>::Header {
Executive::finalize_block()
}
fn inherent_extrinsics(data: inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
data.create_extrinsics()
}
fn check_inherents(
block: Block,
data: inherents::InherentData,
) -> inherents::CheckInherentsResult {
data.check_extrinsics(&block)
}
fn random_seed() -> <Block as BlockT>::Hash {
RandomnessCollectiveFlip::random_seed()
}
}
impl tx_pool_api::TaggedTransactionQueue<Block> for Runtime {
fn validate_transaction(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
Executive::validate_transaction(tx)
}
}
impl offchain_primitives::OffchainWorkerApi<Block> for Runtime {
fn offchain_worker(number: NumberFor<Block>) {
Executive::offchain_worker(number)
}
}
impl aura_primitives::AuraApi<Block, AuraId> for Runtime {
fn slot_duration() -> u64 {
Aura::slot_duration()
}
fn authorities() -> Vec<AuraId> {
Aura::authorities()
}
}
impl substrate_session::SessionKeys<Block> for Runtime {
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
opaque::SessionKeys::generate(seed)
}
}
impl fg_primitives::GrandpaApi<Block> for Runtime {
fn grandpa_authorities() -> GrandpaAuthorityList {
Grandpa::grandpa_authorities()
}
}
}
@@ -0,0 +1,131 @@
/// A runtime module template with necessary imports
/// Feel free to remove or edit this file as needed.
/// If you change the name of this file, make sure to update its references in runtime/src/lib.rs
/// If you remove this file, you can remove those references
/// For more guidance on Substrate modules, see the example module
/// https://github.com/paritytech/substrate/blob/master/paint/example/src/lib.rs
use support::{decl_module, decl_storage, decl_event, dispatch::Result};
use system::ensure_signed;
/// The module's configuration trait.
pub trait Trait: system::Trait {
// TODO: Add other types and constants required configure this module.
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
// This module's storage items.
decl_storage! {
trait Store for Module<T: Trait> as TemplateModule {
// Just a dummy storage item.
// Here we are declaring a StorageValue, `Something` as a Option<u32>
// `get(fn something)` is the default getter which returns either the stored `u32` or `None` if nothing stored
Something get(fn something): Option<u32>;
}
}
// The module's dispatchable functions.
decl_module! {
/// The module declaration.
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
// Initializing events
// this is needed only if you are using events in your module
fn deposit_event() = default;
// Just a dummy entry point.
// function that can be called by the external world as an extrinsics call
// takes a parameter of the type `AccountId`, stores it and emits an event
pub fn do_something(origin, something: u32) -> Result {
// TODO: You only need this if you want to check it was signed.
let who = ensure_signed(origin)?;
// TODO: Code to execute when something calls this.
// For example: the following line stores the passed in u32 in the storage
Something::put(something);
// here we are raising the Something event
Self::deposit_event(RawEvent::SomethingStored(something, who));
Ok(())
}
}
}
decl_event!(
pub enum Event<T> where AccountId = <T as system::Trait>::AccountId {
// Just a dummy event.
// Event `Something` is declared with a parameter of the type `u32` and `AccountId`
// To emit this event, we call the deposit funtion, from our runtime funtions
SomethingStored(u32, AccountId),
}
);
/// tests for this module
#[cfg(test)]
mod tests {
use super::*;
use primitives::H256;
use support::{impl_outer_origin, assert_ok, parameter_types};
use sr_primitives::{
traits::{BlakeTwo256, IdentityLookup}, testing::Header, weights::Weight, Perbill,
};
impl_outer_origin! {
pub enum Origin for Test {}
}
// For testing the module, we construct most of a mock runtime. This means
// first constructing a configuration type (`Test`) which `impl`s each of the
// configuration traits of modules we want to use.
#[derive(Clone, Eq, PartialEq)]
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: Weight = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);
}
impl system::Trait for Test {
type Origin = Origin;
type Call = ();
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
type AvailableBlockRatio = AvailableBlockRatio;
type Version = ();
}
impl Trait for Test {
type Event = ();
}
type TemplateModule = Module<Test>;
// This function basically just builds a genesis storage key/value store according to
// our desired mockup.
fn new_test_ext() -> runtime_io::TestExternalities {
system::GenesisConfig::default().build_storage::<Test>().unwrap().into()
}
#[test]
fn it_works_for_default_value() {
new_test_ext().execute_with(|| {
// Just a dummy test for the dummy funtion `do_something`
// calling the `do_something` function with a value 42
assert_ok!(TemplateModule::do_something(Origin::signed(1), 42));
// asserting that the stored value is equal to what we stored
assert_eq!(TemplateModule::something(), Some(42));
});
}
}
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -e
echo "*** Initializing WASM build environment"
if [ -z $CI_PROJECT_NAME ] ; then
rustup update nightly
rustup update stable
fi
rustup target add wasm32-unknown-unknown --toolchain nightly
@@ -0,0 +1,143 @@
use primitives::{Pair, Public, sr25519};
use runtime::{
AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig,
SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, Signature
};
use aura_primitives::sr25519::{AuthorityId as AuraId};
use grandpa_primitives::{AuthorityId as GrandpaId};
use substrate_service;
use sr_primitives::traits::{Verify, IdentifyAccount};
// Note this is the URL for the telemetry server
//const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
pub type ChainSpec = substrate_service::ChainSpec<GenesisConfig>;
/// The chain specification option. This is expected to come in from the CLI and
/// is little more than one of a number of alternatives which can easily be converted
/// from a string (`--chain=...`) into a `ChainSpec`.
#[derive(Clone, Debug)]
pub enum Alternative {
/// Whatever the current runtime is, with just Alice as an auth.
Development,
/// Whatever the current runtime is, with simple Alice/Bob auths.
LocalTestnet,
}
/// Helper function to generate a crypto pair from seed
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{}", seed), None)
.expect("static values are valid; qed")
.public()
}
type AccountPublic = <Signature as Verify>::Signer;
/// Helper function to generate an account ID from seed
pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where
AccountPublic: From<<TPublic::Pair as Pair>::Public>
{
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}
/// Helper function to generate an authority key for Aura
pub fn get_authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) {
(
get_from_seed::<AuraId>(s),
get_from_seed::<GrandpaId>(s),
)
}
impl Alternative {
/// Get an actual chain config from one of the alternatives.
pub(crate) fn load(self) -> Result<ChainSpec, String> {
Ok(match self {
Alternative::Development => ChainSpec::from_genesis(
"Development",
"dev",
|| testnet_genesis(vec![
get_authority_keys_from_seed("Alice"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
true),
vec![],
None,
None,
None,
None
),
Alternative::LocalTestnet => ChainSpec::from_genesis(
"Local Testnet",
"local_testnet",
|| testnet_genesis(vec![
get_authority_keys_from_seed("Alice"),
get_authority_keys_from_seed("Bob"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
true),
vec![],
None,
None,
None,
None
),
})
}
pub(crate) fn from(s: &str) -> Option<Self> {
match s {
"dev" => Some(Alternative::Development),
"" | "local" => Some(Alternative::LocalTestnet),
_ => None,
}
}
}
fn testnet_genesis(initial_authorities: Vec<(AuraId, GrandpaId)>,
root_key: AccountId,
endowed_accounts: Vec<AccountId>,
_enable_println: bool) -> GenesisConfig {
GenesisConfig {
system: Some(SystemConfig {
code: WASM_BINARY.to_vec(),
changes_trie_config: Default::default(),
}),
indices: Some(IndicesConfig {
ids: endowed_accounts.clone(),
}),
balances: Some(BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(),
vesting: vec![],
}),
sudo: Some(SudoConfig {
key: root_key,
}),
aura: Some(AuraConfig {
authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(),
}),
grandpa: Some(GrandpaConfig {
authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(),
}),
}
}
+114
View File
@@ -0,0 +1,114 @@
use crate::service;
use futures::{future, Future, sync::oneshot};
use std::cell::RefCell;
use tokio::runtime::Runtime;
pub use substrate_cli::{VersionInfo, IntoExit, error};
use substrate_cli::{display_role, informant, parse_and_prepare, ParseAndPrepare, NoCustom};
use substrate_service::{AbstractService, Roles as ServiceRoles, Configuration};
use aura_primitives::sr25519::{AuthorityPair as AuraPair};
use crate::chain_spec;
use log::info;
/// Parse command line arguments into service configuration.
pub fn run<I, T, E>(args: I, exit: E, version: VersionInfo) -> error::Result<()> where
I: IntoIterator<Item = T>,
T: Into<std::ffi::OsString> + Clone,
E: IntoExit,
{
type Config<T> = Configuration<(), T>;
match parse_and_prepare::<NoCustom, NoCustom, _>(&version, "substrate-node", args) {
ParseAndPrepare::Run(cmd) => cmd.run(load_spec, exit,
|exit, _cli_args, _custom_args, config: Config<_>| {
info!("{}", version.name);
info!(" version {}", config.full_version());
info!(" by {}, 2017, 2018", version.author);
info!("Chain specification: {}", config.chain_spec.name());
info!("Node name: {}", config.name);
info!("Roles: {}", display_role(&config));
let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?;
match config.roles {
ServiceRoles::LIGHT => run_until_exit(
runtime,
service::new_light(config)?,
exit
),
_ => run_until_exit(
runtime,
service::new_full(config)?,
exit
),
}
}),
ParseAndPrepare::BuildSpec(cmd) => cmd.run::<NoCustom, _, _, _>(load_spec),
ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|
Ok(new_full_start!(config).0), load_spec, exit),
ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|
Ok(new_full_start!(config).0), load_spec, exit),
ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec),
ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder(|config: Config<_>|
Ok(new_full_start!(config).0), load_spec),
ParseAndPrepare::CustomCommand(_) => Ok(())
}?;
Ok(())
}
fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
Ok(match chain_spec::Alternative::from(id) {
Some(spec) => Some(spec.load()?),
None => None,
})
}
fn run_until_exit<T, E>(
mut runtime: Runtime,
service: T,
e: E,
) -> error::Result<()>
where
T: AbstractService,
E: IntoExit,
{
let (exit_send, exit) = exit_future::signal();
let informant = informant::build(&service);
runtime.executor().spawn(exit.until(informant).map(|_| ()));
// we eagerly drop the service so that the internal exit future is fired,
// but we need to keep holding a reference to the global telemetry guard
let _telemetry = service.telemetry();
let service_res = {
let exit = e.into_exit().map_err(|_| error::Error::Other("Exit future failed.".into()));
let service = service.map_err(|err| error::Error::Service(err));
let select = service.select(exit).map(|_| ()).map_err(|(err, _)| err);
runtime.block_on(select)
};
exit_send.fire();
// TODO [andre]: timeout this future #1318
let _ = runtime.shutdown_on_idle().wait();
service_res
}
// handles ctrl-c
pub struct Exit;
impl IntoExit for Exit {
type Exit = future::MapErr<oneshot::Receiver<()>, fn(oneshot::Canceled) -> ()>;
fn into_exit(self) -> Self::Exit {
// can't use signal directly here because CtrlC takes only `Fn`.
let (exit_send, exit) = oneshot::channel();
let exit_send_cell = RefCell::new(Some(exit_send));
ctrlc::set_handler(move || {
let exit_send = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take();
if let Some(exit_send) = exit_send {
exit_send.send(()).expect("Error sending exit notification");
}
}).expect("Error setting Ctrl-C handler");
exit.map_err(drop)
}
}
+25
View File
@@ -0,0 +1,25 @@
//! Substrate Node Template CLI library.
#![warn(missing_docs)]
#![warn(unused_extern_crates)]
mod chain_spec;
#[macro_use]
mod service;
mod cli;
pub use substrate_cli::{VersionInfo, IntoExit, error};
fn main() -> Result<(), cli::error::Error> {
let version = VersionInfo {
name: "Substrate Node",
commit: env!("VERGEN_SHA_SHORT"),
version: env!("CARGO_PKG_VERSION"),
executable_name: "node-template",
author: "Anonymous",
description: "Template Node",
support_url: "support.anonymous.an",
};
cli::run(std::env::args(), cli::Exit, version)
}
+225
View File
@@ -0,0 +1,225 @@
//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
use std::sync::Arc;
use std::time::Duration;
use substrate_client::LongestChain;
use runtime::{self, GenesisConfig, opaque::Block, RuntimeApi};
use substrate_service::{error::{Error as ServiceError}, AbstractService, Configuration, ServiceBuilder};
use transaction_pool::{self, txpool::{Pool as TransactionPool}};
use inherents::InherentDataProviders;
use network::{construct_simple_protocol};
use substrate_executor::native_executor_instance;
pub use substrate_executor::NativeExecutor;
use aura_primitives::sr25519::{AuthorityPair as AuraPair};
use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider};
use basic_authorship;
// Our native executor instance.
native_executor_instance!(
pub Executor,
runtime::api::dispatch,
runtime::native_version,
);
construct_simple_protocol! {
/// Demo protocol attachment for substrate.
pub struct NodeProtocol where Block = Block { }
}
/// Starts a `ServiceBuilder` for a full service.
///
/// Use this macro if you don't actually need the full service, but just the builder in order to
/// be able to perform chain operations.
macro_rules! new_full_start {
($config:expr) => {{
let mut import_setup = None;
let inherent_data_providers = inherents::InherentDataProviders::new();
let builder = substrate_service::ServiceBuilder::new_full::<
runtime::opaque::Block, runtime::RuntimeApi, crate::service::Executor
>($config)?
.with_select_chain(|_config, backend| {
Ok(substrate_client::LongestChain::new(backend.clone()))
})?
.with_transaction_pool(|config, client|
Ok(transaction_pool::txpool::Pool::new(config, transaction_pool::FullChainApi::new(client)))
)?
.with_import_queue(|_config, client, mut select_chain, transaction_pool| {
let select_chain = select_chain.take()
.ok_or_else(|| substrate_service::Error::SelectChainRequired)?;
let (grandpa_block_import, grandpa_link) =
grandpa::block_import::<_, _, _, runtime::RuntimeApi, _>(
client.clone(), &*client, select_chain
)?;
let import_queue = aura::import_queue::<_, _, AuraPair, _>(
aura::SlotDuration::get_or_compute(&*client)?,
Box::new(grandpa_block_import.clone()),
Some(Box::new(grandpa_block_import.clone())),
None,
client,
inherent_data_providers.clone(),
Some(transaction_pool),
)?;
import_setup = Some((grandpa_block_import, grandpa_link));
Ok(import_queue)
})?;
(builder, import_setup, inherent_data_providers)
}}
}
/// Builds a new service for a full client.
pub fn new_full<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
-> Result<impl AbstractService, ServiceError>
{
let is_authority = config.roles.is_authority();
let force_authoring = config.force_authoring;
let name = config.name.clone();
let disable_grandpa = config.disable_grandpa;
// sentry nodes announce themselves as authorities to the network
// and should run the same protocols authorities do, but it should
// never actively participate in any consensus process.
let participates_in_consensus = is_authority && !config.sentry_mode;
let (builder, mut import_setup, inherent_data_providers) = new_full_start!(config);
let (block_import, grandpa_link) =
import_setup.take()
.expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
let service = builder.with_network_protocol(|_| Ok(NodeProtocol::new()))?
.with_finality_proof_provider(|client, backend|
Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
)?
.build()?;
if participates_in_consensus {
let proposer = basic_authorship::ProposerFactory {
client: service.client(),
transaction_pool: service.transaction_pool(),
};
let client = service.client();
let select_chain = service.select_chain()
.ok_or(ServiceError::SelectChainRequired)?;
let aura = aura::start_aura::<_, _, _, _, _, AuraPair, _, _, _>(
aura::SlotDuration::get_or_compute(&*client)?,
client,
select_chain,
block_import,
proposer,
service.network(),
inherent_data_providers.clone(),
force_authoring,
service.keystore(),
)?;
// the AURA authoring task is considered essential, i.e. if it
// fails we take down the service with it.
service.spawn_essential_task(aura);
}
// if the node isn't actively participating in consensus then it doesn't
// need a keystore, regardless of which protocol we use below.
let keystore = if participates_in_consensus {
Some(service.keystore())
} else {
None
};
let grandpa_config = grandpa::Config {
// FIXME #1578 make this available through chainspec
gossip_duration: Duration::from_millis(333),
justification_period: 512,
name: Some(name),
observer_enabled: true,
keystore,
is_authority,
};
match (is_authority, disable_grandpa) {
(false, false) => {
// start the lightweight GRANDPA observer
service.spawn_task(grandpa::run_grandpa_observer(
grandpa_config,
grandpa_link,
service.network(),
service.on_exit(),
)?);
},
(true, false) => {
// start the full GRANDPA voter
let voter_config = grandpa::GrandpaParams {
config: grandpa_config,
link: grandpa_link,
network: service.network(),
inherent_data_providers: inherent_data_providers.clone(),
on_exit: service.on_exit(),
telemetry_on_connect: Some(service.telemetry_on_connect_stream()),
voting_rule: grandpa::VotingRulesBuilder::default().build(),
};
// the GRANDPA voter task is considered infallible, i.e.
// if it fails we take down the service with it.
service.spawn_essential_task(grandpa::run_grandpa_voter(voter_config)?);
},
(_, true) => {
grandpa::setup_disabled_grandpa(
service.client(),
&inherent_data_providers,
service.network(),
)?;
},
}
Ok(service)
}
/// Builds a new service for a light client.
pub fn new_light<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
-> Result<impl AbstractService, ServiceError>
{
let inherent_data_providers = InherentDataProviders::new();
ServiceBuilder::new_light::<Block, RuntimeApi, Executor>(config)?
.with_select_chain(|_config, backend| {
Ok(LongestChain::new(backend.clone()))
})?
.with_transaction_pool(|config, client|
Ok(TransactionPool::new(config, transaction_pool::FullChainApi::new(client)))
)?
.with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| {
let fetch_checker = fetcher
.map(|fetcher| fetcher.checker().clone())
.ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
let grandpa_block_import = grandpa::light_block_import::<_, _, _, RuntimeApi>(
client.clone(), backend, &*client.clone(), Arc::new(fetch_checker),
)?;
let finality_proof_import = grandpa_block_import.clone();
let finality_proof_request_builder =
finality_proof_import.create_finality_proof_request_builder();
let import_queue = aura::import_queue::<_, _, AuraPair, ()>(
aura::SlotDuration::get_or_compute(&*client)?,
Box::new(grandpa_block_import),
None,
Some(Box::new(finality_proof_import)),
client,
inherent_data_providers.clone(),
None,
)?;
Ok((import_queue, finality_proof_request_builder))
})?
.with_network_protocol(|_| Ok(NodeProtocol::new()))?
.with_finality_proof_provider(|client, backend|
Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
)?
.build()
}
+141
View File
@@ -0,0 +1,141 @@
[package]
name = "node-cli"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Substrate node implementation in Rust."
build = "build.rs"
edition = "2018"
default-run = "substrate"
[badges]
travis-ci = { repository = "paritytech/substrate", branch = "master" }
maintenance = { status = "actively-developed" }
is-it-maintained-issue-resolution = { repository = "paritytech/substrate" }
is-it-maintained-open-issues = { repository = "paritytech/substrate" }
[[bin]]
name = "substrate"
path = "bin/main.rs"
required-features = ["cli"]
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
# third-party dependencies
codec = { package = "parity-scale-codec", version = "1.0.6" }
serde = { version = "1.0.102", features = [ "derive" ] }
futures = "0.1.29"
futures03 = { package = "futures-preview", version = "0.3.0-alpha.19", features = ["compat"] }
hex-literal = "0.2.1"
jsonrpc-core = "14.0.3"
log = "0.4.8"
rand = "0.7.2"
structopt = "0.3.3"
# primitives
authority-discovery-primitives = { package = "substrate-authority-discovery-primitives", path = "../../../primitives/authority-discovery"}
babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../../primitives/consensus/babe" }
grandpa_primitives = { package = "substrate-finality-grandpa-primitives", path = "../../../primitives/finality-grandpa" }
primitives = { package = "substrate-primitives", path = "../../../primitives/core" }
sr-primitives = { path = "../../../primitives/sr-primitives" }
# core dependencies
runtime-io = { package = "sr-io", path = "../../../primitives/sr-io" }
client-api = { package = "substrate-client-api", path = "../../../client/api" }
client = { package = "substrate-client", path = "../../../client/" }
inherents = { package = "substrate-inherents", path = "../../../primitives/inherents" }
chain-spec = { package = "substrate-chain-spec", path = "../../../client/chain-spec" }
transaction_pool = { package = "substrate-transaction-pool", path = "../../../client/transaction-pool" }
network = { package = "substrate-network", path = "../../../client/network" }
babe = { package = "substrate-consensus-babe", path = "../../../client/consensus/babe" }
grandpa = { package = "substrate-finality-grandpa", path = "../../../client/finality-grandpa" }
keyring = { package = "substrate-keyring", path = "../../../primitives/keyring" }
client_db = { package = "substrate-client-db", path = "../../../client/db", default-features = false }
offchain = { package = "substrate-offchain", path = "../../../client/offchain" }
substrate-rpc = { package = "substrate-rpc", path = "../../../client/rpc" }
substrate-basic-authorship = { path = "../../../client/basic-authorship" }
substrate-service = { path = "../../../client/service", default-features = false }
substrate-telemetry = { package = "substrate-telemetry", path = "../../../client/telemetry" }
authority-discovery = { package = "substrate-authority-discovery", path = "../../../client/authority-discovery"}
# paint dependencies
indices = { package = "paint-indices", path = "../../../paint/indices" }
timestamp = { package = "paint-timestamp", path = "../../../paint/timestamp", default-features = false }
finality_tracker = { package = "paint-finality-tracker", path = "../../../paint/finality-tracker", default-features = false }
contracts = { package = "paint-contracts", path = "../../../paint/contracts" }
system = { package = "paint-system", path = "../../../paint/system" }
balances = { package = "paint-balances", path = "../../../paint/balances" }
transaction-payment = { package = "paint-transaction-payment", path = "../../../paint/transaction-payment" }
support = { package = "paint-support", path = "../../../paint/support", default-features = false }
im_online = { package = "paint-im-online", path = "../../../paint/im-online", default-features = false }
sr-authority-discovery = { package = "paint-authority-discovery", path = "../../../paint/authority-discovery"}
# node-specific dependencies
node-runtime = { path = "../runtime" }
node-rpc = { path = "../rpc" }
node-primitives = { path = "../primitives" }
node-executor = { path = "../executor" }
# CLI-specific dependencies
tokio = { version = "0.1.22", optional = true }
exit-future = { version = "0.1.4", optional = true }
substrate-cli = { path = "../../../client/cli", optional = true }
transaction-factory = { path = "../../../test/utils/transaction-factory", optional = true }
ctrlc = { version = "3.1.3", features = ["termination"], optional = true }
# WASM-specific dependencies
libp2p = { version = "0.13.0", default-features = false, optional = true }
clear_on_drop = { version = "0.2.3", features = ["no_cc"], optional = true } # Imported just for the `no_cc` feature
console_error_panic_hook = { version = "0.1.1", optional = true }
console_log = { version = "0.1.2", optional = true }
js-sys = { version = "0.3.22", optional = true }
wasm-bindgen = { version = "0.2.45", optional = true }
wasm-bindgen-futures = { version = "0.3.22", optional = true }
kvdb-memorydb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d", optional = true }
rand6 = { package = "rand", version = "0.6", features = ["wasm-bindgen"], optional = true } # Imported just for the `wasm-bindgen` feature
[dev-dependencies]
keystore = { package = "substrate-keystore", path = "../../../client/keystore" }
babe = { package = "substrate-consensus-babe", path = "../../../client/consensus/babe", features = ["test-helpers"] }
consensus-common = { package = "substrate-consensus-common", path = "../../../primitives/consensus/common" }
service-test = { package = "substrate-service-test", path = "../../../client/service/test" }
futures03 = { package = "futures-preview", version = "0.3.0-alpha.19" }
tempfile = "3.1.0"
[build-dependencies]
substrate-cli = { package = "substrate-cli", path = "../../../client/cli" }
build-script-utils = { package = "substrate-build-script-utils", path = "../../../utils/build-script-utils" }
structopt = "0.3.3"
vergen = "3.0.4"
[features]
default = ["cli"]
browser = [
"clear_on_drop",
"console_error_panic_hook",
"console_log",
"js-sys",
"libp2p",
"wasm-bindgen",
"wasm-bindgen-futures",
"kvdb-memorydb",
"rand/wasm-bindgen",
"rand6"
]
cli = [
"substrate-cli",
"transaction-factory",
"tokio",
"exit-future",
"ctrlc",
"substrate-service/rocksdb",
"node-executor/wasmi-errno",
]
wasmtime = [
"cli",
"node-executor/wasmtime",
"substrate-cli/wasmtime",
"substrate-service/wasmtime",
]
+58
View File
@@ -0,0 +1,58 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate Node CLI
#![warn(missing_docs)]
use futures::sync::oneshot;
use futures::{future, Future};
use substrate_cli::VersionInfo;
use std::cell::RefCell;
// handles ctrl-c
struct Exit;
impl substrate_cli::IntoExit for Exit {
type Exit = future::MapErr<oneshot::Receiver<()>, fn(oneshot::Canceled) -> ()>;
fn into_exit(self) -> Self::Exit {
// can't use signal directly here because CtrlC takes only `Fn`.
let (exit_send, exit) = oneshot::channel();
let exit_send_cell = RefCell::new(Some(exit_send));
ctrlc::set_handler(move || {
if let Some(exit_send) = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take() {
exit_send.send(()).expect("Error sending exit notification");
}
}).expect("Error setting Ctrl-C handler");
exit.map_err(drop)
}
}
fn main() -> Result<(), substrate_cli::error::Error> {
let version = VersionInfo {
name: "Substrate Node",
commit: env!("VERGEN_SHA_SHORT"),
version: env!("CARGO_PKG_VERSION"),
executable_name: "substrate",
author: "Parity Technologies <admin@parity.io>",
description: "Generic substrate node",
support_url: "https://github.com/paritytech/substrate/issues/new",
};
node_cli::run(std::env::args(), Exit, version)
}
@@ -0,0 +1 @@
pkg
@@ -0,0 +1,10 @@
# How to run this demo
```sh
cargo install wasm-pack # If necessary
# From the `node/cli` directory (parent from this README)
wasm-pack build --target web --out-dir ./demo/pkg --no-typescript --release -- --no-default-features --features "browser"
xdg-open index.html
```
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env sh
wasm-pack build --target web --out-dir ./browser-demo/pkg --no-typescript --release ./.. -- --no-default-features --features "browser"
python -m SimpleHTTPServer 8000
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title>Substrate node</title>
<link rel="shortcut icon" href="/favicon.png" />
<script type="module">
import { start_client, default as init } from './pkg/node_cli.js';
import ws from './ws.js';
function log(msg) {
document.getElementsByTagName('body')[0].innerHTML += msg + '\n';
}
async function start() {
log('Loading WASM');
await init('./pkg/node_cli_bg.wasm');
log('Successfully loaded WASM');
// Build our client.
log('Starting client');
let client = start_client(ws());
log('Client started');
client.rpcSubscribe('{"method":"chain_subscribeNewHead","params":[],"id":1,"jsonrpc":"2.0"}',
(r) => log("New chain head: " + r));
setInterval(() => {
client
.rpcSend('{"method":"system_networkState","params":[],"id":1,"jsonrpc":"2.0"}')
.then((r) => log("Network state: " + r));
}, 1000);
}
start();
</script>
</head>
<body style="white-space: pre"></body>
</html>
+148
View File
@@ -0,0 +1,148 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
export default () => {
return {
dial: dial,
listen_on: (addr) => {
let err = new Error("Listening on WebSockets is not possible from within a browser");
err.name = "NotSupportedError";
throw err;
},
};
}
/// Turns a string multiaddress into a WebSockets string URL.
// TODO: support dns addresses as well
const multiaddr_to_ws = (addr) => {
let parsed = addr.match(/^\/(ip4|ip6|dns4|dns6)\/(.*?)\/tcp\/(.*?)\/(ws|wss|x-parity-ws\/(.*)|x-parity-wss\/(.*))$/);
let proto = 'wss';
if (parsed[4] == 'ws' || parsed[4] == 'x-parity-ws') {
proto = 'ws';
}
let url = decodeURIComponent(parsed[5] || parsed[6] || '');
if (parsed != null) {
if (parsed[1] == 'ip6') {
return proto + "://[" + parsed[2] + "]:" + parsed[3] + url;
} else {
return proto + "://" + parsed[2] + ":" + parsed[3] + url;
}
}
let err = new Error("Address not supported: " + addr);
err.name = "NotSupportedError";
throw err;
}
// Attempt to dial a multiaddress.
const dial = (addr) => {
let ws = new WebSocket(multiaddr_to_ws(addr));
let reader = read_queue();
return new Promise((resolve, reject) => {
// TODO: handle ws.onerror properly after dialing has happened
ws.onerror = (ev) => reject(ev);
ws.onmessage = (ev) => reader.inject_blob(ev.data);
ws.onclose = () => reader.inject_eof();
ws.onopen = () => resolve({
read: (function*() { while(ws.readyState == 1) { yield reader.next(); } })(),
write: (data) => {
if (ws.readyState == 1) {
ws.send(data);
return promise_when_ws_finished(ws);
} else {
return Promise.reject("WebSocket is closed");
}
},
shutdown: () => {},
close: () => ws.close()
});
});
}
// Takes a WebSocket object and returns a Promise that resolves when bufferedAmount is 0.
const promise_when_ws_finished = (ws) => {
if (ws.bufferedAmount == 0) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
setTimeout(function check() {
if (ws.bufferedAmount == 0) {
resolve();
} else {
setTimeout(check, 100);
}
}, 2);
})
}
// Creates a queue reading system.
const read_queue = () => {
// State of the queue.
let state = {
// Array of promises resolving to `ArrayBuffer`s, that haven't been transmitted back with
// `next` yet.
queue: new Array(),
// If `resolve` isn't null, it is a "resolve" function of a promise that has already been
// returned by `next`. It should be called with some data.
resolve: null,
};
return {
// Inserts a new Blob in the queue.
inject_blob: (blob) => {
if (state.resolve != null) {
var resolve = state.resolve;
state.resolve = null;
var reader = new FileReader();
reader.addEventListener("loadend", () => resolve(reader.result));
reader.readAsArrayBuffer(blob);
} else {
state.queue.push(new Promise((resolve, reject) => {
var reader = new FileReader();
reader.addEventListener("loadend", () => resolve(reader.result));
reader.readAsArrayBuffer(blob);
}));
}
},
// Inserts an EOF message in the queue.
inject_eof: () => {
if (state.resolve != null) {
var resolve = state.resolve;
state.resolve = null;
resolve(null);
} else {
state.queue.push(Promise.resolve(null));
}
},
// Returns a Promise that yields the next entry as an ArrayBuffer.
next: () => {
if (state.queue.length != 0) {
return state.queue.shift(0);
} else {
if (state.resolve !== null)
throw "Internal error: already have a pending promise";
return new Promise((resolve, reject) => {
state.resolve = resolve;
});
}
}
};
};
+52
View File
@@ -0,0 +1,52 @@
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use std::{fs, env, path::Path};
use structopt::{StructOpt, clap::Shell};
use substrate_cli::{NoCustom, CoreParams};
use vergen::{ConstantsFlags, generate_cargo_keys};
fn main() {
build_shell_completion();
generate_cargo_keys(ConstantsFlags::all()).expect("Failed to generate metadata files");
build_script_utils::rerun_if_git_head_changed();
}
/// Build shell completion scripts for all known shells
/// Full list in https://github.com/kbknapp/clap-rs/blob/e9d0562a1dc5dfe731ed7c767e6cee0af08f0cf9/src/app/parser.rs#L123
fn build_shell_completion() {
for shell in &[Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell] {
build_completion(shell);
}
}
/// Build the shell auto-completion for a given Shell
fn build_completion(shell: &Shell) {
let outdir = match env::var_os("OUT_DIR") {
None => return,
Some(dir) => dir,
};
let path = Path::new(&outdir)
.parent().unwrap()
.parent().unwrap()
.parent().unwrap()
.join("completion-scripts");
fs::create_dir(&path).ok();
CoreParams::<NoCustom, NoCustom>::clap().gen_completions("substrate-node", *shell, &path);
}
@@ -0,0 +1,41 @@
== Shell completion
The Substrate cli command supports shell auto-completion. For this to work, you will need to run the completion script matching you build and system.
Assuming you built a release version using `cargo build --release` and use `bash` run the following:
[source, shell]
source target/release/completion-scripts/substrate.bash
You can find completion scripts for:
- bash
- fish
- zsh
- elvish
- powershell
To make this change persistent, you can proceed as follow:
.First install
[source, shell]
----
COMPL_DIR=$HOME/.completion
mkdir -p $COMPL_DIR
cp -f target/release/completion-scripts/substrate.bash $COMPL_DIR/
echo "source $COMPL_DIR/substrate.bash" >> $HOME/.bash_profile
source $HOME/.bash_profile
----
.Update
When you build a new version of Substrate, the following will ensure you auto-completion script matches the current binary:
[source, shell]
----
COMPL_DIR=$HOME/.completion
mkdir -p $COMPL_DIR
cp -f target/release/completion-scripts/substrate.bash $COMPL_DIR/
source $HOME/.bash_profile
----
File diff suppressed because one or more lines are too long
+160
View File
@@ -0,0 +1,160 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use crate::ChainSpec;
use futures::{prelude::*, sync::oneshot, sync::mpsc};
use libp2p::wasm_ext;
use log::{debug, info};
use std::sync::Arc;
use substrate_service::{AbstractService, RpcSession, Roles as ServiceRoles, Configuration, config::DatabaseConfig};
use wasm_bindgen::prelude::*;
/// Starts the client.
///
/// You must pass a libp2p transport that supports .
#[wasm_bindgen]
pub fn start_client(wasm_ext: wasm_ext::ffi::Transport) -> Result<Client, JsValue> {
start_inner(wasm_ext)
.map_err(|err| JsValue::from_str(&err.to_string()))
}
fn start_inner(wasm_ext: wasm_ext::ffi::Transport) -> Result<Client, Box<dyn std::error::Error>> {
console_error_panic_hook::set_once();
console_log::init_with_level(log::Level::Info);
// Build the configuration to pass to the service.
let config = {
let wasm_ext = wasm_ext::ExtTransport::new(wasm_ext);
let chain_spec = ChainSpec::FlamingFir.load().map_err(|e| format!("{:?}", e))?;
let mut config = Configuration::<(), _, _>::default_with_spec_and_base_path(chain_spec, None);
config.network.transport = network::config::TransportConfig::Normal {
wasm_external_transport: Some(wasm_ext.clone()),
allow_private_ipv4: true,
enable_mdns: false,
};
config.telemetry_external_transport = Some(wasm_ext);
config.roles = ServiceRoles::LIGHT;
config.name = "Browser node".to_string();
config.database = {
let db = Arc::new(kvdb_memorydb::create(10));
DatabaseConfig::Custom(db)
};
config
};
info!("Substrate browser node");
info!(" version {}", config.full_version());
info!(" by Parity Technologies, 2017-2019");
info!("Chain specification: {}", config.chain_spec.name());
info!("Node name: {}", config.name);
info!("Roles: {:?}", config.roles);
// Create the service. This is the most heavy initialization step.
let mut service = crate::service::new_light(config).map_err(|e| format!("{:?}", e))?;
// We now dispatch a background task responsible for processing the service.
//
// The main action performed by the code below consists in polling the service with
// `service.poll()`.
// The rest consists in handling RPC requests.
let (rpc_send_tx, mut rpc_send_rx) = mpsc::unbounded::<RpcMessage>();
wasm_bindgen_futures::spawn_local(futures::future::poll_fn(move || {
loop {
match rpc_send_rx.poll() {
Ok(Async::Ready(Some(message))) => {
let fut = service.rpc_query(&message.session, &message.rpc_json);
let _ = message.send_back.send(Box::new(fut));
},
Ok(Async::NotReady) => break,
Err(_) | Ok(Async::Ready(None)) => return Ok(Async::Ready(())),
}
}
loop {
match service.poll().map_err(|_| ())? {
Async::Ready(()) => return Ok(Async::Ready(())),
Async::NotReady => break
}
}
Ok(Async::NotReady)
}));
Ok(Client {
rpc_send_tx,
})
}
/// A running client.
#[wasm_bindgen]
pub struct Client {
rpc_send_tx: mpsc::UnboundedSender<RpcMessage>,
}
struct RpcMessage {
rpc_json: String,
session: RpcSession,
send_back: oneshot::Sender<Box<dyn Future<Item = Option<String>, Error = ()>>>,
}
#[wasm_bindgen]
impl Client {
/// Allows starting an RPC request. Returns a `Promise` containing the result of that request.
#[wasm_bindgen(js_name = "rpcSend")]
pub fn rpc_send(&mut self, rpc: &str) -> js_sys::Promise {
let rpc_session = RpcSession::new(mpsc::channel(1).0);
let (tx, rx) = oneshot::channel();
let _ = self.rpc_send_tx.unbounded_send(RpcMessage {
rpc_json: rpc.to_owned(),
session: rpc_session,
send_back: tx,
});
let fut = rx
.map_err(|_| ())
.and_then(|fut| fut)
.map(|s| JsValue::from_str(&s.unwrap_or(String::new())))
.map_err(|_| JsValue::NULL);
wasm_bindgen_futures::future_to_promise(fut)
}
/// Subscribes to an RPC pubsub endpoint.
#[wasm_bindgen(js_name = "rpcSubscribe")]
pub fn rpc_subscribe(&mut self, rpc: &str, callback: js_sys::Function) {
let (tx, rx) = mpsc::channel(4);
let rpc_session = RpcSession::new(tx);
let (fut_tx, fut_rx) = oneshot::channel();
let _ = self.rpc_send_tx.unbounded_send(RpcMessage {
rpc_json: rpc.to_owned(),
session: rpc_session.clone(),
send_back: fut_tx,
});
let fut_rx = fut_rx
.map_err(|_| ())
.and_then(|fut| fut);
wasm_bindgen_futures::spawn_local(fut_rx.then(|_| Ok(())));
wasm_bindgen_futures::spawn_local(rx.for_each(move |s| {
match callback.call1(&callback, &JsValue::from_str(&s)) {
Ok(_) => Ok(()),
Err(_) => Err(()),
}
}).then(move |v| {
// We need to keep `rpc_session` alive.
debug!("RPC subscription has ended");
drop(rpc_session);
v
}));
}
}
+406
View File
@@ -0,0 +1,406 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate chain configurations.
use chain_spec::ChainSpecExtension;
use primitives::{Pair, Public, crypto::UncheckedInto, sr25519};
use serde::{Serialize, Deserialize};
use node_runtime::{
AuthorityDiscoveryConfig, BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig,
GrandpaConfig, ImOnlineConfig, IndicesConfig, SessionConfig, SessionKeys, StakerStatus, StakingConfig, SudoConfig,
SystemConfig, TechnicalCommitteeConfig, WASM_BINARY,
};
use node_runtime::Block;
use node_runtime::constants::currency::*;
use substrate_service;
use hex_literal::hex;
use substrate_telemetry::TelemetryEndpoints;
use grandpa_primitives::{AuthorityId as GrandpaId};
use babe_primitives::{AuthorityId as BabeId};
use im_online::sr25519::{AuthorityId as ImOnlineId};
use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId;
use sr_primitives::{Perbill, traits::{Verify, IdentifyAccount}};
pub use node_primitives::{AccountId, Balance, Signature};
pub use node_runtime::GenesisConfig;
type AccountPublic = <Signature as Verify>::Signer;
const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
/// Node `ChainSpec` extensions.
///
/// Additional parameters for some Substrate core modules,
/// customizable from the chain spec.
#[derive(Default, Clone, Serialize, Deserialize, ChainSpecExtension)]
pub struct Extensions {
/// Block numbers with known hashes.
pub fork_blocks: client::ForkBlocks<Block>,
}
/// Specialized `ChainSpec`.
pub type ChainSpec = substrate_service::ChainSpec<
GenesisConfig,
Extensions,
>;
/// Flaming Fir testnet generator
pub fn flaming_fir_config() -> Result<ChainSpec, String> {
ChainSpec::from_json_bytes(&include_bytes!("../res/flaming-fir.json")[..])
}
fn session_keys(
grandpa: GrandpaId,
babe: BabeId,
im_online: ImOnlineId,
authority_discovery: AuthorityDiscoveryId,
) -> SessionKeys {
SessionKeys { grandpa, babe, im_online, authority_discovery }
}
fn staging_testnet_config_genesis() -> GenesisConfig {
// stash, controller, session-key
// generated with secret:
// for i in 1 2 3 4 ; do for j in stash controller; do subkey inspect "$secret"/fir/$j/$i; done; done
// and
// for i in 1 2 3 4 ; do for j in session; do subkey --ed25519 inspect "$secret"//fir//$j//$i; done; done
let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId)> = vec![(
// 5Fbsd6WXDGiLTxunqeK5BATNiocfCqu9bS1yArVjCgeBLkVy
hex!["9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"].into(),
// 5EnCiV7wSHeNhjW3FSUwiJNkcc2SBkPLn5Nj93FmbLtBjQUq
hex!["781ead1e2fa9ccb74b44c19d29cb2a7a4b5be3972927ae98cd3877523976a276"].into(),
// 5Fb9ayurnxnaXj56CjmyQLBiadfRCqUbL2VWNbbe1nZU6wiC
hex!["9becad03e6dcac03cee07edebca5475314861492cdfc96a2144a67bbe9699332"].unchecked_into(),
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(),
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(),
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(),
),(
// 5ERawXCzCWkjVq3xz1W5KGNtVx2VdefvZ62Bw1FEuZW4Vny2
hex!["68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"].into(),
// 5Gc4vr42hH1uDZc93Nayk5G7i687bAQdHHc9unLuyeawHipF
hex!["c8dc79e36b29395413399edaec3e20fcca7205fb19776ed8ddb25d6f427ec40e"].into(),
// 5EockCXN6YkiNCDjpqqnbcqd4ad35nU4RmA1ikM4YeRN4WcE
hex!["7932cff431e748892fa48e10c63c17d30f80ca42e4de3921e641249cd7fa3c2f"].unchecked_into(),
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(),
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(),
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(),
),(
// 5DyVtKWPidondEu8iHZgi6Ffv9yrJJ1NDNLom3X9cTDi98qp
hex!["547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"].into(),
// 5FeD54vGVNpFX3PndHPXJ2MDakc462vBCD5mgtWRnWYCpZU9
hex!["9e42241d7cd91d001773b0b616d523dd80e13c6c2cab860b1234ef1b9ffc1526"].into(),
// 5E1jLYfLdUQKrFrtqoKgFrRvxM3oQPMbf6DfcsrugZZ5Bn8d
hex!["5633b70b80a6c8bb16270f82cca6d56b27ed7b76c8fd5af2986a25a4788ce440"].unchecked_into(),
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(),
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(),
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(),
),(
// 5HYZnKWe5FVZQ33ZRJK1rG3WaLMztxWrrNDb1JRwaHHVWyP9
hex!["f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"].into(),
// 5EPQdAQ39WQNLCRjWsCk5jErsCitHiY5ZmjfWzzbXDoAoYbn
hex!["66bc1e5d275da50b72b15de072a2468a5ad414919ca9054d2695767cf650012f"].into(),
// 5DMa31Hd5u1dwoRKgC4uvqyrdK45RHv3CpwvpUC1EzuwDit4
hex!["3919132b851ef0fd2dae42a7e734fe547af5a6b809006100f48944d7fae8e8ef"].unchecked_into(),
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(),
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(),
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(),
)];
// generated with secret: subkey inspect "$secret"/fir
let root_key: AccountId = hex![
// 5Ff3iXP75ruzroPWRP2FYBHWnmGGBSb63857BgnzCoXNxfPo
"9ee5e5bdc0ec239eb164f865ecc345ce4c88e76ee002e0f7e318097347471809"
].into();
let endowed_accounts: Vec<AccountId> = vec![root_key.clone()];
testnet_genesis(
initial_authorities,
root_key,
Some(endowed_accounts),
false,
)
}
/// Staging testnet config.
pub fn staging_testnet_config() -> ChainSpec {
let boot_nodes = vec![];
ChainSpec::from_genesis(
"Staging Testnet",
"staging_testnet",
staging_testnet_config_genesis,
boot_nodes,
Some(TelemetryEndpoints::new(vec![(STAGING_TELEMETRY_URL.to_string(), 0)])),
None,
None,
Default::default(),
)
}
/// Helper function to generate a crypto pair from seed
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{}", seed), None)
.expect("static values are valid; qed")
.public()
}
/// Helper function to generate an account ID from seed
pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where
AccountPublic: From<<TPublic::Pair as Pair>::Public>
{
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}
/// Helper function to generate stash, controller and session key from seed
pub fn get_authority_keys_from_seed(seed: &str) -> (
AccountId,
AccountId,
GrandpaId,
BabeId,
ImOnlineId,
AuthorityDiscoveryId,
) {
(
get_account_id_from_seed::<sr25519::Public>(&format!("{}//stash", seed)),
get_account_id_from_seed::<sr25519::Public>(seed),
get_from_seed::<GrandpaId>(seed),
get_from_seed::<BabeId>(seed),
get_from_seed::<ImOnlineId>(seed),
get_from_seed::<AuthorityDiscoveryId>(seed),
)
}
/// Helper function to create GenesisConfig for testing
pub fn testnet_genesis(
initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId)>,
root_key: AccountId,
endowed_accounts: Option<Vec<AccountId>>,
enable_println: bool,
) -> GenesisConfig {
let endowed_accounts: Vec<AccountId> = endowed_accounts.unwrap_or_else(|| {
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
]
});
const ENDOWMENT: Balance = 10_000_000 * DOLLARS;
const STASH: Balance = 100 * DOLLARS;
GenesisConfig {
system: Some(SystemConfig {
code: WASM_BINARY.to_vec(),
changes_trie_config: Default::default(),
}),
balances: Some(BalancesConfig {
balances: endowed_accounts.iter().cloned()
.map(|k| (k, ENDOWMENT))
.chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH)))
.collect(),
vesting: vec![],
}),
indices: Some(IndicesConfig {
ids: endowed_accounts.iter().cloned()
.chain(initial_authorities.iter().map(|x| x.0.clone()))
.collect::<Vec<_>>(),
}),
session: Some(SessionConfig {
keys: initial_authorities.iter().map(|x| {
(x.0.clone(), session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone()))
}).collect::<Vec<_>>(),
}),
staking: Some(StakingConfig {
current_era: 0,
validator_count: initial_authorities.len() as u32 * 2,
minimum_validator_count: initial_authorities.len() as u32,
stakers: initial_authorities.iter().map(|x| {
(x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator)
}).collect(),
invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
slash_reward_fraction: Perbill::from_percent(10),
.. Default::default()
}),
democracy: Some(DemocracyConfig::default()),
collective_Instance1: Some(CouncilConfig {
members: vec![],
phantom: Default::default(),
}),
collective_Instance2: Some(TechnicalCommitteeConfig {
members: vec![],
phantom: Default::default(),
}),
contracts: Some(ContractsConfig {
current_schedule: contracts::Schedule {
enable_println, // this should only be enabled on development chains
..Default::default()
},
gas_price: 1 * MILLICENTS,
}),
sudo: Some(SudoConfig {
key: root_key,
}),
babe: Some(BabeConfig {
authorities: vec![],
}),
im_online: Some(ImOnlineConfig {
keys: vec![],
}),
authority_discovery: Some(AuthorityDiscoveryConfig {
keys: vec![],
}),
grandpa: Some(GrandpaConfig {
authorities: vec![],
}),
membership_Instance1: Some(Default::default()),
treasury: Some(Default::default()),
}
}
fn development_config_genesis() -> GenesisConfig {
testnet_genesis(
vec![
get_authority_keys_from_seed("Alice"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
None,
true,
)
}
/// Development config (single validator Alice)
pub fn development_config() -> ChainSpec {
ChainSpec::from_genesis(
"Development",
"dev",
development_config_genesis,
vec![],
None,
None,
None,
Default::default(),
)
}
fn local_testnet_genesis() -> GenesisConfig {
testnet_genesis(
vec![
get_authority_keys_from_seed("Alice"),
get_authority_keys_from_seed("Bob"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
None,
false,
)
}
/// Local testnet config (multivalidator Alice + Bob)
pub fn local_testnet_config() -> ChainSpec {
ChainSpec::from_genesis(
"Local Testnet",
"local_testnet",
local_testnet_genesis,
vec![],
None,
None,
None,
Default::default(),
)
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::service::new_full;
use substrate_service::Roles;
use service_test;
fn local_testnet_genesis_instant_single() -> GenesisConfig {
testnet_genesis(
vec![
get_authority_keys_from_seed("Alice"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
None,
false,
)
}
/// Local testnet config (single validator - Alice)
pub fn integration_test_config_with_single_authority() -> ChainSpec {
ChainSpec::from_genesis(
"Integration Test",
"test",
local_testnet_genesis_instant_single,
vec![],
None,
None,
None,
Default::default(),
)
}
/// Local testnet config (multivalidator Alice + Bob)
pub fn integration_test_config_with_two_authorities() -> ChainSpec {
ChainSpec::from_genesis(
"Integration Test",
"test",
local_testnet_genesis,
vec![],
None,
None,
None,
Default::default(),
)
}
#[test]
#[ignore]
fn test_connectivity() {
service_test::connectivity(
integration_test_config_with_two_authorities(),
|config| new_full(config),
|mut config| {
// light nodes are unsupported
config.roles = Roles::FULL;
new_full(config)
},
true,
);
}
}
+210
View File
@@ -0,0 +1,210 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
pub use substrate_cli::error;
use tokio::prelude::Future;
use tokio::runtime::{Builder as RuntimeBuilder, Runtime};
pub use substrate_cli::{VersionInfo, IntoExit, NoCustom, SharedParams, ExecutionStrategyParam};
use substrate_service::{AbstractService, Roles as ServiceRoles, Configuration};
use log::info;
use structopt::{StructOpt, clap::App};
use substrate_cli::{display_role, parse_and_prepare, AugmentClap, GetLogFilter, ParseAndPrepare};
use crate::{service, ChainSpec, load_spec};
use crate::factory_impl::FactoryState;
use transaction_factory::RuntimeAdapter;
use client::ExecutionStrategies;
/// Custom subcommands.
#[derive(Clone, Debug, StructOpt)]
pub enum CustomSubcommands {
/// The custom factory subcommmand for manufacturing transactions.
#[structopt(
name = "factory",
about = "Manufactures num transactions from Alice to random accounts. \
Only supported for development or local testnet."
)]
Factory(FactoryCmd),
}
impl GetLogFilter for CustomSubcommands {
fn get_log_filter(&self) -> Option<String> {
None
}
}
/// The `factory` command used to generate transactions.
/// Please note: this command currently only works on an empty database!
#[derive(Debug, StructOpt, Clone)]
pub struct FactoryCmd {
/// How often to repeat. This option only has an effect in mode `MasterToNToM`.
#[structopt(long="rounds", default_value = "1")]
pub rounds: u64,
/// MasterToN: Manufacture `num` transactions from the master account
/// to `num` randomly created accounts, one each.
///
/// MasterTo1: Manufacture `num` transactions from the master account
/// to exactly one other randomly created account.
///
/// MasterToNToM: Manufacture `num` transactions from the master account
/// to `num` randomly created accounts.
/// From each of these randomly created accounts manufacture
/// a transaction to another randomly created account.
/// Repeat this `rounds` times. If `rounds` = 1 the behavior
/// is the same as `MasterToN`.{n}
/// A -> B, A -> C, A -> D, ... x `num`{n}
/// B -> E, C -> F, D -> G, ...{n}
/// ... x `rounds`
///
/// These three modes control manufacturing.
#[structopt(long="mode", default_value = "MasterToN")]
pub mode: transaction_factory::Mode,
/// Number of transactions to generate. In mode `MasterNToNToM` this is
/// the number of transactions per round.
#[structopt(long="num", default_value = "8")]
pub num: u64,
#[allow(missing_docs)]
#[structopt(flatten)]
pub shared_params: SharedParams,
/// The means of execution used when calling into the runtime while importing blocks.
#[structopt(
long = "execution",
value_name = "STRATEGY",
possible_values = &ExecutionStrategyParam::variants(),
case_insensitive = true,
default_value = "NativeElseWasm"
)]
pub execution: ExecutionStrategyParam,
}
impl AugmentClap for FactoryCmd {
fn augment_clap<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
FactoryCmd::augment_clap(app)
}
}
/// Parse command line arguments into service configuration.
pub fn run<I, T, E>(args: I, exit: E, version: substrate_cli::VersionInfo) -> error::Result<()> where
I: IntoIterator<Item = T>,
T: Into<std::ffi::OsString> + Clone,
E: IntoExit,
{
type Config<A, B> = Configuration<(), A, B>;
match parse_and_prepare::<CustomSubcommands, NoCustom, _>(&version, "substrate-node", args) {
ParseAndPrepare::Run(cmd) => cmd.run(load_spec, exit,
|exit, _cli_args, _custom_args, config: Config<_, _>| {
info!("{}", version.name);
info!(" version {}", config.full_version());
info!(" by Parity Technologies, 2017-2019");
info!("Chain specification: {}", config.chain_spec.name());
info!("Node name: {}", config.name);
info!("Roles: {}", display_role(&config));
let runtime = RuntimeBuilder::new().name_prefix("main-tokio-").build()
.map_err(|e| format!("{:?}", e))?;
match config.roles {
ServiceRoles::LIGHT => run_until_exit(
runtime,
service::new_light(config)?,
exit
),
_ => run_until_exit(
runtime,
service::new_full(config)?,
exit
),
}
}),
ParseAndPrepare::BuildSpec(cmd) => cmd.run::<NoCustom, _, _, _>(load_spec),
ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(|config: Config<_, _>|
Ok(new_full_start!(config).0), load_spec, exit),
ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(|config: Config<_, _>|
Ok(new_full_start!(config).0), load_spec, exit),
ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec),
ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder(|config: Config<_, _>|
Ok(new_full_start!(config).0), load_spec),
ParseAndPrepare::CustomCommand(CustomSubcommands::Factory(cli_args)) => {
let mut config: Config<_, _> = substrate_cli::create_config_with_db_path(
load_spec,
&cli_args.shared_params,
&version,
)?;
config.execution_strategies = ExecutionStrategies {
importing: cli_args.execution.into(),
block_construction: cli_args.execution.into(),
other: cli_args.execution.into(),
..Default::default()
};
match ChainSpec::from(config.chain_spec.id()) {
Some(ref c) if c == &ChainSpec::Development || c == &ChainSpec::LocalTestnet => {},
_ => panic!("Factory is only supported for development and local testnet."),
}
let factory_state = FactoryState::new(
cli_args.mode.clone(),
cli_args.num,
cli_args.rounds,
);
let service_builder = new_full_start!(config).0;
transaction_factory::factory::<FactoryState<_>, _, _, _, _, _>(
factory_state,
service_builder.client(),
service_builder.select_chain()
.expect("The select_chain is always initialized by new_full_start!; QED")
).map_err(|e| format!("Error in transaction factory: {}", e))?;
Ok(())
}
}
}
fn run_until_exit<T, E>(
mut runtime: Runtime,
service: T,
e: E,
) -> error::Result<()>
where
T: AbstractService,
E: IntoExit,
{
let (exit_send, exit) = exit_future::signal();
let informant = substrate_cli::informant::build(&service);
runtime.executor().spawn(exit.until(informant).map(|_| ()));
// we eagerly drop the service so that the internal exit future is fired,
// but we need to keep holding a reference to the global telemetry guard
let _telemetry = service.telemetry();
let service_res = {
let exit = e.into_exit().map_err(|_| error::Error::Other("Exit future failed.".into()));
let service = service.map_err(|err| error::Error::Service(err));
let select = service.select(exit).map(|_| ()).map_err(|(err, _)| err);
runtime.block_on(select)
};
exit_send.fire();
// TODO [andre]: timeout this future #1318
let _ = runtime.shutdown_on_idle().wait();
service_res
}
+268
View File
@@ -0,0 +1,268 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Implementation of the transaction factory trait, which enables
//! using the cli to manufacture transactions and distribute them
//! to accounts.
use rand::{Rng, SeedableRng};
use rand::rngs::StdRng;
use codec::{Encode, Decode};
use keyring::sr25519::Keyring;
use node_runtime::{
Call, CheckedExtrinsic, UncheckedExtrinsic, SignedExtra, BalancesCall, ExistentialDeposit,
MinimumPeriod
};
use node_primitives::Signature;
use primitives::{sr25519, crypto::Pair};
use sr_primitives::{
generic::Era, traits::{Block as BlockT, Header as HeaderT, SignedExtension, Verify, IdentifyAccount}
};
use transaction_factory::RuntimeAdapter;
use transaction_factory::modes::Mode;
use inherents::InherentData;
use timestamp;
use finality_tracker;
type AccountPublic = <Signature as Verify>::Signer;
pub struct FactoryState<N> {
block_no: N,
mode: Mode,
start_number: u32,
rounds: u32,
round: u32,
block_in_round: u32,
num: u32,
}
type Number = <<node_primitives::Block as BlockT>::Header as HeaderT>::Number;
impl<Number> FactoryState<Number> {
fn build_extra(index: node_primitives::Index, phase: u64) -> node_runtime::SignedExtra {
(
system::CheckVersion::new(),
system::CheckGenesis::new(),
system::CheckEra::from(Era::mortal(256, phase)),
system::CheckNonce::from(index),
system::CheckWeight::new(),
transaction_payment::ChargeTransactionPayment::from(0),
Default::default(),
)
}
}
impl RuntimeAdapter for FactoryState<Number> {
type AccountId = node_primitives::AccountId;
type Balance = node_primitives::Balance;
type Block = node_primitives::Block;
type Phase = sr_primitives::generic::Phase;
type Secret = sr25519::Pair;
type Index = node_primitives::Index;
type Number = Number;
fn new(
mode: Mode,
num: u64,
rounds: u64,
) -> FactoryState<Self::Number> {
FactoryState {
mode,
num: num as u32,
round: 0,
rounds: rounds as u32,
block_in_round: 0,
block_no: 0,
start_number: 0,
}
}
fn block_no(&self) -> Self::Number {
self.block_no
}
fn block_in_round(&self) -> Self::Number {
self.block_in_round
}
fn rounds(&self) -> Self::Number {
self.rounds
}
fn num(&self) -> Self::Number {
self.num
}
fn round(&self) -> Self::Number {
self.round
}
fn start_number(&self) -> Self::Number {
self.start_number
}
fn mode(&self) -> &Mode {
&self.mode
}
fn set_block_no(&mut self, val: Self::Number) {
self.block_no = val;
}
fn set_block_in_round(&mut self, val: Self::Number) {
self.block_in_round = val;
}
fn set_round(&mut self, val: Self::Number) {
self.round = val;
}
fn transfer_extrinsic(
&self,
sender: &Self::AccountId,
key: &Self::Secret,
destination: &Self::AccountId,
amount: &Self::Balance,
version: u32,
genesis_hash: &<Self::Block as BlockT>::Hash,
prior_block_hash: &<Self::Block as BlockT>::Hash,
) -> <Self::Block as BlockT>::Extrinsic {
let index = self.extract_index(&sender, prior_block_hash);
let phase = self.extract_phase(*prior_block_hash);
sign::<Self>(CheckedExtrinsic {
signed: Some((sender.clone(), Self::build_extra(index, phase))),
function: Call::Balances(
BalancesCall::transfer(
indices::address::Address::Id(destination.clone().into()),
(*amount).into()
)
)
}, key, (version, genesis_hash.clone(), prior_block_hash.clone(), (), (), (), ()))
}
fn inherent_extrinsics(&self) -> InherentData {
let timestamp = (self.block_no as u64 + 1) * MinimumPeriod::get();
let mut inherent = InherentData::new();
inherent.put_data(timestamp::INHERENT_IDENTIFIER, &timestamp)
.expect("Failed putting timestamp inherent");
inherent.put_data(finality_tracker::INHERENT_IDENTIFIER, &self.block_no)
.expect("Failed putting finalized number inherent");
inherent
}
fn minimum_balance() -> Self::Balance {
ExistentialDeposit::get()
}
fn master_account_id() -> Self::AccountId {
Keyring::Alice.to_account_id()
}
fn master_account_secret() -> Self::Secret {
Keyring::Alice.pair()
}
/// Generates a random `AccountId` from `seed`.
fn gen_random_account_id(seed: &Self::Number) -> Self::AccountId {
let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(*seed));
AccountPublic::from(pair.public()).into_account()
}
/// Generates a random `Secret` from `seed`.
fn gen_random_account_secret(seed: &Self::Number) -> Self::Secret {
let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(*seed));
pair
}
fn extract_index(
&self,
_account_id: &Self::AccountId,
_block_hash: &<Self::Block as BlockT>::Hash,
) -> Self::Index {
// TODO get correct index for account via api. See #2587.
// This currently prevents the factory from being used
// without a preceding purge of the database.
if self.mode == Mode::MasterToN || self.mode == Mode::MasterTo1 {
self.block_no() as Self::Index
} else {
match self.round() {
0 =>
// if round is 0 all transactions will be done with master as a sender
self.block_no() as Self::Index,
_ =>
// if round is e.g. 1 every sender account will be new and not yet have
// any transactions done
0
}
}
}
fn extract_phase(
&self,
_block_hash: <Self::Block as BlockT>::Hash
) -> Self::Phase {
// TODO get correct phase via api. See #2587.
// This currently prevents the factory from being used
// without a preceding purge of the database.
self.block_no() as Self::Phase
}
}
fn gen_seed_bytes(seed: u32) -> [u8; 32] {
let mut rng: StdRng = SeedableRng::seed_from_u64(seed as u64);
let mut seed_bytes = [0u8; 32];
for i in 0..32 {
seed_bytes[i] = rng.gen::<u8>();
}
seed_bytes
}
/// Creates an `UncheckedExtrinsic` containing the appropriate signature for
/// a `CheckedExtrinsics`.
fn sign<RA: RuntimeAdapter>(
xt: CheckedExtrinsic,
key: &sr25519::Pair,
additional_signed: <SignedExtra as SignedExtension>::AdditionalSigned,
) -> <RA::Block as BlockT>::Extrinsic {
let s = match xt.signed {
Some((signed, extra)) => {
let payload = (xt.function, extra.clone(), additional_signed);
let signature = payload.using_encoded(|b| {
if b.len() > 256 {
key.sign(&runtime_io::hashing::blake2_256(b))
} else {
key.sign(b)
}
}).into();
UncheckedExtrinsic {
signature: Some((indices::address::Address::Id(signed), signature, extra)),
function: payload.0,
}
}
None => UncheckedExtrinsic {
signature: None,
function: xt.function,
},
};
let e = Encode::encode(&s);
Decode::decode(&mut &e[..]).expect("Failed to decode signed unchecked extrinsic")
}
+88
View File
@@ -0,0 +1,88 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate CLI library.
//!
//! This package has two Cargo features:
//!
//! - `cli` (default): exposes functions that parse command-line options, then start and run the
//! node as a CLI application.
//!
//! - `browser`: exposes the content of the `browser` module, which consists of exported symbols
//! that are meant to be passed through the `wasm-bindgen` utility and called from JavaScript.
//! Despite its name the produced WASM can theoretically also be used from NodeJS, although this
//! hasn't been tested.
#![warn(missing_docs)]
#![warn(unused_extern_crates)]
pub mod chain_spec;
#[macro_use]
mod service;
#[cfg(feature = "browser")]
mod browser;
#[cfg(feature = "cli")]
mod cli;
#[cfg(feature = "cli")]
mod factory_impl;
#[cfg(feature = "browser")]
pub use browser::*;
#[cfg(feature = "cli")]
pub use cli::*;
/// The chain specification option.
#[derive(Clone, Debug, PartialEq)]
pub enum ChainSpec {
/// Whatever the current runtime is, with just Alice as an auth.
Development,
/// Whatever the current runtime is, with simple Alice/Bob auths.
LocalTestnet,
/// The Flaming Fir testnet.
FlamingFir,
/// Whatever the current runtime is with the "global testnet" defaults.
StagingTestnet,
}
/// Get a chain config from a spec setting.
impl ChainSpec {
pub(crate) fn load(self) -> Result<chain_spec::ChainSpec, String> {
Ok(match self {
ChainSpec::FlamingFir => chain_spec::flaming_fir_config()?,
ChainSpec::Development => chain_spec::development_config(),
ChainSpec::LocalTestnet => chain_spec::local_testnet_config(),
ChainSpec::StagingTestnet => chain_spec::staging_testnet_config(),
})
}
pub(crate) fn from(s: &str) -> Option<Self> {
match s {
"dev" => Some(ChainSpec::Development),
"local" => Some(ChainSpec::LocalTestnet),
"" | "fir" | "flaming-fir" => Some(ChainSpec::FlamingFir),
"staging" => Some(ChainSpec::StagingTestnet),
_ => None,
}
}
}
fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
Ok(match ChainSpec::from(id) {
Some(spec) => Some(spec.load()?),
None => None,
})
}
+616
View File
@@ -0,0 +1,616 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
#![warn(unused_extern_crates)]
//! Service implementation. Specialized wrapper over substrate service.
use std::sync::Arc;
use babe;
use client::{self, LongestChain};
use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider};
use node_executor;
use node_primitives::Block;
use node_runtime::{GenesisConfig, RuntimeApi};
use substrate_service::{
AbstractService, ServiceBuilder, config::Configuration, error::{Error as ServiceError},
};
use transaction_pool::{self, txpool::{Pool as TransactionPool}};
use inherents::InherentDataProviders;
use network::construct_simple_protocol;
use substrate_service::{Service, NetworkStatus};
use client::{Client, LocalCallExecutor};
use client_db::Backend;
use sr_primitives::traits::Block as BlockT;
use node_executor::NativeExecutor;
use network::NetworkService;
use offchain::OffchainWorkers;
use primitives::Blake2Hasher;
construct_simple_protocol! {
/// Demo protocol attachment for substrate.
pub struct NodeProtocol where Block = Block { }
}
/// Starts a `ServiceBuilder` for a full service.
///
/// Use this macro if you don't actually need the full service, but just the builder in order to
/// be able to perform chain operations.
macro_rules! new_full_start {
($config:expr) => {{
type RpcExtension = jsonrpc_core::IoHandler<substrate_rpc::Metadata>;
let mut import_setup = None;
let inherent_data_providers = inherents::InherentDataProviders::new();
let builder = substrate_service::ServiceBuilder::new_full::<
node_primitives::Block, node_runtime::RuntimeApi, node_executor::Executor
>($config)?
.with_select_chain(|_config, backend| {
Ok(client::LongestChain::new(backend.clone()))
})?
.with_transaction_pool(|config, client|
Ok(transaction_pool::txpool::Pool::new(config, transaction_pool::FullChainApi::new(client)))
)?
.with_import_queue(|_config, client, mut select_chain, _transaction_pool| {
let select_chain = select_chain.take()
.ok_or_else(|| substrate_service::Error::SelectChainRequired)?;
let (grandpa_block_import, grandpa_link) = grandpa::block_import(
client.clone(),
&*client,
select_chain,
)?;
let justification_import = grandpa_block_import.clone();
let (block_import, babe_link) = babe::block_import(
babe::Config::get_or_compute(&*client)?,
grandpa_block_import,
client.clone(),
client.clone(),
)?;
let import_queue = babe::import_queue(
babe_link.clone(),
block_import.clone(),
Some(Box::new(justification_import)),
None,
client.clone(),
client,
inherent_data_providers.clone(),
)?;
import_setup = Some((block_import, grandpa_link, babe_link));
Ok(import_queue)
})?
.with_rpc_extensions(|client, pool, _backend| -> RpcExtension {
node_rpc::create(client, pool)
})?;
(builder, import_setup, inherent_data_providers)
}}
}
/// Creates a full service from the configuration.
///
/// We need to use a macro because the test suit doesn't work with an opaque service. It expects
/// concrete types instead.
macro_rules! new_full {
($config:expr, $with_startup_data: expr) => {{
use futures::sync::mpsc;
use network::DhtEvent;
use futures03::{
compat::Stream01CompatExt,
stream::StreamExt,
future::{FutureExt, TryFutureExt},
};
let (
is_authority,
force_authoring,
name,
disable_grandpa
) = (
$config.roles.is_authority(),
$config.force_authoring,
$config.name.clone(),
$config.disable_grandpa
);
// sentry nodes announce themselves as authorities to the network
// and should run the same protocols authorities do, but it should
// never actively participate in any consensus process.
let participates_in_consensus = is_authority && !$config.sentry_mode;
let (builder, mut import_setup, inherent_data_providers) = new_full_start!($config);
// Dht event channel from the network to the authority discovery module. Use bounded channel to ensure
// back-pressure. Authority discovery is triggering one event per authority within the current authority set.
// This estimates the authority set size to be somewhere below 10 000 thereby setting the channel buffer size to
// 10 000.
let (dht_event_tx, dht_event_rx) =
mpsc::channel::<DhtEvent>(10_000);
let service = builder.with_network_protocol(|_| Ok(crate::service::NodeProtocol::new()))?
.with_finality_proof_provider(|client, backend|
Ok(Arc::new(grandpa::FinalityProofProvider::new(backend, client)) as _)
)?
.with_dht_event_tx(dht_event_tx)?
.build()?;
let (block_import, grandpa_link, babe_link) = import_setup.take()
.expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
($with_startup_data)(&block_import, &babe_link);
if participates_in_consensus {
let proposer = substrate_basic_authorship::ProposerFactory {
client: service.client(),
transaction_pool: service.transaction_pool(),
};
let client = service.client();
let select_chain = service.select_chain()
.ok_or(substrate_service::Error::SelectChainRequired)?;
let babe_config = babe::BabeParams {
keystore: service.keystore(),
client,
select_chain,
env: proposer,
block_import,
sync_oracle: service.network(),
inherent_data_providers: inherent_data_providers.clone(),
force_authoring,
babe_link,
};
let babe = babe::start_babe(babe_config)?;
service.spawn_essential_task(babe);
let future03_dht_event_rx = dht_event_rx.compat()
.map(|x| x.expect("<mpsc::channel::Receiver as Stream> never returns an error; qed"))
.boxed();
let authority_discovery = authority_discovery::AuthorityDiscovery::new(
service.client(),
service.network(),
service.keystore(),
future03_dht_event_rx,
);
let future01_authority_discovery = authority_discovery.map(|x| Ok(x)).compat();
service.spawn_task(future01_authority_discovery);
}
// if the node isn't actively participating in consensus then it doesn't
// need a keystore, regardless of which protocol we use below.
let keystore = if participates_in_consensus {
Some(service.keystore())
} else {
None
};
let config = grandpa::Config {
// FIXME #1578 make this available through chainspec
gossip_duration: std::time::Duration::from_millis(333),
justification_period: 512,
name: Some(name),
observer_enabled: true,
keystore,
is_authority,
};
match (is_authority, disable_grandpa) {
(false, false) => {
// start the lightweight GRANDPA observer
service.spawn_task(grandpa::run_grandpa_observer(
config,
grandpa_link,
service.network(),
service.on_exit(),
)?);
},
(true, false) => {
// start the full GRANDPA voter
let grandpa_config = grandpa::GrandpaParams {
config: config,
link: grandpa_link,
network: service.network(),
inherent_data_providers: inherent_data_providers.clone(),
on_exit: service.on_exit(),
telemetry_on_connect: Some(service.telemetry_on_connect_stream()),
voting_rule: grandpa::VotingRulesBuilder::default().build(),
};
// the GRANDPA voter task is considered infallible, i.e.
// if it fails we take down the service with it.
service.spawn_essential_task(grandpa::run_grandpa_voter(grandpa_config)?);
},
(_, true) => {
grandpa::setup_disabled_grandpa(
service.client(),
&inherent_data_providers,
service.network(),
)?;
},
}
Ok((service, inherent_data_providers))
}};
($config:expr) => {{
new_full!($config, |_, _| {})
}}
}
#[allow(dead_code)]
type ConcreteBlock = node_primitives::Block;
#[allow(dead_code)]
type ConcreteClient =
Client<
Backend<ConcreteBlock>,
LocalCallExecutor<Backend<ConcreteBlock>,
NativeExecutor<node_executor::Executor>>,
ConcreteBlock,
node_runtime::RuntimeApi
>;
#[allow(dead_code)]
type ConcreteBackend = Backend<ConcreteBlock>;
/// A specialized configuration object for setting up the node..
pub type NodeConfiguration<C> = Configuration<C, GenesisConfig, crate::chain_spec::Extensions>;
/// Builds a new service for a full client.
pub fn new_full<C: Send + Default + 'static>(config: NodeConfiguration<C>)
-> Result<
Service<
ConcreteBlock,
ConcreteClient,
LongestChain<ConcreteBackend, ConcreteBlock>,
NetworkStatus<ConcreteBlock>,
NetworkService<ConcreteBlock, crate::service::NodeProtocol, <ConcreteBlock as BlockT>::Hash>,
TransactionPool<transaction_pool::FullChainApi<ConcreteClient, ConcreteBlock>>,
OffchainWorkers<
ConcreteClient,
<ConcreteBackend as client_api::backend::Backend<Block, Blake2Hasher>>::OffchainStorage,
ConcreteBlock,
>
>,
ServiceError,
>
{
new_full!(config).map(|(service, _)| service)
}
/// Builds a new service for a light client.
pub fn new_light<C: Send + Default + 'static>(config: NodeConfiguration<C>)
-> Result<impl AbstractService, ServiceError> {
type RpcExtension = jsonrpc_core::IoHandler<substrate_rpc::Metadata>;
let inherent_data_providers = InherentDataProviders::new();
let service = ServiceBuilder::new_light::<Block, RuntimeApi, node_executor::Executor>(config)?
.with_select_chain(|_config, backend| {
Ok(LongestChain::new(backend.clone()))
})?
.with_transaction_pool(|config, client|
Ok(TransactionPool::new(config, transaction_pool::FullChainApi::new(client)))
)?
.with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| {
let fetch_checker = fetcher
.map(|fetcher| fetcher.checker().clone())
.ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
let grandpa_block_import = grandpa::light_block_import::<_, _, _, RuntimeApi>(
client.clone(),
backend,
&*client,
Arc::new(fetch_checker),
)?;
let finality_proof_import = grandpa_block_import.clone();
let finality_proof_request_builder =
finality_proof_import.create_finality_proof_request_builder();
let (babe_block_import, babe_link) = babe::block_import(
babe::Config::get_or_compute(&*client)?,
grandpa_block_import,
client.clone(),
client.clone(),
)?;
let import_queue = babe::import_queue(
babe_link,
babe_block_import,
None,
Some(Box::new(finality_proof_import)),
client.clone(),
client,
inherent_data_providers.clone(),
)?;
Ok((import_queue, finality_proof_request_builder))
})?
.with_network_protocol(|_| Ok(NodeProtocol::new()))?
.with_finality_proof_provider(|client, backend|
Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
)?
.with_rpc_extensions(|client, pool, _backend| -> RpcExtension {
node_rpc::create(client, pool)
})?
.build()?;
Ok(service)
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use babe::CompatibleDigestItem;
use consensus_common::{
Environment, Proposer, BlockImportParams, BlockOrigin, ForkChoiceStrategy, BlockImport,
};
use node_primitives::{Block, DigestItem, Signature};
use node_runtime::{BalancesCall, Call, UncheckedExtrinsic, Address};
use node_runtime::constants::{currency::CENTS, time::SLOT_DURATION};
use codec::{Encode, Decode};
use primitives::{crypto::Pair as CryptoPair, H256};
use sr_primitives::{
generic::{BlockId, Era, Digest, SignedPayload},
traits::Block as BlockT,
traits::Verify,
OpaqueExtrinsic,
};
use timestamp;
use finality_tracker;
use keyring::AccountKeyring;
use substrate_service::{AbstractService, Roles};
use crate::service::new_full;
use sr_primitives::traits::IdentifyAccount;
type AccountPublic = <Signature as Verify>::Signer;
#[cfg(feature = "rhd")]
fn test_sync() {
use primitives::ed25519::Pair;
use {service_test, Factory};
use client::{BlockImportParams, BlockOrigin};
let alice: Arc<ed25519::Pair> = Arc::new(Keyring::Alice.into());
let bob: Arc<ed25519::Pair> = Arc::new(Keyring::Bob.into());
let validators = vec![alice.public().0.into(), bob.public().0.into()];
let keys: Vec<&ed25519::Pair> = vec![&*alice, &*bob];
let dummy_runtime = ::tokio::runtime::Runtime::new().unwrap();
let block_factory = |service: &<Factory as service::ServiceFactory>::FullService| {
let block_id = BlockId::number(service.client().info().chain.best_number);
let parent_header = service.client().header(&block_id).unwrap().unwrap();
let consensus_net = ConsensusNetwork::new(service.network(), service.client().clone());
let proposer_factory = consensus::ProposerFactory {
client: service.client().clone(),
transaction_pool: service.transaction_pool().clone(),
network: consensus_net,
force_delay: 0,
handle: dummy_runtime.executor(),
};
let (proposer, _, _) = proposer_factory.init(&parent_header, &validators, alice.clone()).unwrap();
let block = proposer.propose().expect("Error making test block");
BlockImportParams {
origin: BlockOrigin::File,
justification: Vec::new(),
internal_justification: Vec::new(),
finalized: false,
body: Some(block.extrinsics),
header: block.header,
auxiliary: Vec::new(),
}
};
let extrinsic_factory =
|service: &SyncService<<Factory as service::ServiceFactory>::FullService>|
{
let payload = (
0,
Call::Balances(BalancesCall::transfer(RawAddress::Id(bob.public().0.into()), 69.into())),
Era::immortal(),
service.client().genesis_hash()
);
let signature = alice.sign(&payload.encode()).into();
let id = alice.public().0.into();
let xt = UncheckedExtrinsic {
signature: Some((RawAddress::Id(id), signature, payload.0, Era::immortal())),
function: payload.1,
}.encode();
let v: Vec<u8> = Decode::decode(&mut xt.as_slice()).unwrap();
OpaqueExtrinsic(v)
};
service_test::sync(
chain_spec::integration_test_config(),
|config| new_full(config),
|mut config| {
// light nodes are unsupported
config.roles = Roles::FULL;
new_full(config)
},
block_factory,
extrinsic_factory,
);
}
#[test]
#[ignore]
fn test_sync() {
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
let keystore = keystore::Store::open(keystore_path.path(), None)
.expect("Creates keystore");
let alice = keystore.write().insert_ephemeral_from_seed::<babe::AuthorityPair>("//Alice")
.expect("Creates authority pair");
let chain_spec = crate::chain_spec::tests::integration_test_config_with_single_authority();
// For the block factory
let mut slot_num = 1u64;
// For the extrinsics factory
let bob = Arc::new(AccountKeyring::Bob.pair());
let charlie = Arc::new(AccountKeyring::Charlie.pair());
let mut index = 0;
service_test::sync(
chain_spec,
|config| {
let mut setup_handles = None;
new_full!(config, |
block_import: &babe::BabeBlockImport<_, _, Block, _, _, _>,
babe_link: &babe::BabeLink<Block>,
| {
setup_handles = Some((block_import.clone(), babe_link.clone()));
}).map(move |(node, x)| (node, (x, setup_handles.unwrap())))
},
|mut config| {
// light nodes are unsupported
config.roles = Roles::FULL;
new_full(config)
},
|service, &mut (ref inherent_data_providers, (ref mut block_import, ref babe_link))| {
let mut inherent_data = inherent_data_providers
.create_inherent_data()
.expect("Creates inherent data.");
inherent_data.replace_data(finality_tracker::INHERENT_IDENTIFIER, &1u64);
let parent_id = BlockId::number(service.client().info().chain.best_number);
let parent_header = service.client().header(&parent_id).unwrap().unwrap();
let mut proposer_factory = substrate_basic_authorship::ProposerFactory {
client: service.client(),
transaction_pool: service.transaction_pool(),
};
let mut digest = Digest::<H256>::default();
// even though there's only one authority some slots might be empty,
// so we must keep trying the next slots until we can claim one.
let babe_pre_digest = loop {
inherent_data.replace_data(timestamp::INHERENT_IDENTIFIER, &(slot_num * SLOT_DURATION));
if let Some(babe_pre_digest) = babe::test_helpers::claim_slot(
slot_num,
&parent_header,
&*service.client(),
&keystore,
&babe_link,
) {
break babe_pre_digest;
}
slot_num += 1;
};
digest.push(<DigestItem as CompatibleDigestItem>::babe_pre_digest(babe_pre_digest));
let mut proposer = proposer_factory.init(&parent_header).unwrap();
let new_block = futures03::executor::block_on(proposer.propose(
inherent_data,
digest,
std::time::Duration::from_secs(1),
)).expect("Error making test block");
let (new_header, new_body) = new_block.deconstruct();
let pre_hash = new_header.hash();
// sign the pre-sealed hash of the block and then
// add it to a digest item.
let to_sign = pre_hash.encode();
let signature = alice.sign(&to_sign[..]);
let item = <DigestItem as CompatibleDigestItem>::babe_seal(
signature.into(),
);
slot_num += 1;
let params = BlockImportParams {
origin: BlockOrigin::File,
header: new_header,
justification: None,
post_digests: vec![item],
body: Some(new_body),
finalized: false,
auxiliary: Vec::new(),
fork_choice: ForkChoiceStrategy::LongestChain,
allow_missing_state: false,
};
block_import.import_block(params, Default::default())
.expect("error importing test block");
},
|service, _| {
let amount = 5 * CENTS;
let to: Address = AccountPublic::from(bob.public()).into_account().into();
let from: Address = AccountPublic::from(charlie.public()).into_account().into();
let genesis_hash = service.client().block_hash(0).unwrap().unwrap();
let best_block_id = BlockId::number(service.client().info().chain.best_number);
let version = service.client().runtime_version_at(&best_block_id).unwrap().spec_version;
let signer = charlie.clone();
let function = Call::Balances(BalancesCall::transfer(to.into(), amount));
let check_version = system::CheckVersion::new();
let check_genesis = system::CheckGenesis::new();
let check_era = system::CheckEra::from(Era::Immortal);
let check_nonce = system::CheckNonce::from(index);
let check_weight = system::CheckWeight::new();
let payment = transaction_payment::ChargeTransactionPayment::from(0);
let extra = (
check_version,
check_genesis,
check_era,
check_nonce,
check_weight,
payment,
Default::default(),
);
let raw_payload = SignedPayload::from_raw(
function,
extra,
(version, genesis_hash, genesis_hash, (), (), (), ())
);
let signature = raw_payload.using_encoded(|payload| {
signer.sign(payload)
});
let (function, extra, _) = raw_payload.deconstruct();
let xt = UncheckedExtrinsic::new_signed(
function,
from.into(),
signature.into(),
extra,
).encode();
let v: Vec<u8> = Decode::decode(&mut xt.as_slice()).unwrap();
index += 1;
OpaqueExtrinsic(v)
},
);
}
#[test]
#[ignore]
fn test_consensus() {
service_test::consensus(
crate::chain_spec::tests::integration_test_config_with_two_authorities(),
|config| new_full(config),
|mut config| {
// light nodes are unsupported
config.roles = Roles::FULL;
new_full(config)
},
vec![
"//Alice".into(),
"//Bob".into(),
],
)
}
}
+48
View File
@@ -0,0 +1,48 @@
[package]
name = "node-executor"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Substrate node implementation in Rust."
edition = "2018"
[dependencies]
trie-root = "0.15.2"
codec = { package = "parity-scale-codec", version = "1.0.0" }
runtime_io = { package = "sr-io", path = "../../../primitives/sr-io" }
state_machine = { package = "substrate-state-machine", path = "../../../primitives/state-machine" }
substrate-executor = { path = "../../../client/executor" }
primitives = { package = "substrate-primitives", path = "../../../primitives/core" }
trie = { package = "substrate-trie", path = "../../../primitives/trie" }
node-primitives = { path = "../primitives" }
node-runtime = { path = "../runtime" }
[dev-dependencies]
node-testing = { path = "../testing" }
test-client = { package = "substrate-test-client", path = "../../../test/utils/client" }
sr-primitives = { path = "../../../primitives/sr-primitives" }
runtime_support = { package = "paint-support", path = "../../../paint/support" }
balances = { package = "paint-balances", path = "../../../paint/balances" }
transaction-payment = { package = "paint-transaction-payment", path = "../../../paint/transaction-payment" }
session = { package = "paint-session", path = "../../../paint/session" }
system = { package = "paint-system", path = "../../../paint/system" }
timestamp = { package = "paint-timestamp", path = "../../../paint/timestamp" }
treasury = { package = "paint-treasury", path = "../../../paint/treasury" }
contracts = { package = "paint-contracts", path = "../../../paint/contracts" }
grandpa = { package = "paint-grandpa", path = "../../../paint/grandpa" }
indices = { package = "paint-indices", path = "../../../paint/indices" }
wabt = "0.9.2"
criterion = "0.3.0"
[features]
wasmtime = [
"substrate-executor/wasmtime",
]
wasmi-errno = [
"substrate-executor/wasmi-errno",
]
stress-test = []
[[bench]]
name = "bench"
harness = false
@@ -0,0 +1,196 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use codec::{Decode, Encode};
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
use node_executor::Executor;
use node_primitives::{BlockNumber, Hash};
use node_runtime::{
Block, BuildStorage, Call, CheckedExtrinsic, GenesisConfig, Header, UncheckedExtrinsic,
};
use node_runtime::constants::currency::*;
use node_testing::keyring::*;
use primitives::{Blake2Hasher, NativeOrEncoded, NeverNativeValue};
use primitives::storage::well_known_keys;
use primitives::traits::CodeExecutor;
use runtime_support::Hashable;
use state_machine::TestExternalities as CoreTestExternalities;
use substrate_executor::{NativeExecutor, RuntimeInfo, WasmExecutionMethod, Externalities};
criterion_group!(benches, bench_execute_block);
criterion_main!(benches);
/// The wasm runtime code.
const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY;
const GENESIS_HASH: [u8; 32] = [69u8; 32];
const VERSION: u32 = node_runtime::VERSION.spec_version;
const HEAP_PAGES: u64 = 20;
type TestExternalities<H> = CoreTestExternalities<H, u64>;
#[derive(Debug)]
enum ExecutionMethod {
Native,
Wasm(WasmExecutionMethod),
}
fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
node_testing::keyring::sign(xt, VERSION, GENESIS_HASH)
}
fn new_test_ext(genesis_config: &GenesisConfig) -> TestExternalities<Blake2Hasher> {
let mut test_ext = TestExternalities::new_with_code(
COMPACT_CODE,
genesis_config.build_storage().unwrap(),
);
test_ext.ext().place_storage(well_known_keys::HEAP_PAGES.to_vec(), Some(HEAP_PAGES.encode()));
test_ext
}
fn construct_block<E: Externalities>(
executor: &NativeExecutor<Executor>,
ext: &mut E,
number: BlockNumber,
parent_hash: Hash,
extrinsics: Vec<CheckedExtrinsic>,
) -> (Vec<u8>, Hash) {
use trie::{TrieConfiguration, trie_types::Layout};
// sign extrinsics.
let extrinsics = extrinsics.into_iter().map(sign).collect::<Vec<_>>();
// calculate the header fields that we can.
let extrinsics_root = Layout::<Blake2Hasher>::ordered_trie_root(
extrinsics.iter().map(Encode::encode)
).to_fixed_bytes()
.into();
let header = Header {
parent_hash,
number,
extrinsics_root,
state_root: Default::default(),
digest: Default::default(),
};
// execute the block to get the real header.
executor.call::<_, NeverNativeValue, fn() -> _>(
ext,
"Core_initialize_block",
&header.encode(),
true,
None,
).0.unwrap();
for i in extrinsics.iter() {
executor.call::<_, NeverNativeValue, fn() -> _>(
ext,
"BlockBuilder_apply_extrinsic",
&i.encode(),
true,
None,
).0.unwrap();
}
let header = match executor.call::<_, NeverNativeValue, fn() -> _>(
ext,
"BlockBuilder_finalize_block",
&[0u8;0],
true,
None,
).0.unwrap() {
NativeOrEncoded::Native(_) => unreachable!(),
NativeOrEncoded::Encoded(h) => Header::decode(&mut &h[..]).unwrap(),
};
let hash = header.blake2_256();
(Block { header, extrinsics }.encode(), hash.into())
}
fn test_blocks(genesis_config: &GenesisConfig, executor: &NativeExecutor<Executor>)
-> Vec<(Vec<u8>, Hash)>
{
let mut test_ext = new_test_ext(genesis_config);
let mut block1_extrinsics = vec![
CheckedExtrinsic {
signed: None,
function: Call::Timestamp(timestamp::Call::set(42 * 1000)),
},
];
block1_extrinsics.extend((0..20).map(|i| {
CheckedExtrinsic {
signed: Some((alice(), signed_extra(i, 0))),
function: Call::Balances(balances::Call::transfer(bob().into(), 1 * DOLLARS)),
}
}));
let block1 = construct_block(
executor,
&mut test_ext.ext(),
1,
GENESIS_HASH.into(),
block1_extrinsics,
);
vec![block1]
}
fn bench_execute_block(c: &mut Criterion) {
c.bench_function_over_inputs(
"execute blocks",
|b, strategy| {
let genesis_config = node_testing::genesis::config(false, Some(COMPACT_CODE));
let (use_native, wasm_method) = match strategy {
ExecutionMethod::Native => (true, WasmExecutionMethod::Interpreted),
ExecutionMethod::Wasm(wasm_method) => (false, *wasm_method),
};
let executor = NativeExecutor::new(wasm_method, None);
// Get the runtime version to initialize the runtimes cache.
{
let mut test_ext = new_test_ext(&genesis_config);
executor.runtime_version(&mut test_ext.ext());
}
let blocks = test_blocks(&genesis_config, &executor);
b.iter_batched_ref(
|| new_test_ext(&genesis_config),
|test_ext| {
for block in blocks.iter() {
executor.call::<_, NeverNativeValue, fn() -> _>(
&mut test_ext.ext(),
"Core_execute_block",
&block.0,
use_native,
None,
).0.unwrap();
}
},
BatchSize::LargeInput,
);
},
vec![
ExecutionMethod::Native,
ExecutionMethod::Wasm(WasmExecutionMethod::Interpreted),
#[cfg(feature = "wasmtime")]
ExecutionMethod::Wasm(WasmExecutionMethod::Compiled),
],
);
}
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "node-primitives"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
primitives = { package = "substrate-primitives", path = "../../../primitives/core", default-features = false }
sr-primitives = { path = "../../../primitives/sr-primitives", default-features = false }
[dev-dependencies]
substrate-serializer = { path = "../../../primitives/serializer" }
pretty_assertions = "0.6.1"
[features]
default = ["std"]
std = [
"primitives/std",
"sr-primitives/std",
]
+64
View File
@@ -0,0 +1,64 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Low-level types used throughout the Substrate code.
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
use sr_primitives::{
generic, traits::{Verify, BlakeTwo256, IdentifyAccount}, OpaqueExtrinsic, MultiSignature
};
/// An index to a block.
pub type BlockNumber = u32;
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = MultiSignature;
/// Some way of identifying an account on the chain. We intentionally make it equivalent
/// to the public key of our transaction signing scheme.
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
/// The type for looking up accounts. We don't expect more than 4 billion of them.
pub type AccountIndex = u32;
/// Balance of an account.
pub type Balance = u128;
/// Type used for expressing timestamp.
pub type Moment = u64;
/// Index of a transaction in the chain.
pub type Index = u32;
/// A hash of some data used by the chain.
pub type Hash = primitives::H256;
/// A timestamp: milliseconds since the unix epoch.
/// `u64` is enough to represent a duration of half a billion years, when the
/// time scale is milliseconds.
pub type Timestamp = u64;
/// Digest item type.
pub type DigestItem = generic::DigestItem<Hash>;
/// Header type.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Block type.
pub type Block = generic::Block<Header, OpaqueExtrinsic>;
/// Block ID.
pub type BlockId = generic::BlockId<Block>;
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "node-rpc-client"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
env_logger = "0.7.0"
futures = "0.1.29"
hyper = "0.12.35"
jsonrpc-core-client = { version = "14.0.3", features = ["http", "ws"] }
log = "0.4.8"
node-primitives = { path = "../primitives" }
substrate-rpc = { path = "../../../client/rpc", version = "2.0.0" }
+70
View File
@@ -0,0 +1,70 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
#![warn(missing_docs)]
//! Example substrate RPC client code.
//!
//! This module shows how you can write a Rust RPC client that connects to a running
//! substrate node and use staticly typed RPC wrappers.
use futures::Future;
use hyper::rt;
use node_primitives::Hash;
use substrate_rpc::author::{
AuthorClient,
hash::ExtrinsicOrHash,
};
use jsonrpc_core_client::{
transports::http,
RpcError,
};
fn main() {
env_logger::init();
rt::run(rt::lazy(|| {
let uri = "http://localhost:9933";
http::connect(uri)
.and_then(|client: AuthorClient<Hash, Hash>| {
remove_all_extrinsics(client)
})
.map_err(|e| {
println!("Error: {:?}", e);
})
}))
}
/// Remove all pending extrinsics from the node.
///
/// The example code takes `AuthorClient` and first:
/// 1. Calls the `pending_extrinsics` method to get all extrinsics in the pool.
/// 2. Then calls `remove_extrinsic` passing the obtained raw extrinsics.
///
/// As the resul of running the code the entire content of the transaction pool is going
/// to be removed and the extrinsics are going to be temporarily banned.
fn remove_all_extrinsics(client: AuthorClient<Hash, Hash>) -> impl Future<Item=(), Error=RpcError> {
client.pending_extrinsics()
.and_then(move |pending| {
client.remove_extrinsic(
pending.into_iter().map(|tx| ExtrinsicOrHash::Extrinsic(tx.into())).collect()
)
})
.map(|removed| {
println!("Removed extrinsics: {:?}", removed);
})
}
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "node-rpc"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
client = { package = "substrate-client", path = "../../../client/" }
jsonrpc-core = "14.0.3"
node-primitives = { path = "../primitives" }
node-runtime = { path = "../runtime" }
sr-primitives = { path = "../../../primitives/sr-primitives" }
paint-contracts-rpc = { path = "../../../paint/contracts/rpc/" }
paint-transaction-payment-rpc = { path = "../../../paint/transaction-payment/rpc/" }
paint-system-rpc = { path = "../../../paint/system/rpc/" }
transaction_pool = { package = "substrate-transaction-pool", path = "../../../client/transaction-pool" }
+65
View File
@@ -0,0 +1,65 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! A collection of node-specific RPC methods.
//!
//! Since `substrate` core functionality makes no assumptions
//! about the modules used inside the runtime, so do
//! RPC methods defined in `substrate-rpc` crate.
//! It means that `client/rpc` can't have any methods that
//! need some strong assumptions about the particular runtime.
//!
//! The RPCs available in this crate however can make some assumptions
//! about how the runtime is constructed and what `SRML` modules
//! are part of it. Therefore all node-runtime-specific RPCs can
//! be placed here or imported from corresponding `SRML` RPC definitions.
#![warn(missing_docs)]
use std::sync::Arc;
use node_primitives::{Block, AccountId, Index, Balance};
use node_runtime::UncheckedExtrinsic;
use sr_primitives::traits::ProvideRuntimeApi;
use transaction_pool::txpool::{ChainApi, Pool};
/// Instantiate all RPC extensions.
pub fn create<C, P, M>(client: Arc<C>, pool: Arc<Pool<P>>) -> jsonrpc_core::IoHandler<M> where
C: ProvideRuntimeApi,
C: client::blockchain::HeaderBackend<Block>,
C: Send + Sync + 'static,
C::Api: paint_system_rpc::AccountNonceApi<Block, AccountId, Index>,
C::Api: paint_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance>,
C::Api: paint_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance, UncheckedExtrinsic>,
P: ChainApi + Sync + Send + 'static,
M: jsonrpc_core::Metadata + Default,
{
use paint_system_rpc::{System, SystemApi};
use paint_contracts_rpc::{Contracts, ContractsApi};
use paint_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi};
let mut io = jsonrpc_core::IoHandler::default();
io.extend_with(
SystemApi::to_delegate(System::new(client.clone(), pool))
);
io.extend_with(
ContractsApi::to_delegate(Contracts::new(client.clone()))
);
io.extend_with(
TransactionPaymentApi::to_delegate(TransactionPayment::new(client))
);
io
}
+122
View File
@@ -0,0 +1,122 @@
[package]
name = "node-runtime"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
build = "build.rs"
[dependencies]
# third-party dependencies
codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false, features = ["derive"] }
integer-sqrt = { version = "0.1.2" }
safe-mix = { version = "1.0", default-features = false }
rustc-hex = { version = "2.0", optional = true }
serde = { version = "1.0.102", optional = true }
# primitives
authority-discovery-primitives = { package = "substrate-authority-discovery-primitives", path = "../../../primitives/authority-discovery", default-features = false }
babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../../primitives/consensus/babe", default-features = false }
block-builder-api = { package = "substrate-block-builder-runtime-api", path = "../../../primitives/block-builder/runtime-api", default-features = false}
inherents = { package = "substrate-inherents", path = "../../../primitives/inherents", default-features = false }
node-primitives = { path = "../primitives", default-features = false }
offchain-primitives = { package = "substrate-offchain-primitives", path = "../../../primitives/offchain", default-features = false }
primitives = { package = "substrate-primitives", path = "../../../primitives/core", default-features = false }
rstd = { package = "sr-std", path = "../../../primitives/sr-std", default-features = false }
sr-api = { path = "../../../primitives/sr-api", default-features = false }
sr-primitives = { path = "../../../primitives/sr-primitives", default-features = false }
sr-staking-primitives = { path = "../../../primitives/sr-staking-primitives", default-features = false }
substrate-keyring = { path = "../../../primitives/keyring", optional = true }
substrate-session = { path = "../../../primitives/session", default-features = false }
tx-pool-api = { package = "substrate-transaction-pool-runtime-api", path = "../../../primitives/transaction-pool/runtime-api", default-features = false }
version = { package = "sr-version", path = "../../../primitives/sr-version", default-features = false }
# paint dependencies
authority-discovery = { package = "paint-authority-discovery", path = "../../../paint/authority-discovery", default-features = false }
authorship = { package = "paint-authorship", path = "../../../paint/authorship", default-features = false }
babe = { package = "paint-babe", path = "../../../paint/babe", default-features = false }
balances = { package = "paint-balances", path = "../../../paint/balances", default-features = false }
collective = { package = "paint-collective", path = "../../../paint/collective", default-features = false }
contracts = { package = "paint-contracts", path = "../../../paint/contracts", default-features = false }
contracts-rpc-runtime-api = { package = "paint-contracts-rpc-runtime-api", path = "../../../paint/contracts/rpc/runtime-api/", default-features = false }
democracy = { package = "paint-democracy", path = "../../../paint/democracy", default-features = false }
elections-phragmen = { package = "paint-elections-phragmen", path = "../../../paint/elections-phragmen", default-features = false }
executive = { package = "paint-executive", path = "../../../paint/executive", default-features = false }
finality-tracker = { package = "paint-finality-tracker", path = "../../../paint/finality-tracker", default-features = false }
grandpa = { package = "paint-grandpa", path = "../../../paint/grandpa", default-features = false }
im-online = { package = "paint-im-online", path = "../../../paint/im-online", default-features = false }
indices = { package = "paint-indices", path = "../../../paint/indices", default-features = false }
membership = { package = "paint-membership", path = "../../../paint/membership", default-features = false }
nicks = { package = "paint-nicks", path = "../../../paint/nicks", default-features = false }
offences = { package = "paint-offences", path = "../../../paint/offences", default-features = false }
randomness-collective-flip = { package = "paint-randomness-collective-flip", path = "../../../paint/randomness-collective-flip", default-features = false }
session = { package = "paint-session", path = "../../../paint/session", default-features = false, features = ["historical"] }
staking = { package = "paint-staking", path = "../../../paint/staking", default-features = false }
paint-staking-reward-curve = { path = "../../../paint/staking/reward-curve"}
sudo = { package = "paint-sudo", path = "../../../paint/sudo", default-features = false }
support = { package = "paint-support", path = "../../../paint/support", default-features = false }
system = { package = "paint-system", path = "../../../paint/system", default-features = false }
system-rpc-runtime-api = { package = "paint-system-rpc-runtime-api", path = "../../../paint/system/rpc/runtime-api/", default-features = false }
timestamp = { package = "paint-timestamp", path = "../../../paint/timestamp", default-features = false }
treasury = { package = "paint-treasury", path = "../../../paint/treasury", default-features = false }
utility = { package = "paint-utility", path = "../../../paint/utility", default-features = false }
transaction-payment = { package = "paint-transaction-payment", path = "../../../paint/transaction-payment", default-features = false }
transaction-payment-rpc-runtime-api = { package = "paint-transaction-payment-rpc-runtime-api", path = "../../../paint/transaction-payment/rpc/runtime-api/", default-features = false }
[build-dependencies]
wasm-builder-runner = { package = "substrate-wasm-builder-runner", path = "../../../client/utils/wasm-builder-runner", version = "1.0.4" }
[dev-dependencies]
runtime_io = { package = "sr-io", path = "../../../primitives/sr-io" }
[features]
default = ["std"]
std = [
"authority-discovery-primitives/std",
"authority-discovery/std",
"authorship/std",
"babe-primitives/std",
"babe/std",
"balances/std",
"block-builder-api/std",
"codec/std",
"collective/std",
"contracts-rpc-runtime-api/std",
"contracts/std",
"democracy/std",
"elections-phragmen/std",
"executive/std",
"finality-tracker/std",
"grandpa/std",
"im-online/std",
"indices/std",
"inherents/std",
"membership/std",
"nicks/std",
"node-primitives/std",
"offchain-primitives/std",
"offences/std",
"primitives/std",
"randomness-collective-flip/std",
"rstd/std",
"rustc-hex",
"safe-mix/std",
"serde",
"session/std",
"sr-api/std",
"sr-primitives/std",
"sr-staking-primitives/std",
"staking/std",
"substrate-keyring",
"substrate-session/std",
"sudo/std",
"support/std",
"system-rpc-runtime-api/std",
"system/std",
"timestamp/std",
"transaction-payment-rpc-runtime-api/std",
"transaction-payment/std",
"treasury/std",
"tx-pool-api/std",
"utility/std",
"version/std",
]
+30
View File
@@ -0,0 +1,30 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource};
fn main() {
build_current_project_with_rustflags(
"wasm_binary.rs",
WasmBuilderSource::CratesOrPath {
path: "../../../utils/wasm-builder",
version: "1.0.8",
},
// This instructs LLD to export __heap_base as a global variable, which is used by the
// external memory allocator.
"-Clink-arg=--export=__heap_base",
);
}
@@ -0,0 +1,68 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! A set of constant values used in substrate runtime.
/// Money matters.
pub mod currency {
use node_primitives::Balance;
pub const MILLICENTS: Balance = 1_000_000_000;
pub const CENTS: Balance = 1_000 * MILLICENTS; // assume this is worth about a cent.
pub const DOLLARS: Balance = 100 * CENTS;
}
/// Time.
pub mod time {
use node_primitives::{Moment, BlockNumber};
/// Since BABE is probabilistic this is the average expected block time that
/// we are targetting. Blocks will be produced at a minimum duration defined
/// by `SLOT_DURATION`, but some slots will not be allocated to any
/// authority and hence no block will be produced. We expect to have this
/// block time on average following the defined slot duration and the value
/// of `c` configured for BABE (where `1 - c` represents the probability of
/// a slot being empty).
/// This value is only used indirectly to define the unit constants below
/// that are expressed in blocks. The rest of the code should use
/// `SLOT_DURATION` instead (like the timestamp module for calculating the
/// minimum period).
///
/// If using BABE with secondary slots (default) then all of the slots will
/// always be assigned, in which case `MILLISECS_PER_BLOCK` and
/// `SLOT_DURATION` should have the same value.
///
/// <https://research.web3.foundation/en/latest/polkadot/BABE/Babe/#6-practical-results>
pub const MILLISECS_PER_BLOCK: Moment = 3000;
pub const SECS_PER_BLOCK: Moment = MILLISECS_PER_BLOCK / 1000;
pub const SLOT_DURATION: Moment = MILLISECS_PER_BLOCK;
// 1 in 4 blocks (on average, not counting collisions) will be primary BABE blocks.
pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4);
pub const EPOCH_DURATION_IN_BLOCKS: BlockNumber = 10 * MINUTES;
pub const EPOCH_DURATION_IN_SLOTS: u64 = {
const SLOT_FILL_RATE: f64 = MILLISECS_PER_BLOCK as f64 / SLOT_DURATION as f64;
(EPOCH_DURATION_IN_BLOCKS as f64 * SLOT_FILL_RATE) as u64
};
// These time units are defined in number of blocks.
pub const MINUTES: BlockNumber = 60 / (SECS_PER_BLOCK as BlockNumber);
pub const HOURS: BlockNumber = MINUTES * 60;
pub const DAYS: BlockNumber = HOURS * 24;
}
+365
View File
@@ -0,0 +1,365 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Some configurable implementations as associated type for the substrate runtime.
use node_primitives::Balance;
use sr_primitives::weights::Weight;
use sr_primitives::traits::{Convert, Saturating};
use sr_primitives::{Fixed64, Perbill};
use support::traits::{OnUnbalanced, Currency, Get};
use crate::{Balances, System, Authorship, MaximumBlockWeight, NegativeImbalance};
pub struct Author;
impl OnUnbalanced<NegativeImbalance> for Author {
fn on_nonzero_unbalanced(amount: NegativeImbalance) {
Balances::resolve_creating(&Authorship::author(), amount);
}
}
/// Struct that handles the conversion of Balance -> `u64`. This is used for staking's election
/// calculation.
pub struct CurrencyToVoteHandler;
impl CurrencyToVoteHandler {
fn factor() -> Balance { (Balances::total_issuance() / u64::max_value() as Balance).max(1) }
}
impl Convert<Balance, u64> for CurrencyToVoteHandler {
fn convert(x: Balance) -> u64 { (x / Self::factor()) as u64 }
}
impl Convert<u128, Balance> for CurrencyToVoteHandler {
fn convert(x: u128) -> Balance { x * Self::factor() }
}
/// Convert from weight to balance via a simple coefficient multiplication
/// The associated type C encapsulates a constant in units of balance per weight
pub struct LinearWeightToFee<C>(rstd::marker::PhantomData<C>);
impl<C: Get<Balance>> Convert<Weight, Balance> for LinearWeightToFee<C> {
fn convert(w: Weight) -> Balance {
// substrate-node a weight of 10_000 (smallest non-zero weight) to be mapped to 10^7 units of
// fees, hence:
let coefficient = C::get();
Balance::from(w).saturating_mul(coefficient)
}
}
/// Update the given multiplier based on the following formula
///
/// diff = (previous_block_weight - target_weight)
/// v = 0.00004
/// next_weight = weight * (1 + (v . diff) + (v . diff)^2 / 2)
///
/// Where `target_weight` must be given as the `Get` implementation of the `T` generic type.
/// https://research.web3.foundation/en/latest/polkadot/Token%20Economics/#relay-chain-transaction-fees
pub struct TargetedFeeAdjustment<T>(rstd::marker::PhantomData<T>);
impl<T: Get<Perbill>> Convert<Fixed64, Fixed64> for TargetedFeeAdjustment<T> {
fn convert(multiplier: Fixed64) -> Fixed64 {
let block_weight = System::all_extrinsics_weight();
let max_weight = MaximumBlockWeight::get();
let target_weight = (T::get() * max_weight) as u128;
let block_weight = block_weight as u128;
// determines if the first_term is positive
let positive = block_weight >= target_weight;
let diff_abs = block_weight.max(target_weight) - block_weight.min(target_weight);
// diff is within u32, safe.
let diff = Fixed64::from_rational(diff_abs as i64, max_weight as u64);
let diff_squared = diff.saturating_mul(diff);
// 0.00004 = 4/100_000 = 40_000/10^9
let v = Fixed64::from_rational(4, 100_000);
// 0.00004^2 = 16/10^10 ~= 2/10^9. Taking the future /2 into account, then it is just 1
// parts from a billionth.
let v_squared_2 = Fixed64::from_rational(1, 1_000_000_000);
let first_term = v.saturating_mul(diff);
// It is very unlikely that this will exist (in our poor perbill estimate) but we are giving
// it a shot.
let second_term = v_squared_2.saturating_mul(diff_squared);
if positive {
// Note: this is merely bounded by how big the multiplier and the inner value can go,
// not by any economical reasoning.
let excess = first_term.saturating_add(second_term);
multiplier.saturating_add(excess)
} else {
// Proof: first_term > second_term. Safe subtraction.
let negative = first_term - second_term;
multiplier.saturating_sub(negative)
// despite the fact that apply_to saturates weight (final fee cannot go below 0)
// it is crucially important to stop here and don't further reduce the weight fee
// multiplier. While at -1, it means that the network is so un-congested that all
// transactions have no weight fee. We stop here and only increase if the network
// became more busy.
.max(Fixed64::from_rational(-1, 1))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use sr_primitives::weights::Weight;
use sr_primitives::assert_eq_error_rate;
use crate::{MaximumBlockWeight, AvailableBlockRatio, Runtime};
use crate::{constants::currency::*, TransactionPayment, TargetBlockFullness};
fn max() -> Weight {
MaximumBlockWeight::get()
}
fn target() -> Weight {
TargetBlockFullness::get() * max()
}
// poc reference implementation.
fn fee_multiplier_update(block_weight: Weight, previous: Fixed64) -> Fixed64 {
let block_weight = block_weight as f32;
let v: f32 = 0.00004;
// maximum tx weight
let m = max() as f32;
// Ideal saturation in terms of weight
let ss = target() as f32;
// Current saturation in terms of weight
let s = block_weight;
let fm = v * (s/m - ss/m) + v.powi(2) * (s/m - ss/m).powi(2) / 2.0;
let addition_fm = Fixed64::from_parts((fm * 1_000_000_000_f32).round() as i64);
previous.saturating_add(addition_fm)
}
fn feemul(parts: i64) -> Fixed64 {
Fixed64::from_parts(parts)
}
fn run_with_system_weight<F>(w: Weight, assertions: F) where F: Fn() -> () {
let mut t: runtime_io::TestExternalities =
system::GenesisConfig::default().build_storage::<Runtime>().unwrap().into();
t.execute_with(|| {
System::set_block_limits(w, 0);
assertions()
});
}
#[test]
fn fee_multiplier_update_poc_works() {
let fm = Fixed64::from_rational(0, 1);
let test_set = vec![
(0, fm.clone()),
(100, fm.clone()),
(target(), fm.clone()),
(max() / 2, fm.clone()),
(max(), fm.clone()),
];
test_set.into_iter().for_each(|(w, fm)| {
run_with_system_weight(w, || {
assert_eq_error_rate!(
fee_multiplier_update(w, fm).into_inner(),
TargetedFeeAdjustment::<TargetBlockFullness>::convert(fm).into_inner(),
5,
);
})
})
}
#[test]
fn empty_chain_simulation() {
// just a few txs per_block.
let block_weight = 0;
run_with_system_weight(block_weight, || {
let mut fm = Fixed64::default();
let mut iterations: u64 = 0;
loop {
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(fm);
fm = next;
if fm == Fixed64::from_rational(-1, 1) { break; }
iterations += 1;
}
println!("iteration {}, new fm = {:?}. Weight fee is now zero", iterations, fm);
assert!(iterations > 50_000, "This assertion is just a warning; Don't panic. \
Current substrate/polkadot node are configured with a _slow adjusting fee_ \
mechanism. Hence, it is really unlikely that fees collapse to zero even on an \
empty chain in less than at least of couple of thousands of empty blocks. But this \
simulation indicates that fees collapsed to zero after {} almost-empty blocks. \
Check it",
iterations,
);
})
}
#[test]
#[ignore]
fn congested_chain_simulation() {
// `cargo test congested_chain_simulation -- --nocapture` to get some insight.
// almost full. The entire quota of normal transactions is taken.
let block_weight = AvailableBlockRatio::get() * max() - 100;
// Default substrate minimum.
let tx_weight = 10_000;
run_with_system_weight(block_weight, || {
// initial value configured on module
let mut fm = Fixed64::default();
assert_eq!(fm, TransactionPayment::next_fee_multiplier());
let mut iterations: u64 = 0;
loop {
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(fm);
// if no change, panic. This should never happen in this case.
if fm == next { panic!("The fee should ever increase"); }
fm = next;
iterations += 1;
let fee = <Runtime as transaction_payment::Trait>::WeightToFee::convert(tx_weight);
let adjusted_fee = fm.saturated_multiply_accumulate(fee);
println!(
"iteration {}, new fm = {:?}. Fee at this point is: {} units / {} millicents, \
{} cents, {} dollars",
iterations,
fm,
adjusted_fee,
adjusted_fee / MILLICENTS,
adjusted_fee / CENTS,
adjusted_fee / DOLLARS,
);
}
});
}
#[test]
fn stateless_weight_mul() {
run_with_system_weight(target() / 4, || {
// Light block. Fee is reduced a little.
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed64::default()),
feemul(-7500),
);
});
run_with_system_weight(target() / 2, || {
// a bit more. Fee is decreased less, meaning that the fee increases as the block grows.
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed64::default()),
feemul(-5000),
);
});
run_with_system_weight(target(), || {
// ideal. Original fee. No changes.
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed64::default()),
feemul(0),
);
});
run_with_system_weight(target() * 2, || {
// // More than ideal. Fee is increased.
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed64::default()),
feemul(10000),
);
});
}
#[test]
fn stateful_weight_mul_grow_to_infinity() {
run_with_system_weight(target() * 2, || {
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed64::default()),
feemul(10000)
);
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(feemul(10000)),
feemul(20000)
);
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(feemul(20000)),
feemul(30000)
);
// ...
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(feemul(1_000_000_000)),
feemul(1_000_000_000 + 10000)
);
});
}
#[test]
fn stateful_weight_mil_collapse_to_minus_one() {
run_with_system_weight(0, || {
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed64::default()),
feemul(-10000)
);
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(feemul(-10000)),
feemul(-20000)
);
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(feemul(-20000)),
feemul(-30000)
);
// ...
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(feemul(1_000_000_000 * -1)),
feemul(-1_000_000_000)
);
})
}
#[test]
fn weight_to_fee_should_not_overflow_on_large_weights() {
let kb = 1024 as Weight;
let mb = kb * kb;
let max_fm = Fixed64::from_natural(i64::max_value());
// check that for all values it can compute, correctly.
vec![
0,
1,
10,
1000,
kb,
10 * kb,
100 * kb,
mb,
10 * mb,
Weight::max_value() / 2,
Weight::max_value()
].into_iter().for_each(|i| {
run_with_system_weight(i, || {
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed64::default());
let truth = fee_multiplier_update(i, Fixed64::default());
assert_eq_error_rate!(truth.into_inner(), next.into_inner(), 5);
});
});
// Some values that are all above the target and will cause an increase.
let t = target();
vec![t + 100, t * 2, t * 4]
.into_iter()
.for_each(|i| {
run_with_system_weight(i, || {
let fm = TargetedFeeAdjustment::<TargetBlockFullness>::convert(max_fm);
// won't grow. The convert saturates everything.
assert_eq!(fm, max_fm);
})
});
}
}
+764
View File
@@ -0,0 +1,764 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! The Substrate runtime. This can be compiled with ``#[no_std]`, ready for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
#![recursion_limit="256"]
use rstd::prelude::*;
use support::{
construct_runtime, parameter_types, traits::{SplitTwoWays, Currency, Randomness}
};
use primitives::u32_trait::{_1, _2, _3, _4};
use node_primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Moment, Signature};
use sr_api::impl_runtime_apis;
use sr_primitives::{Permill, Perbill, ApplyResult, impl_opaque_keys, generic, create_runtime_str};
use sr_primitives::curve::PiecewiseLinear;
use sr_primitives::transaction_validity::TransactionValidity;
use sr_primitives::weights::Weight;
use sr_primitives::traits::{
self, BlakeTwo256, Block as BlockT, NumberFor, StaticLookup, SaturatedConversion,
OpaqueKeys,
};
use version::RuntimeVersion;
#[cfg(any(feature = "std", test))]
use version::NativeVersion;
use primitives::OpaqueMetadata;
use grandpa::AuthorityList as GrandpaAuthorityList;
use grandpa::fg_primitives;
use im_online::sr25519::{AuthorityId as ImOnlineId};
use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId;
use transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
use contracts_rpc_runtime_api::ContractExecResult;
use system::offchain::TransactionSubmitter;
use inherents::{InherentData, CheckInherentsResult};
#[cfg(any(feature = "std", test))]
pub use sr_primitives::BuildStorage;
pub use timestamp::Call as TimestampCall;
pub use balances::Call as BalancesCall;
pub use contracts::Gas;
pub use support::StorageValue;
pub use staking::StakerStatus;
/// Implementations of some helper traits passed into runtime modules as associated types.
pub mod impls;
use impls::{CurrencyToVoteHandler, Author, LinearWeightToFee, TargetedFeeAdjustment};
/// Constant values used within the runtime.
pub mod constants;
use constants::{time::*, currency::*};
// Make the WASM binary available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
/// Runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node"),
impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10,
// Per convention: if the runtime behavior changes, increment spec_version
// and set impl_version to equal spec_version. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 194,
impl_version: 194,
apis: RUNTIME_API_VERSIONS,
};
/// Native version.
#[cfg(any(feature = "std", test))]
pub fn native_version() -> NativeVersion {
NativeVersion {
runtime_version: VERSION,
can_author_with: Default::default(),
}
}
type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;
pub type DealWithFees = SplitTwoWays<
Balance,
NegativeImbalance,
_4, Treasury, // 4 parts (80%) goes to the treasury.
_1, Author, // 1 part (20%) goes to the block author.
>;
parameter_types! {
pub const BlockHashCount: BlockNumber = 250;
pub const MaximumBlockWeight: Weight = 1_000_000_000;
pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;
pub const Version: RuntimeVersion = VERSION;
pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);
}
impl system::Trait for Runtime {
type Origin = Origin;
type Call = Call;
type Index = Index;
type BlockNumber = BlockNumber;
type Hash = Hash;
type Hashing = BlakeTwo256;
type AccountId = AccountId;
type Lookup = Indices;
type Header = generic::Header<BlockNumber, BlakeTwo256>;
type Event = Event;
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
type AvailableBlockRatio = AvailableBlockRatio;
type Version = Version;
}
impl utility::Trait for Runtime {
type Event = Event;
type Call = Call;
}
parameter_types! {
pub const EpochDuration: u64 = EPOCH_DURATION_IN_SLOTS;
pub const ExpectedBlockTime: Moment = MILLISECS_PER_BLOCK;
}
impl babe::Trait for Runtime {
type EpochDuration = EpochDuration;
type ExpectedBlockTime = ExpectedBlockTime;
type EpochChangeTrigger = babe::ExternalTrigger;
}
impl indices::Trait for Runtime {
type AccountIndex = AccountIndex;
type IsDeadAccount = Balances;
type ResolveHint = indices::SimpleResolveHint<Self::AccountId, Self::AccountIndex>;
type Event = Event;
}
parameter_types! {
pub const ExistentialDeposit: Balance = 1 * DOLLARS;
pub const TransferFee: Balance = 1 * CENTS;
pub const CreationFee: Balance = 1 * CENTS;
}
impl balances::Trait for Runtime {
type Balance = Balance;
type OnFreeBalanceZero = ((Staking, Contracts), Session);
type OnNewAccount = Indices;
type Event = Event;
type DustRemoval = ();
type TransferPayment = ();
type ExistentialDeposit = ExistentialDeposit;
type TransferFee = TransferFee;
type CreationFee = CreationFee;
}
parameter_types! {
pub const TransactionBaseFee: Balance = 1 * CENTS;
pub const TransactionByteFee: Balance = 10 * MILLICENTS;
// setting this to zero will disable the weight fee.
pub const WeightFeeCoefficient: Balance = 1_000;
// for a sane configuration, this should always be less than `AvailableBlockRatio`.
pub const TargetBlockFullness: Perbill = Perbill::from_percent(25);
}
impl transaction_payment::Trait for Runtime {
type Currency = Balances;
type OnTransactionPayment = DealWithFees;
type TransactionBaseFee = TransactionBaseFee;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = LinearWeightToFee<WeightFeeCoefficient>;
type FeeMultiplierUpdate = TargetedFeeAdjustment<TargetBlockFullness>;
}
parameter_types! {
pub const MinimumPeriod: Moment = SLOT_DURATION / 2;
}
impl timestamp::Trait for Runtime {
type Moment = Moment;
type OnTimestampSet = Babe;
type MinimumPeriod = MinimumPeriod;
}
parameter_types! {
pub const UncleGenerations: BlockNumber = 5;
}
impl authorship::Trait for Runtime {
type FindAuthor = session::FindAccountFromAuthorIndex<Self, Babe>;
type UncleGenerations = UncleGenerations;
type FilterUncle = ();
type EventHandler = (Staking, ImOnline);
}
impl_opaque_keys! {
pub struct SessionKeys {
pub grandpa: Grandpa,
pub babe: Babe,
pub im_online: ImOnline,
pub authority_discovery: AuthorityDiscovery,
}
}
parameter_types! {
pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(17);
}
impl session::Trait for Runtime {
type OnSessionEnding = Staking;
type SessionHandler = <SessionKeys as OpaqueKeys>::KeyTypeIdProviders;
type ShouldEndSession = Babe;
type Event = Event;
type Keys = SessionKeys;
type ValidatorId = <Self as system::Trait>::AccountId;
type ValidatorIdOf = staking::StashOf<Self>;
type SelectInitialValidators = Staking;
type DisabledValidatorsThreshold = DisabledValidatorsThreshold;
}
impl session::historical::Trait for Runtime {
type FullIdentification = staking::Exposure<AccountId, Balance>;
type FullIdentificationOf = staking::ExposureOf<Runtime>;
}
paint_staking_reward_curve::build! {
const REWARD_CURVE: PiecewiseLinear<'static> = curve!(
min_inflation: 0_025_000,
max_inflation: 0_100_000,
ideal_stake: 0_500_000,
falloff: 0_050_000,
max_piece_count: 40,
test_precision: 0_005_000,
);
}
parameter_types! {
pub const SessionsPerEra: sr_staking_primitives::SessionIndex = 6;
pub const BondingDuration: staking::EraIndex = 24 * 28;
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
}
impl staking::Trait for Runtime {
type Currency = Balances;
type Time = Timestamp;
type CurrencyToVote = CurrencyToVoteHandler;
type RewardRemainder = Treasury;
type Event = Event;
type Slash = Treasury; // send the slashed funds to the treasury.
type Reward = (); // rewards are minted from the void
type SessionsPerEra = SessionsPerEra;
type BondingDuration = BondingDuration;
type SessionInterface = Self;
type RewardCurve = RewardCurve;
}
parameter_types! {
pub const LaunchPeriod: BlockNumber = 28 * 24 * 60 * MINUTES;
pub const VotingPeriod: BlockNumber = 28 * 24 * 60 * MINUTES;
pub const EmergencyVotingPeriod: BlockNumber = 3 * 24 * 60 * MINUTES;
pub const MinimumDeposit: Balance = 100 * DOLLARS;
pub const EnactmentPeriod: BlockNumber = 30 * 24 * 60 * MINUTES;
pub const CooloffPeriod: BlockNumber = 28 * 24 * 60 * MINUTES;
}
impl democracy::Trait for Runtime {
type Proposal = Call;
type Event = Event;
type Currency = Balances;
type EnactmentPeriod = EnactmentPeriod;
type LaunchPeriod = LaunchPeriod;
type VotingPeriod = VotingPeriod;
type EmergencyVotingPeriod = EmergencyVotingPeriod;
type MinimumDeposit = MinimumDeposit;
/// A straight majority of the council can decide what their next motion is.
type ExternalOrigin = collective::EnsureProportionAtLeast<_1, _2, AccountId, CouncilCollective>;
/// A super-majority can have the next scheduled referendum be a straight majority-carries vote.
type ExternalMajorityOrigin = collective::EnsureProportionAtLeast<_3, _4, AccountId, CouncilCollective>;
/// A unanimous council can have the next scheduled referendum be a straight default-carries
/// (NTB) vote.
type ExternalDefaultOrigin = collective::EnsureProportionAtLeast<_1, _1, AccountId, CouncilCollective>;
/// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote
/// be tabled immediately and with a shorter voting/enactment period.
type FastTrackOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, TechnicalCollective>;
// To cancel a proposal which has been passed, 2/3 of the council must agree to it.
type CancellationOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilCollective>;
// Any single technical committee member may veto a coming council proposal, however they can
// only do it once and it lasts only for the cooloff period.
type VetoOrigin = collective::EnsureMember<AccountId, TechnicalCollective>;
type CooloffPeriod = CooloffPeriod;
}
type CouncilCollective = collective::Instance1;
impl collective::Trait<CouncilCollective> for Runtime {
type Origin = Origin;
type Proposal = Call;
type Event = Event;
}
parameter_types! {
pub const CandidacyBond: Balance = 10 * DOLLARS;
pub const VotingBond: Balance = 1 * DOLLARS;
pub const TermDuration: BlockNumber = 7 * DAYS;
pub const DesiredMembers: u32 = 13;
pub const DesiredRunnersUp: u32 = 7;
}
impl elections_phragmen::Trait for Runtime {
type Event = Event;
type Currency = Balances;
type CurrencyToVote = CurrencyToVoteHandler;
type CandidacyBond = CandidacyBond;
type VotingBond = VotingBond;
type TermDuration = TermDuration;
type DesiredMembers = DesiredMembers;
type DesiredRunnersUp = DesiredRunnersUp;
type LoserCandidate = ();
type BadReport = ();
type KickedMember = ();
type ChangeMembers = Council;
}
type TechnicalCollective = collective::Instance2;
impl collective::Trait<TechnicalCollective> for Runtime {
type Origin = Origin;
type Proposal = Call;
type Event = Event;
}
impl membership::Trait<membership::Instance1> for Runtime {
type Event = Event;
type AddOrigin = collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>;
type RemoveOrigin = collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>;
type SwapOrigin = collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>;
type ResetOrigin = collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>;
type MembershipInitialized = TechnicalCommittee;
type MembershipChanged = TechnicalCommittee;
}
parameter_types! {
pub const ProposalBond: Permill = Permill::from_percent(5);
pub const ProposalBondMinimum: Balance = 1 * DOLLARS;
pub const SpendPeriod: BlockNumber = 1 * DAYS;
pub const Burn: Permill = Permill::from_percent(50);
}
impl treasury::Trait for Runtime {
type Currency = Balances;
type ApproveOrigin = collective::EnsureMembers<_4, AccountId, CouncilCollective>;
type RejectOrigin = collective::EnsureMembers<_2, AccountId, CouncilCollective>;
type Event = Event;
type ProposalRejection = ();
type ProposalBond = ProposalBond;
type ProposalBondMinimum = ProposalBondMinimum;
type SpendPeriod = SpendPeriod;
type Burn = Burn;
}
parameter_types! {
pub const ContractTransferFee: Balance = 1 * CENTS;
pub const ContractCreationFee: Balance = 1 * CENTS;
pub const ContractTransactionBaseFee: Balance = 1 * CENTS;
pub const ContractTransactionByteFee: Balance = 10 * MILLICENTS;
pub const ContractFee: Balance = 1 * CENTS;
pub const TombstoneDeposit: Balance = 1 * DOLLARS;
pub const RentByteFee: Balance = 1 * DOLLARS;
pub const RentDepositOffset: Balance = 1000 * DOLLARS;
pub const SurchargeReward: Balance = 150 * DOLLARS;
}
impl contracts::Trait for Runtime {
type Currency = Balances;
type Time = Timestamp;
type Randomness = RandomnessCollectiveFlip;
type Call = Call;
type Event = Event;
type DetermineContractAddress = contracts::SimpleAddressDeterminator<Runtime>;
type ComputeDispatchFee = contracts::DefaultDispatchFeeComputor<Runtime>;
type TrieIdGenerator = contracts::TrieIdFromParentCounter<Runtime>;
type GasPayment = ();
type RentPayment = ();
type SignedClaimHandicap = contracts::DefaultSignedClaimHandicap;
type TombstoneDeposit = TombstoneDeposit;
type StorageSizeOffset = contracts::DefaultStorageSizeOffset;
type RentByteFee = RentByteFee;
type RentDepositOffset = RentDepositOffset;
type SurchargeReward = SurchargeReward;
type TransferFee = ContractTransferFee;
type CreationFee = ContractCreationFee;
type TransactionBaseFee = ContractTransactionBaseFee;
type TransactionByteFee = ContractTransactionByteFee;
type ContractFee = ContractFee;
type CallBaseFee = contracts::DefaultCallBaseFee;
type InstantiateBaseFee = contracts::DefaultInstantiateBaseFee;
type MaxDepth = contracts::DefaultMaxDepth;
type MaxValueSize = contracts::DefaultMaxValueSize;
type BlockGasLimit = contracts::DefaultBlockGasLimit;
}
impl sudo::Trait for Runtime {
type Event = Event;
type Proposal = Call;
}
type SubmitTransaction = TransactionSubmitter<ImOnlineId, Runtime, UncheckedExtrinsic>;
parameter_types! {
pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _;
}
impl im_online::Trait for Runtime {
type AuthorityId = ImOnlineId;
type Call = Call;
type Event = Event;
type SubmitTransaction = SubmitTransaction;
type ReportUnresponsiveness = Offences;
type SessionDuration = SessionDuration;
}
impl offences::Trait for Runtime {
type Event = Event;
type IdentificationTuple = session::historical::IdentificationTuple<Self>;
type OnOffenceHandler = Staking;
}
impl authority_discovery::Trait for Runtime {}
impl grandpa::Trait for Runtime {
type Event = Event;
}
parameter_types! {
pub const WindowSize: BlockNumber = 101;
pub const ReportLatency: BlockNumber = 1000;
}
impl finality_tracker::Trait for Runtime {
type OnFinalizationStalled = Grandpa;
type WindowSize = WindowSize;
type ReportLatency = ReportLatency;
}
parameter_types! {
pub const ReservationFee: Balance = 1 * DOLLARS;
pub const MinLength: usize = 3;
pub const MaxLength: usize = 16;
}
impl nicks::Trait for Runtime {
type Event = Event;
type Currency = Balances;
type ReservationFee = ReservationFee;
type Slashed = Treasury;
type ForceOrigin = collective::EnsureMember<AccountId, CouncilCollective>;
type MinLength = MinLength;
type MaxLength = MaxLength;
}
impl system::offchain::CreateTransaction<Runtime, UncheckedExtrinsic> for Runtime {
type Public = <Signature as traits::Verify>::Signer;
type Signature = Signature;
fn create_transaction<F: system::offchain::Signer<Self::Public, Self::Signature>>(
call: Call,
public: Self::Public,
account: AccountId,
index: Index,
) -> Option<(Call, <UncheckedExtrinsic as traits::Extrinsic>::SignaturePayload)> {
let period = 1 << 8;
let current_block = System::block_number().saturated_into::<u64>();
let tip = 0;
let extra: SignedExtra = (
system::CheckVersion::<Runtime>::new(),
system::CheckGenesis::<Runtime>::new(),
system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)),
system::CheckNonce::<Runtime>::from(index),
system::CheckWeight::<Runtime>::new(),
transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),
Default::default(),
);
let raw_payload = SignedPayload::new(call, extra).ok()?;
let signature = F::sign(public, &raw_payload)?;
let address = Indices::unlookup(account);
let (call, extra, _) = raw_payload.deconstruct();
Some((call, (address, signature, extra)))
}
}
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = node_primitives::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: system::{Module, Call, Storage, Config, Event},
Utility: utility::{Module, Call, Event},
Babe: babe::{Module, Call, Storage, Config, Inherent(Timestamp)},
Timestamp: timestamp::{Module, Call, Storage, Inherent},
Authorship: authorship::{Module, Call, Storage, Inherent},
Indices: indices,
Balances: balances::{default, Error},
TransactionPayment: transaction_payment::{Module, Storage},
Staking: staking::{default, OfflineWorker},
Session: session::{Module, Call, Storage, Event, Config<T>},
Democracy: democracy::{Module, Call, Storage, Config, Event<T>},
Council: collective::<Instance1>::{Module, Call, Storage, Origin<T>, Event<T>, Config<T>},
TechnicalCommittee: collective::<Instance2>::{Module, Call, Storage, Origin<T>, Event<T>, Config<T>},
Elections: elections_phragmen::{Module, Call, Storage, Event<T>},
TechnicalMembership: membership::<Instance1>::{Module, Call, Storage, Event<T>, Config<T>},
FinalityTracker: finality_tracker::{Module, Call, Inherent},
Grandpa: grandpa::{Module, Call, Storage, Config, Event},
Treasury: treasury::{Module, Call, Storage, Config, Event<T>},
Contracts: contracts,
Sudo: sudo,
ImOnline: im_online::{Module, Call, Storage, Event<T>, ValidateUnsigned, Config<T>},
AuthorityDiscovery: authority_discovery::{Module, Call, Config},
Offences: offences::{Module, Call, Storage, Event},
RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage},
Nicks: nicks::{Module, Call, Storage, Event<T>},
}
);
/// The address format for describing accounts.
pub type Address = <Indices as StaticLookup>::Source;
/// Block header type as expected by this runtime.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Block type as expected by this runtime.
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// A Block signed with a Justification
pub type SignedBlock = generic::SignedBlock<Block>;
/// BlockId type as expected by this runtime.
pub type BlockId = generic::BlockId<Block>;
/// The SignedExtension to the basic transaction logic.
pub type SignedExtra = (
system::CheckVersion<Runtime>,
system::CheckGenesis<Runtime>,
system::CheckEra<Runtime>,
system::CheckNonce<Runtime>,
system::CheckWeight<Runtime>,
transaction_payment::ChargeTransactionPayment<Runtime>,
contracts::CheckBlockGasLimit<Runtime>,
);
/// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
/// The payload being signed in transactions.
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
/// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Call, SignedExtra>;
/// Executive: handles dispatch to the various modules.
pub type Executive = executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Runtime, AllModules>;
impl_runtime_apis! {
impl sr_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion {
VERSION
}
fn execute_block(block: Block) {
Executive::execute_block(block)
}
fn initialize_block(header: &<Block as BlockT>::Header) {
Executive::initialize_block(header)
}
}
impl sr_api::Metadata<Block> for Runtime {
fn metadata() -> OpaqueMetadata {
Runtime::metadata().into()
}
}
impl block_builder_api::BlockBuilder<Block> for Runtime {
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyResult {
Executive::apply_extrinsic(extrinsic)
}
fn finalize_block() -> <Block as BlockT>::Header {
Executive::finalize_block()
}
fn inherent_extrinsics(data: InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
data.create_extrinsics()
}
fn check_inherents(block: Block, data: InherentData) -> CheckInherentsResult {
data.check_extrinsics(&block)
}
fn random_seed() -> <Block as BlockT>::Hash {
RandomnessCollectiveFlip::random_seed()
}
}
impl tx_pool_api::TaggedTransactionQueue<Block> for Runtime {
fn validate_transaction(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
Executive::validate_transaction(tx)
}
}
impl offchain_primitives::OffchainWorkerApi<Block> for Runtime {
fn offchain_worker(number: NumberFor<Block>) {
Executive::offchain_worker(number)
}
}
impl fg_primitives::GrandpaApi<Block> for Runtime {
fn grandpa_authorities() -> GrandpaAuthorityList {
Grandpa::grandpa_authorities()
}
}
impl babe_primitives::BabeApi<Block> for Runtime {
fn configuration() -> babe_primitives::BabeConfiguration {
// The choice of `c` parameter (where `1 - c` represents the
// probability of a slot being empty), is done in accordance to the
// slot duration and expected target block time, for safely
// resisting network delays of maximum two seconds.
// <https://research.web3.foundation/en/latest/polkadot/BABE/Babe/#6-practical-results>
babe_primitives::BabeConfiguration {
slot_duration: Babe::slot_duration(),
epoch_length: EpochDuration::get(),
c: PRIMARY_PROBABILITY,
genesis_authorities: Babe::authorities(),
randomness: Babe::randomness(),
secondary_slots: true,
}
}
}
impl authority_discovery_primitives::AuthorityDiscoveryApi<Block> for Runtime {
fn authorities() -> Vec<AuthorityDiscoveryId> {
AuthorityDiscovery::authorities()
}
}
impl system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {
fn account_nonce(account: AccountId) -> Index {
System::account_nonce(account)
}
}
impl contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance> for Runtime {
fn call(
origin: AccountId,
dest: AccountId,
value: Balance,
gas_limit: u64,
input_data: Vec<u8>,
) -> ContractExecResult {
let exec_result = Contracts::bare_call(
origin,
dest.into(),
value,
gas_limit,
input_data,
);
match exec_result {
Ok(v) => ContractExecResult::Success {
status: v.status,
data: v.data,
},
Err(_) => ContractExecResult::Error,
}
}
fn get_storage(
address: AccountId,
key: [u8; 32],
) -> contracts_rpc_runtime_api::GetStorageResult {
Contracts::get_storage(address, key).map_err(|rpc_err| {
use contracts::GetStorageError;
use contracts_rpc_runtime_api::{GetStorageError as RpcGetStorageError};
/// Map the contract error into the RPC layer error.
match rpc_err {
GetStorageError::ContractDoesntExist => RpcGetStorageError::ContractDoesntExist,
GetStorageError::IsTombstone => RpcGetStorageError::IsTombstone,
}
})
}
}
impl transaction_payment_rpc_runtime_api::TransactionPaymentApi<
Block,
Balance,
UncheckedExtrinsic,
> for Runtime {
fn query_info(uxt: UncheckedExtrinsic, len: u32) -> RuntimeDispatchInfo<Balance> {
TransactionPayment::query_info(uxt, len)
}
}
impl substrate_session::SessionKeys<Block> for Runtime {
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
SessionKeys::generate(seed)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use system::offchain::SubmitSignedTransaction;
fn is_submit_signed_transaction<T>(_arg: T) where
T: SubmitSignedTransaction<
Runtime,
Call,
Extrinsic=UncheckedExtrinsic,
CreateTransaction=Runtime,
Signer=ImOnlineId,
>,
{}
#[test]
fn validate_bounds() {
let x = SubmitTransaction::default();
is_submit_signed_transaction(x);
}
#[test]
fn block_hooks_weight_should_not_exceed_limits() {
use sr_primitives::weights::WeighBlock;
let check_for_block = |b| {
let block_hooks_weight =
<AllModules as WeighBlock<BlockNumber>>::on_initialize(b) +
<AllModules as WeighBlock<BlockNumber>>::on_finalize(b);
assert_eq!(
block_hooks_weight,
0,
"This test might fail simply because the value being compared to has increased to a \
module declaring a new weight for a hook or call. In this case update the test and \
happily move on.",
);
// Invariant. Always must be like this to have a sane chain.
assert!(block_hooks_weight < MaximumBlockWeight::get());
// Warning.
if block_hooks_weight > MaximumBlockWeight::get() / 2 {
println!(
"block hooks weight is consuming more than a block's capacity. You probably want \
to re-think this. This test will fail now."
);
assert!(false);
}
};
let _ = (0..100_000).for_each(check_for_block);
}
}
+31
View File
@@ -0,0 +1,31 @@
[package]
name = "node-testing"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Test utilities for Substrate node."
edition = "2018"
[dependencies]
balances = { package = "paint-balances", path = "../../../paint/balances" }
client = { package = "substrate-client", path = "../../../client/" }
codec = { package = "parity-scale-codec", version = "1.0.0" }
contracts = { package = "paint-contracts", path = "../../../paint/contracts" }
grandpa = { package = "paint-grandpa", path = "../../../paint/grandpa" }
indices = { package = "paint-indices", path = "../../../paint/indices" }
keyring = { package = "substrate-keyring", path = "../../../primitives/keyring" }
node-executor = { path = "../executor" }
node-primitives = { path = "../primitives" }
node-runtime = { path = "../runtime" }
primitives = { package = "substrate-primitives", path = "../../../primitives/core" }
runtime-io = { package = "sr-io", path = "../../../primitives/sr-io" }
runtime_support = { package = "paint-support", path = "../../../paint/support" }
session = { package = "paint-session", path = "../../../paint/session" }
sr-primitives = { path = "../../../primitives/sr-primitives" }
staking = { package = "paint-staking", path = "../../../paint/staking" }
substrate-executor = { path = "../../../client/executor" }
system = { package = "paint-system", path = "../../../paint/system" }
test-client = { package = "substrate-test-client", path = "../../../test/utils/client" }
timestamp = { package = "paint-timestamp", path = "../../../paint/timestamp" }
transaction-payment = { package = "paint-transaction-payment", path = "../../../paint/transaction-payment" }
treasury = { package = "paint-treasury", path = "../../../paint/treasury" }
wabt = "0.9.2"
+73
View File
@@ -0,0 +1,73 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Utilites to build a `TestClient` for `node-runtime`.
use sr_primitives::BuildStorage;
/// Re-export test-client utilities.
pub use test_client::*;
/// Call executor for `node-runtime` `TestClient`.
pub type Executor = substrate_executor::NativeExecutor<node_executor::Executor>;
/// Default backend type.
pub type Backend = client_db::Backend<node_primitives::Block>;
/// Test client type.
pub type Client = client::Client<
Backend,
client::LocalCallExecutor<Backend, Executor>,
node_primitives::Block,
node_runtime::RuntimeApi,
>;
/// Genesis configuration parameters for `TestClient`.
#[derive(Default)]
pub struct GenesisParameters {
support_changes_trie: bool,
}
impl test_client::GenesisInit for GenesisParameters {
fn genesis_storage(&self) -> (StorageOverlay, ChildrenStorageOverlay) {
crate::genesis::config(self.support_changes_trie, None).build_storage().unwrap()
}
}
/// A `test-runtime` extensions to `TestClientBuilder`.
pub trait TestClientBuilderExt: Sized {
/// Create test client builder.
fn new() -> Self;
/// Build the test client.
fn build(self) -> Client;
}
impl TestClientBuilderExt for test_client::TestClientBuilder<
client::LocalCallExecutor<Backend, Executor>,
Backend,
GenesisParameters,
> {
fn new() -> Self{
Self::default()
}
fn build(self) -> Client {
self.build_with_native_executor(None).0
}
}
+100
View File
@@ -0,0 +1,100 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Genesis Configuration.
use crate::keyring::*;
use keyring::{Ed25519Keyring, Sr25519Keyring};
use node_runtime::{
GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, SystemConfig,
GrandpaConfig, IndicesConfig, ContractsConfig, WASM_BINARY,
};
use node_runtime::constants::currency::*;
use primitives::ChangesTrieConfiguration;
use sr_primitives::Perbill;
/// Create genesis runtime configuration for tests.
pub fn config(support_changes_trie: bool, code: Option<&[u8]>) -> GenesisConfig {
GenesisConfig {
system: Some(SystemConfig {
changes_trie_config: if support_changes_trie { Some(ChangesTrieConfiguration {
digest_interval: 2,
digest_levels: 2,
}) } else { None },
code: code.map(|x| x.to_vec()).unwrap_or_else(|| WASM_BINARY.to_vec()),
}),
indices: Some(IndicesConfig {
ids: vec![alice(), bob(), charlie(), dave(), eve(), ferdie()],
}),
balances: Some(BalancesConfig {
balances: vec![
(alice(), 111 * DOLLARS),
(bob(), 100 * DOLLARS),
(charlie(), 100_000_000 * DOLLARS),
(dave(), 111 * DOLLARS),
(eve(), 101 * DOLLARS),
(ferdie(), 100 * DOLLARS),
],
vesting: vec![],
}),
session: Some(SessionConfig {
keys: vec![
(alice(), to_session_keys(
&Ed25519Keyring::Alice,
&Sr25519Keyring::Alice,
)),
(bob(), to_session_keys(
&Ed25519Keyring::Bob,
&Sr25519Keyring::Bob,
)),
(charlie(), to_session_keys(
&Ed25519Keyring::Charlie,
&Sr25519Keyring::Charlie,
)),
]
}),
staking: Some(StakingConfig {
current_era: 0,
stakers: vec![
(dave(), alice(), 111 * DOLLARS, staking::StakerStatus::Validator),
(eve(), bob(), 100 * DOLLARS, staking::StakerStatus::Validator),
(ferdie(), charlie(), 100 * DOLLARS, staking::StakerStatus::Validator)
],
validator_count: 3,
minimum_validator_count: 0,
slash_reward_fraction: Perbill::from_percent(10),
invulnerables: vec![alice(), bob(), charlie()],
.. Default::default()
}),
contracts: Some(ContractsConfig {
current_schedule: Default::default(),
gas_price: 1 * MILLICENTS,
}),
babe: Some(Default::default()),
grandpa: Some(GrandpaConfig {
authorities: vec![],
}),
im_online: Some(Default::default()),
authority_discovery: Some(Default::default()),
democracy: Some(Default::default()),
collective_Instance1: Some(Default::default()),
collective_Instance2: Some(Default::default()),
membership_Instance1: Some(Default::default()),
sudo: Some(Default::default()),
treasury: Some(Default::default()),
}
}
+102
View File
@@ -0,0 +1,102 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Test accounts.
use keyring::{AccountKeyring, Sr25519Keyring, Ed25519Keyring};
use node_primitives::{AccountId, Balance, Index};
use node_runtime::{CheckedExtrinsic, UncheckedExtrinsic, SessionKeys, SignedExtra};
use sr_primitives::generic::Era;
use codec::Encode;
/// Alice's account id.
pub fn alice() -> AccountId { AccountKeyring::Alice.into() }
/// Bob's account id.
pub fn bob() -> AccountId {
AccountKeyring::Bob.into()
}
/// Charlie's account id.
pub fn charlie() -> AccountId {
AccountKeyring::Charlie.into()
}
/// Dave's account id.
pub fn dave() -> AccountId {
AccountKeyring::Dave.into()
}
/// Eve's account id.
pub fn eve() -> AccountId {
AccountKeyring::Eve.into()
}
/// Ferdie's account id.
pub fn ferdie() -> AccountId {
AccountKeyring::Ferdie.into()
}
/// Convert keyrings into `SessionKeys`.
pub fn to_session_keys(
ed25519_keyring: &Ed25519Keyring,
sr25519_keyring: &Sr25519Keyring,
) -> SessionKeys {
SessionKeys {
grandpa: ed25519_keyring.to_owned().public().into(),
babe: sr25519_keyring.to_owned().public().into(),
im_online: sr25519_keyring.to_owned().public().into(),
authority_discovery: sr25519_keyring.to_owned().public().into(),
}
}
/// Returns transaction extra.
pub fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra {
(
system::CheckVersion::new(),
system::CheckGenesis::new(),
system::CheckEra::from(Era::mortal(256, 0)),
system::CheckNonce::from(nonce),
system::CheckWeight::new(),
transaction_payment::ChargeTransactionPayment::from(extra_fee),
Default::default(),
)
}
/// Sign given `CheckedExtrinsic`.
pub fn sign(xt: CheckedExtrinsic, version: u32, genesis_hash: [u8; 32]) -> UncheckedExtrinsic {
match xt.signed {
Some((signed, extra)) => {
let payload = (xt.function, extra.clone(), version, genesis_hash, genesis_hash);
let key = AccountKeyring::from_account_id(&signed).unwrap();
let signature = payload.using_encoded(|b| {
if b.len() > 256 {
key.sign(&runtime_io::hashing::blake2_256(b))
} else {
key.sign(b)
}
}).into();
UncheckedExtrinsic {
signature: Some((indices::address::Address::Id(signed), signature, extra)),
function: payload.0,
}
}
None => UncheckedExtrinsic {
signature: None,
function: xt.function,
},
}
}
+24
View File
@@ -0,0 +1,24 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! A set of testing utilities for Substrate Node.
#![warn(missing_docs)]
pub mod client;
pub mod genesis;
pub mod keyring;
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "subkey"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
primitives = { package = "substrate-primitives", version = "*", path = "../../primitives/core" }
node-runtime = { version = "*", path = "../node/runtime" }
node-primitives = { version = "*", path = "../node/primitives" }
sr-primitives = { version = "*", path = "../../primitives/sr-primitives" }
rand = "0.7.2"
clap = { version = "2.33.0", features = ["yaml"] }
tiny-bip39 = "0.6.2"
rustc-hex = "2.0.1"
substrate-bip39 = "0.3.1"
hex = "0.3.2"
hex-literal = "0.2.1"
codec = { package = "parity-scale-codec", version = "1.0.0" }
system = { package = "paint-system", path = "../../paint/system" }
balances = { package = "paint-balances", path = "../../paint/balances" }
transaction-payment = { package = "paint-transaction-payment", path = "../../paint/transaction-payment" }
[features]
bench = []
+70
View File
@@ -0,0 +1,70 @@
= Subkey
Subkey is a commandline utility included with Substrate that generates or restores Substrate keys.
`subkey` will use the http://wiki.polkadot.network/en/latest/polkadot/learn/cryptography/#keypairs-and-signing[sr25519] cryptography by default. If you need to use the older ed25519 cryptography to generate or restore your key pass the `--ed25519` flag to any of the commands.
== Usage
=== Generate a random account
```bash
subkey generate
```
Will output a mnemonic phrase and give you the seed, public key, and address of a new account. DO NOT SHARE your mnemonic or seed with ANYONE it will give them access to your funds. If someone is making a transfer to you they will only need your **Address**.
=== Inspecting a key
You can inspect a given URI (mnemonic, seed, public key, or address) and recover the public key and the address.
```bash
subkey inspect <mnemonic,seed,pubkey,address>
OUTPUT:
Public key (hex): 0x461edcf1ba99e43f50dec4bdeb3d1a2cf521ad7c3cd0eeee5cd3314e50fd424c
Address (SS58): 5DeeNqcAcaHDSed2HYnqMDK7JHcvxZ5QUE9EKmjc5snvU6wF
```
=== Signing
`subkey` expects a message to come in on STDIN, one way to sign a message would look like this:
```bash
echo -n <msg> | subkey sign <seed,mnemonic>
OUTPUT:
a69da4a6ccbf81dbbbfad235fa12cf8528c18012b991ae89214de8d20d29c1280576ced6eb38b7406d1b7e03231df6dd4a5257546ddad13259356e1c3adfb509
```
=== Verifying a signature
```bash
echo -n <msg> | subkey verify <sig> <address>
OUTPUT:
Signature verifies correctly.
```
=== Using the vanity generator
You can use the included vanity generator to find a seed that provides an address which includes the desired pattern. Be warned, depending on your hardware this may take a while.
```bash
subkey vanity 1337
```
=== Signing a transaction
Sign a transaction from an encoded `Call`.
```bash
subkey sign-transaction \
--call <call-as-hex> \
--nonce 0 \
--suri <secret-uri> \
--password <password> \
--prior-block-hash <prior-block-hash-as-hex>
```
Will output a signed and encoded `UncheckedMortalCompactExtrinsic` as hex.
+144
View File
@@ -0,0 +1,144 @@
name: subkey
author: "Parity Team <admin@parity.io>"
about: Utility for generating and restoring with Substrate keys
args:
- ed25519:
short: e
long: ed25519
help: Use Ed25519/BIP39 cryptography
takes_value: false
- sr25519:
short: s
long: sr25519
help: Use Schnorr/Ristretto x25519/BIP39 cryptography
takes_value: false
- secp256k1:
short: k
long: secp256k1
help: Use SECP256k1/ECDSA/BIP39 cryptography
takes_value: false
- password:
short: p
long: password
takes_value: true
required: false
help: The password for the key
- network:
short: n
long: network
takes_value: true
required: false
help: Specify a network. One of substrate (default), polkadot, kusama, or dothereum.
subcommands:
- generate:
about: Generate a random account
args:
- words:
short: w
long: words
help: The number of words in the phrase to generate. One of 12 (default), 15, 18, 21 and 24.
takes_value: true
- inspect:
about: Gets a public key and a SS58 address from the provided Secret URI
args:
- uri:
index: 1
required: true
help: A Key URI to be inspected. May be a secret seed, secret URI (with derivation paths and password), SS58 or public URI.
- sign:
about: Sign a message, provided on STDIN, with a given (secret) key
args:
- suri:
index: 1
required: true
help: The secret key URI.
- hex:
short: h
long: hex
help: The message on STDIN is hex-encoded data
takes_value: false
- transfer:
about: Author and sign a Node balances::Transfer transaction with a given (secret) key
args:
- from:
index: 1
required: true
help: The signing secret key URI.
- to:
index: 2
required: true
help: The destination account public key URI.
- amount:
index: 3
required: true
help: The number of units to transfer.
- index:
index: 4
required: true
help: The signing account's transaction index.
- genesis:
short: g
long: genesis
help: The genesis hash or a recognised chain identifier (dev, elm, alex).
takes_value: true
- verify:
about: Verify a signature for a message, provided on STDIN, with a given (public or secret) key
args:
- sig:
index: 1
required: true
help: Signature, hex-encoded.
- uri:
index: 2
required: true
help: The public or secret key URI.
- hex:
short: h
long: hex
help: The message on STDIN is hex-encoded data
takes_value: false
- vanity:
about: Generate a seed that provides a vanity address
args:
- pattern:
index: 1
help: Desired pattern
- number:
short: n
long: number
help: Number of keys to generate
takes_value: true
default_value: "1"
- sign-transaction:
about: Sign transaction from encoded Call. Returns a signed and encoded UncheckedMortalCompactExtrinsic as hex.
args:
- call:
short: c
long: call
help: The call, hex-encoded.
takes_value: true
required: true
- nonce:
short: n
long: nonce
help: The nonce.
takes_value: true
required: true
- suri:
long: suri
short: s
help: The secret key URI.
takes_value: true
required: true
- password:
short: p
long: password
takes_value: true
help: The password for the key.
required: true
- prior-block-hash:
short: h
long: prior-block-hash
help: The prior block hash, hex-encoded.
takes_value: true
required: true
+534
View File
@@ -0,0 +1,534 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
#![cfg_attr(feature = "bench", feature(test))]
#[cfg(feature = "bench")]
extern crate test;
use bip39::{Language, Mnemonic, MnemonicType};
use clap::{load_yaml, App, ArgMatches};
use codec::{Decode, Encode};
use hex_literal::hex;
use node_primitives::{Balance, Hash, Index, AccountId, Signature};
use node_runtime::{BalancesCall, Call, Runtime, SignedPayload, UncheckedExtrinsic, VERSION};
use primitives::{
crypto::{set_default_ss58_version, Ss58AddressFormat, Ss58Codec},
ed25519, sr25519, ecdsa, Pair, Public, H256, hexdisplay::HexDisplay,
};
use sr_primitives::{traits::{IdentifyAccount, Verify}, generic::Era};
use std::{
convert::{TryInto, TryFrom},
io::{stdin, Read},
str::FromStr,
};
mod vanity;
trait Crypto: Sized {
type Pair: Pair<Public = Self::Public>;
type Public: Public + Ss58Codec + AsRef<[u8]> + std::hash::Hash;
fn pair_from_suri(suri: &str, password: Option<&str>) -> Self::Pair {
Self::Pair::from_string(suri, password).expect("Invalid phrase")
}
fn ss58_from_pair(pair: &Self::Pair) -> String where
<Self::Pair as Pair>::Public: PublicT,
{
pair.public().into_runtime().into_account().to_ss58check()
}
fn public_from_pair(pair: &Self::Pair) -> Self::Public {
pair.public()
}
fn print_from_uri(
uri: &str,
password: Option<&str>,
network_override: Option<Ss58AddressFormat>,
) where
<Self::Pair as Pair>::Public: PublicT,
{
if let Ok((pair, seed)) = Self::Pair::from_phrase(uri, password) {
let public_key = Self::public_from_pair(&pair);
println!("Secret phrase `{}` is account:\n \
Secret seed: {}\n \
Public key (hex): {}\n \
Account ID: {}\n \
SS58 Address: {}",
uri,
format_seed::<Self>(seed),
format_public_key::<Self>(public_key.clone()),
format_account_id::<Self>(public_key),
Self::ss58_from_pair(&pair)
);
} else if let Ok((pair, seed)) = Self::Pair::from_string_with_seed(uri, password) {
let public_key = Self::public_from_pair(&pair);
println!("Secret Key URI `{}` is account:\n \
Secret seed: {}\n \
Public key (hex): {}\n \
Account ID: {}\n \
SS58 Address: {}",
uri,
if let Some(seed) = seed { format_seed::<Self>(seed) } else { "n/a".into() },
format_public_key::<Self>(public_key.clone()),
format_account_id::<Self>(public_key),
Self::ss58_from_pair(&pair)
);
} else if let Ok((public_key, v)) =
<Self::Pair as Pair>::Public::from_string_with_version(uri)
{
let v = network_override.unwrap_or(v);
println!("Public Key URI `{}` is account:\n \
Network ID/version: {}\n \
Public key (hex): {}\n \
Account ID: {}\n \
SS58 Address: {}",
uri,
String::from(v),
format_public_key::<Self>(public_key.clone()),
format_account_id::<Self>(public_key.clone()),
public_key.to_ss58check_with_version(v)
);
} else {
println!("Invalid phrase/URI given");
}
}
}
struct Ed25519;
impl Crypto for Ed25519 {
type Pair = ed25519::Pair;
type Public = ed25519::Public;
fn pair_from_suri(suri: &str, password_override: Option<&str>) -> Self::Pair {
ed25519::Pair::from_legacy_string(suri, password_override)
}
}
struct Sr25519;
impl Crypto for Sr25519 {
type Pair = sr25519::Pair;
type Public = sr25519::Public;
}
struct Ecdsa;
impl Crypto for Ecdsa {
type Pair = ecdsa::Pair;
type Public = ecdsa::Public;
}
type SignatureOf<C> = <<C as Crypto>::Pair as Pair>::Signature;
type PublicOf<C> = <<C as Crypto>::Pair as Pair>::Public;
type SeedOf<C> = <<C as Crypto>::Pair as Pair>::Seed;
type AccountPublic = <Signature as Verify>::Signer;
trait SignatureT: AsRef<[u8]> + AsMut<[u8]> + Default {
/// Converts the signature into a runtime account signature, if possible. If not possible, bombs out.
fn into_runtime(self) -> Signature {
panic!("This cryptography isn't supported for this runtime.")
}
}
trait PublicT: Sized + AsRef<[u8]> + Ss58Codec {
/// Converts the public key into a runtime account public key, if possible. If not possible, bombs out.
fn into_runtime(self) -> AccountPublic {
panic!("This cryptography isn't supported for this runtime.")
}
}
impl SignatureT for sr25519::Signature { fn into_runtime(self) -> Signature { self.into() } }
impl SignatureT for ed25519::Signature { fn into_runtime(self) -> Signature { self.into() } }
impl SignatureT for ecdsa::Signature { fn into_runtime(self) -> Signature { self.into() } }
impl PublicT for sr25519::Public { fn into_runtime(self) -> AccountPublic { self.into() } }
impl PublicT for ed25519::Public { fn into_runtime(self) -> AccountPublic { self.into() } }
impl PublicT for ecdsa::Public { fn into_runtime(self) -> AccountPublic { self.into() } }
fn main() {
let yaml = load_yaml!("cli.yml");
let matches = App::from_yaml(yaml)
.version(env!("CARGO_PKG_VERSION"))
.get_matches();
if matches.is_present("ed25519") {
return execute::<Ed25519>(matches)
}
if matches.is_present("secp256k1") {
return execute::<Ecdsa>(matches)
}
return execute::<Sr25519>(matches)
}
fn execute<C: Crypto>(matches: ArgMatches)
where
SignatureOf<C>: SignatureT,
PublicOf<C>: PublicT,
{
let password = matches.value_of("password");
let maybe_network: Option<Ss58AddressFormat> = matches.value_of("network").map(|network| {
network
.try_into()
.expect("Invalid network name: must be polkadot/substrate/kusama/dothereum")
});
if let Some(network) = maybe_network {
set_default_ss58_version(network);
}
match matches.subcommand() {
("generate", Some(matches)) => {
let mnemonic = generate_mnemonic(matches);
C::print_from_uri(mnemonic.phrase(), password, maybe_network);
}
("inspect", Some(matches)) => {
let uri = matches
.value_of("uri")
.expect("URI parameter is required; thus it can't be None; qed");
C::print_from_uri(uri, password, maybe_network);
}
("sign", Some(matches)) => {
let should_decode = matches.is_present("hex");
let message = read_message_from_stdin(should_decode);
let signature = do_sign::<C>(matches, message, password);
println!("{}", signature);
}
("verify", Some(matches)) => {
let should_decode = matches.is_present("hex");
let message = read_message_from_stdin(should_decode);
let is_valid_signature = do_verify::<C>(matches, message);
if is_valid_signature {
println!("Signature verifies correctly.");
} else {
println!("Signature invalid.");
}
}
("vanity", Some(matches)) => {
let desired: String = matches
.value_of("pattern")
.map(str::to_string)
.unwrap_or_default();
let result = vanity::generate_key::<C>(&desired).expect("Key generation failed");
let formated_seed = format_seed::<C>(result.seed);
C::print_from_uri(&formated_seed, None, maybe_network);
}
("transfer", Some(matches)) => {
let signer = read_pair::<C>(matches.value_of("from"), password);
let index = read_required_parameter::<Index>(matches, "index");
let genesis_hash = read_genesis_hash(matches);
let to: AccountId = read_account_id(matches.value_of("to"));
let amount = read_required_parameter::<Balance>(matches, "amount");
let function = Call::Balances(BalancesCall::transfer(to.into(), amount));
let extrinsic = create_extrinsic::<C>(function, index, signer, genesis_hash);
print_extrinsic(extrinsic);
}
("sign-transaction", Some(matches)) => {
let signer = read_pair::<C>(matches.value_of("suri"), password);
let index = read_required_parameter::<Index>(matches, "nonce");
let genesis_hash = read_genesis_hash(matches);
let call = matches.value_of("call").expect("call is required; qed");
let function: Call = hex::decode(&call)
.ok()
.and_then(|x| Decode::decode(&mut &x[..]).ok())
.unwrap();
let extrinsic = create_extrinsic::<C>(function, index, signer, genesis_hash);
print_extrinsic(extrinsic);
}
_ => print_usage(&matches),
}
}
/// Creates a new randomly generated mnemonic phrase.
fn generate_mnemonic(matches: &ArgMatches) -> Mnemonic {
let words = matches
.value_of("words")
.map(|x| usize::from_str(x).expect("Invalid number given for --words"))
.map(|x| {
MnemonicType::for_word_count(x)
.expect("Invalid number of words given for phrase: must be 12/15/18/21/24")
})
.unwrap_or(MnemonicType::Words12);
Mnemonic::new(words, Language::English)
}
fn do_sign<C: Crypto>(matches: &ArgMatches, message: Vec<u8>, password: Option<&str>) -> String
where
SignatureOf<C>: SignatureT,
PublicOf<C>: PublicT,
{
let pair = read_pair::<C>(matches.value_of("suri"), password);
let signature = pair.sign(&message);
format_signature::<C>(&signature)
}
fn do_verify<C: Crypto>(matches: &ArgMatches, message: Vec<u8>) -> bool
where
SignatureOf<C>: SignatureT,
PublicOf<C>: PublicT,
{
let signature = read_signature::<C>(matches);
let pubkey = read_public_key::<C>(matches.value_of("uri"));
<<C as Crypto>::Pair as Pair>::verify(&signature, &message, &pubkey)
}
fn read_message_from_stdin(should_decode: bool) -> Vec<u8> {
let mut message = vec![];
stdin()
.lock()
.read_to_end(&mut message)
.expect("Error reading from stdin");
if should_decode {
message = hex::decode(&message).expect("Invalid hex in message");
}
message
}
fn read_required_parameter<T: FromStr>(matches: &ArgMatches, name: &str) -> T
where
<T as FromStr>::Err: std::fmt::Debug,
{
let str_value = matches
.value_of(name)
.expect("parameter is required; thus it can't be None; qed");
str::parse::<T>(str_value).expect("Invalid 'nonce' parameter; expecting an integer.")
}
fn read_genesis_hash(matches: &ArgMatches) -> H256 {
let genesis_hash: Hash = match matches.value_of("genesis").unwrap_or("alex") {
"elm" => hex!["10c08714a10c7da78f40a60f6f732cf0dba97acfb5e2035445b032386157d5c3"].into(),
"alex" => hex!["dcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b"].into(),
h => hex::decode(h)
.ok()
.and_then(|x| Decode::decode(&mut &x[..]).ok())
.expect("Invalid genesis hash or unrecognised chain identifier"),
};
println!(
"Using a genesis hash of {}",
HexDisplay::from(&genesis_hash.as_ref())
);
genesis_hash
}
fn read_signature<C: Crypto>(matches: &ArgMatches) -> SignatureOf<C>
where
SignatureOf<C>: SignatureT,
PublicOf<C>: PublicT,
{
let sig_data = matches
.value_of("sig")
.expect("signature parameter is required; thus it can't be None; qed");
let mut signature = <<C as Crypto>::Pair as Pair>::Signature::default();
let sig_data = hex::decode(sig_data).expect("signature is invalid hex");
if sig_data.len() != signature.as_ref().len() {
panic!(
"signature has an invalid length. read {} bytes, expected {} bytes",
sig_data.len(),
signature.as_ref().len(),
);
}
signature.as_mut().copy_from_slice(&sig_data);
signature
}
fn read_public_key<C: Crypto>(matched_uri: Option<&str>) -> PublicOf<C> where
PublicOf<C>: PublicT,
{
let uri = matched_uri.expect("parameter is required; thus it can't be None; qed");
let uri = if uri.starts_with("0x") {
&uri[2..]
} else {
uri
};
if let Ok(pubkey_vec) = hex::decode(uri) {
<C as Crypto>::Public::from_slice(pubkey_vec.as_slice())
} else {
<C as Crypto>::Public::from_string(uri)
.ok()
.expect("Invalid URI; expecting either a secret URI or a public URI.")
}
}
fn read_account_id(matched_uri: Option<&str>) -> AccountId {
let uri = matched_uri.expect("parameter is required; thus it can't be None; qed");
let uri = if uri.starts_with("0x") {
&uri[2..]
} else {
uri
};
if let Ok(data_vec) = hex::decode(uri) {
AccountId::try_from(data_vec.as_slice())
.expect("Invalid hex length for account ID; should be 32 bytes")
} else {
AccountId::from_ss58check(uri).ok()
.expect("Invalid SS58-check address given for account ID.")
}
}
fn read_pair<C: Crypto>(
matched_suri: Option<&str>,
password: Option<&str>,
) -> <C as Crypto>::Pair
where
SignatureOf<C>: SignatureT,
PublicOf<C>: PublicT,
{
let suri = matched_suri.expect("parameter is required; thus it can't be None; qed");
C::pair_from_suri(suri, password)
}
fn format_signature<C: Crypto>(signature: &SignatureOf<C>) -> String {
format!("{}", hex::encode(signature))
}
fn format_seed<C: Crypto>(seed: SeedOf<C>) -> String {
format!("0x{}", HexDisplay::from(&seed.as_ref()))
}
fn format_public_key<C: Crypto>(public_key: PublicOf<C>) -> String {
format!("0x{}", HexDisplay::from(&public_key.as_ref()))
}
fn format_account_id<C: Crypto>(public_key: PublicOf<C>) -> String where
PublicOf<C>: PublicT,
{
format!("0x{}", HexDisplay::from(&public_key.into_runtime().into_account().as_ref()))
}
fn create_extrinsic<C: Crypto>(
function: Call,
index: Index,
signer: C::Pair,
genesis_hash: H256,
) -> UncheckedExtrinsic where
PublicOf<C>: PublicT,
SignatureOf<C>: SignatureT,
{
let extra = |i: Index, f: Balance| {
(
system::CheckVersion::<Runtime>::new(),
system::CheckGenesis::<Runtime>::new(),
system::CheckEra::<Runtime>::from(Era::Immortal),
system::CheckNonce::<Runtime>::from(i),
system::CheckWeight::<Runtime>::new(),
transaction_payment::ChargeTransactionPayment::<Runtime>::from(f),
Default::default(),
)
};
let raw_payload = SignedPayload::from_raw(
function,
extra(index, 0),
(
VERSION.spec_version as u32,
genesis_hash,
genesis_hash,
(),
(),
(),
(),
),
);
let signature = raw_payload.using_encoded(|payload| signer.sign(payload)).into_runtime();
let signer = signer.public().into_runtime();
let (function, extra, _) = raw_payload.deconstruct();
UncheckedExtrinsic::new_signed(
function,
signer.into_account().into(),
signature,
extra,
)
}
fn print_extrinsic(extrinsic: UncheckedExtrinsic) {
println!("0x{}", hex::encode(&extrinsic.encode()));
}
fn print_usage(matches: &ArgMatches) {
println!("{}", matches.usage());
}
#[cfg(test)]
mod tests {
use super::*;
fn test_generate_sign_verify<CryptoType: Crypto>()
where
SignatureOf<CryptoType>: SignatureT,
PublicOf<CryptoType>: PublicT,
{
let yaml = load_yaml!("cli.yml");
let app = App::from_yaml(yaml);
let password = None;
// Generate public key and seed.
let arg_vec = vec!["subkey", "generate"];
let matches = app.clone().get_matches_from(arg_vec);
let matches = matches.subcommand().1.unwrap();
let mnemonic = generate_mnemonic(matches);
let (pair, seed) =
<<CryptoType as Crypto>::Pair as Pair>::from_phrase(mnemonic.phrase(), password)
.unwrap();
let public_key = CryptoType::public_from_pair(&pair);
let public_key = format_public_key::<CryptoType>(public_key);
let seed = format_seed::<CryptoType>(seed);
// Sign a message using previous seed.
let arg_vec = vec!["subkey", "sign", &seed[..]];
let matches = app.get_matches_from(arg_vec);
let matches = matches.subcommand().1.unwrap();
let message = "Blah Blah\n".as_bytes().to_vec();
let signature = do_sign::<CryptoType>(matches, message.clone(), password);
// Verify the previous signature.
let arg_vec = vec!["subkey", "verify", &signature[..], &public_key[..]];
let matches = App::from_yaml(yaml).get_matches_from(arg_vec);
let matches = matches.subcommand().1.unwrap();
assert!(do_verify::<CryptoType>(matches, message));
}
#[test]
fn generate_sign_verify_should_work_for_ed25519() {
test_generate_sign_verify::<Ed25519>();
}
#[test]
fn generate_sign_verify_should_work_for_sr25519() {
test_generate_sign_verify::<Sr25519>();
}
#[test]
fn should_work() {
let s = "0123456789012345678901234567890123456789012345678901234567890123";
let d1: Hash = hex::decode(s)
.ok()
.and_then(|x| Decode::decode(&mut &x[..]).ok())
.unwrap();
let d2: Hash = {
let mut gh: [u8; 32] = Default::default();
gh.copy_from_slice(hex::decode(s).unwrap().as_ref());
Hash::from(gh)
};
assert_eq!(d1, d2);
}
}
+176
View File
@@ -0,0 +1,176 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use super::{PublicOf, PublicT, Crypto};
use primitives::Pair;
use rand::{rngs::OsRng, RngCore};
fn good_waypoint(done: u64) -> u64 {
match done {
0..=1_000_000 => 100_000,
0..=10_000_000 => 1_000_000,
0..=100_000_000 => 10_000_000,
_ => 100_000_000,
}
}
fn next_seed(seed: &mut [u8]) {
for i in 0..seed.len() {
match seed[i] {
255 => {
seed[i] = 0;
}
_ => {
seed[i] += 1;
break;
}
}
}
}
/// A structure used to carry both Pair and seed.
/// This should usually NOT been used. If unsure, use Pair.
pub(super) struct KeyPair<C: Crypto> {
pub pair: C::Pair,
pub seed: <C::Pair as Pair>::Seed,
pub score: usize,
}
/// Calculate the score of a key based on the desired
/// input.
fn calculate_score(_desired: &str, key: &str) -> usize {
for truncate in 0.._desired.len() {
let snip_size = _desired.len() - truncate;
let truncated = &_desired[0..snip_size];
if let Some(pos) = key.find(truncated) {
return (47 - pos) + (snip_size * 48);
}
}
0
}
pub(super) fn generate_key<C: Crypto>(desired: &str) -> Result<KeyPair<C>, &str> where
PublicOf<C>: PublicT,
{
if desired.is_empty() {
return Err("Pattern must not be empty");
}
println!("Generating key containing pattern '{}'", desired);
let top = 45 + (desired.len() * 48);
let mut best = 0;
let mut seed = <C::Pair as Pair>::Seed::default();
let mut done = 0;
loop {
if done % 100000 == 0 {
OsRng.fill_bytes(seed.as_mut());
} else {
next_seed(seed.as_mut());
}
let p = C::Pair::from_seed(&seed);
let ss58 = C::ss58_from_pair(&p);
let score = calculate_score(&desired, &ss58);
if score > best || desired.len() < 2 {
best = score;
let keypair = KeyPair {
pair: p,
seed: seed.clone(),
score: score,
};
if best >= top {
println!("best: {} == top: {}", best, top);
return Ok(keypair);
}
}
done += 1;
if done % good_waypoint(done) == 0 {
println!("{} keys searched; best is {}/{} complete", done, best, top);
}
}
}
#[cfg(test)]
mod tests {
use super::super::Ed25519;
use super::*;
use primitives::{crypto::Ss58Codec, Pair};
#[cfg(feature = "bench")]
use test::Bencher;
#[test]
fn test_generation_with_single_char() {
assert!(generate_key::<Ed25519>("j")
.unwrap()
.pair
.public()
.to_ss58check()
.contains("j"));
}
#[test]
fn test_score_1_char_100() {
let score = calculate_score("j", "5jolkadotwHY5k9GpdTgpqs9xjuNvtv8EcwCFpEeyEf3KHim");
assert_eq!(score, 94);
}
#[test]
fn test_score_100() {
let score = calculate_score(
"Polkadot",
"5PolkadotwHY5k9GpdTgpqs9xjuNvtv8EcwCFpEeyEf3KHim",
);
assert_eq!(score, 430);
}
#[test]
fn test_score_50_2() {
// 50% for the position + 50% for the size
assert_eq!(
calculate_score(
"Polkadot",
"5PolkXXXXwHY5k9GpdTgpqs9xjuNvtv8EcwCFpEeyEf3KHim"
),
238
);
}
#[test]
fn test_score_0() {
assert_eq!(
calculate_score(
"Polkadot",
"5GUWv4bLCchGUHJrzULXnh4JgXsMpTKRnjuXTY7Qo1Kh9uYK"
),
0
);
}
#[cfg(feature = "bench")]
#[bench]
fn bench_paranoiac(b: &mut Bencher) {
b.iter(|| generate_key("polk"));
}
#[cfg(feature = "bench")]
#[bench]
fn bench_not_paranoiac(b: &mut Bencher) {
b.iter(|| generate_key("polk"));
}
}