mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 17:31:05 +00:00
Minimal switch of substrate-node to GRANDPA /Aura (#1128)
* add beginnings of SRML grandpa library * get srml-grandpa compiling * tests for srml-grandpa * add optional session integration to grandpa SRML * start integration into node runtime * Allow extracting pending change from header digest * Make it compile on wasm * make tests compile again * Move Authority Key fetching into service, simplify service factory construction * Generalize Authority Consensus Setup system * Add Authority Setup Docs * Allow CLI params to be extensible - move params to structopts - split parsing and default command execution - add custom config to node - extended parsing of custom config - extending params via structop's flatten * Minor fixes on cli extension params: - added docs - re-add actual app name, rather than node-name - make strategy and subcommand optional * better cli params * synchronize GRANDPA and normal node authorities * Implement grandpa::network for gossip consensus * run_grandpa in Node * Fix missed merge error * Integrate grandpa import queue * more specific type def * link up linkhalf and import block * make grandpa future send * get compiling * Fix new params convention and license header * get it running * rebuild node runtime WASM * change logging level * Update node/cli/src/params.rs Co-Authored-By: rphmeier <rphmeier@gmail.com> * Update node/cli/src/params.rs Co-Authored-By: rphmeier <rphmeier@gmail.com> * Update node/cli/src/lib.rs Co-Authored-By: rphmeier <rphmeier@gmail.com> * Update node/runtime/src/lib.rs Co-Authored-By: rphmeier <rphmeier@gmail.com> * Update node/cli/src/lib.rs Co-Authored-By: rphmeier <rphmeier@gmail.com> * Clean up and Fixme for mutable config * Move GrandpaService Integration into grandpa, feature gated but on per default * Fixing grandpa runtime module test * Update wasm runtime hashes for tests * GRANDPA: use post-header hash when logging scheduled changes * add an extra bit of logging to authorities * fixing missing constrain * remove old code * move `NewAuthorities` to an event in srml-grandpa * fix node-executor tests to use grandpa log * Remove GossipConsensus from tests, use newly provided sync-feature, fixes tests * Update to latest wasm runtimes * address grumbles * address grumbles * only derive deserialize when using std * Clean up use of Deserialize
This commit is contained in:
committed by
GitHub
parent
84da9d4a02
commit
11fe84a742
@@ -3,10 +3,9 @@ name = "substrate-cli"
|
||||
version = "0.3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
description = "Substrate CLI interface."
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "~2.32", features = ["yaml"] }
|
||||
clap = "~2.32"
|
||||
backtrace = "0.3"
|
||||
env_logger = "0.5"
|
||||
error-chain = "0.12"
|
||||
@@ -30,6 +29,4 @@ substrate-primitives = { path = "../../core/primitives" }
|
||||
substrate-service = { path = "../../core/service" }
|
||||
substrate-telemetry = { path = "../../core/telemetry" }
|
||||
names = "0.11.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = "~2.32"
|
||||
structopt = "0.2.13"
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright 2017-2018 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/>.
|
||||
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use clap::Shell;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
build_shell_completion();
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
let shells = [Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell];
|
||||
for shell in shells.iter() {
|
||||
build_completion(shell);
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the shell auto-completion for a given Shell
|
||||
fn build_completion(shell: &Shell) {
|
||||
let yml = load_yaml!("src/cli.yml");
|
||||
|
||||
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();
|
||||
|
||||
let mut app = clap::App::from_yaml(&yml);
|
||||
app.gen_completions(
|
||||
"polkadot",
|
||||
*shell,
|
||||
&path);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
== 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
|
||||
----
|
||||
@@ -1,253 +0,0 @@
|
||||
name: {name}
|
||||
author: {author}
|
||||
about: {description}
|
||||
args:
|
||||
- log:
|
||||
short: l
|
||||
long: log
|
||||
value_name: LOG_PATTERN
|
||||
help: Sets a custom logging filter
|
||||
takes_value: true
|
||||
- base-path:
|
||||
long: base-path
|
||||
short: d
|
||||
value_name: PATH
|
||||
help: Specify custom base path
|
||||
takes_value: true
|
||||
- keystore-path:
|
||||
long: keystore-path
|
||||
value_name: PATH
|
||||
help: Specify custom keystore path
|
||||
takes_value: true
|
||||
- key:
|
||||
long: key
|
||||
value_name: STRING
|
||||
help: Specify additional key seed
|
||||
takes_value: true
|
||||
- node-key:
|
||||
long: node-key
|
||||
value_name: KEY
|
||||
help: Specify node secret key (64-character hex string)
|
||||
takes_value: true
|
||||
- validator:
|
||||
long: validator
|
||||
help: Enable validator mode
|
||||
takes_value: false
|
||||
- light:
|
||||
long: light
|
||||
help: Run in light client mode
|
||||
takes_value: false
|
||||
- dev:
|
||||
long: dev
|
||||
help: Run in development mode; implies --chain=dev --validator --key Alice
|
||||
takes_value: false
|
||||
- listen-addr:
|
||||
long: listen-addr
|
||||
value_name: LISTEN_ADDR
|
||||
help: Listen on this multiaddress
|
||||
takes_value: true
|
||||
multiple: true
|
||||
- port:
|
||||
long: port
|
||||
value_name: PORT
|
||||
help: Specify p2p protocol TCP port. Only used if --listen-addr is not specified.
|
||||
takes_value: true
|
||||
- rpc-external:
|
||||
long: rpc-external
|
||||
help: Listen to all RPC interfaces (default is local)
|
||||
takes_value: false
|
||||
- ws-external:
|
||||
long: ws-external
|
||||
help: Listen to all Websocket interfaces (default is local)
|
||||
takes_value: false
|
||||
- rpc-port:
|
||||
long: rpc-port
|
||||
value_name: PORT
|
||||
help: Specify HTTP RPC server TCP port
|
||||
takes_value: true
|
||||
- ws-port:
|
||||
long: ws-port
|
||||
value_name: PORT
|
||||
help: Specify WebSockets RPC server TCP port
|
||||
takes_value: true
|
||||
- bootnodes:
|
||||
long: bootnodes
|
||||
value_name: URL
|
||||
help: Specify a list of bootnodes
|
||||
takes_value: true
|
||||
multiple: true
|
||||
- reserved-nodes:
|
||||
long: reserved-nodes
|
||||
value_name: URL
|
||||
help: Specify a list of reserved node addresses
|
||||
takes_value: true
|
||||
multiple: true
|
||||
- out-peers:
|
||||
long: out-peers
|
||||
value_name: OUT_PEERS
|
||||
help: Specify the number of outgoing connections we're trying to maintain
|
||||
takes_value: true
|
||||
- in-peers:
|
||||
long: in-peers
|
||||
value_name: IN_PEERS
|
||||
help: Specify the maximum number of incoming connections we're accepting
|
||||
takes_value: true
|
||||
- chain:
|
||||
long: chain
|
||||
value_name: CHAIN_SPEC
|
||||
help: Specify the chain specification (one of dev, local or staging)
|
||||
takes_value: true
|
||||
- pruning:
|
||||
long: pruning
|
||||
value_name: PRUNING_MODE
|
||||
help: Specify the pruning mode, a number of blocks to keep or "archive". Default is 256.
|
||||
takes_value: true
|
||||
- name:
|
||||
long: name
|
||||
value_name: NAME
|
||||
help: The human-readable name for this node, as reported to the telemetry server, if enabled
|
||||
takes_value: true
|
||||
- telemetry:
|
||||
short: t
|
||||
long: telemetry
|
||||
help: Should connect to the Substrate telemetry server (telemetry is off by default on local chains)
|
||||
takes_value: false
|
||||
- no-telemetry:
|
||||
long: no-telemetry
|
||||
help: Should not connect to the Substrate telemetry server (telemetry is on by default on global chains)
|
||||
takes_value: false
|
||||
- telemetry-url:
|
||||
long: telemetry-url
|
||||
value_name: TELEMETRY_URL
|
||||
help: The URL of the telemetry server. Implies --telemetry
|
||||
takes_value: true
|
||||
- execution:
|
||||
long: execution
|
||||
value_name: STRATEGY
|
||||
help: The means of execution used when calling into the runtime. Can be either wasm, native or both.
|
||||
subcommands:
|
||||
- build-spec:
|
||||
about: Build a spec.json file, outputing to stdout
|
||||
args:
|
||||
- raw:
|
||||
long: raw
|
||||
help: Force raw genesis storage output.
|
||||
takes_value: false
|
||||
- chain:
|
||||
long: chain
|
||||
value_name: CHAIN_SPEC
|
||||
help: Specify the chain specification (one of dev, local or staging)
|
||||
takes_value: true
|
||||
- dev:
|
||||
long: dev
|
||||
help: Specify the development chain
|
||||
takes_value: false
|
||||
- export-blocks:
|
||||
about: Export blocks to a file
|
||||
args:
|
||||
- OUTPUT:
|
||||
index: 1
|
||||
help: Output file name or stdout if unspecified.
|
||||
required: false
|
||||
- chain:
|
||||
long: chain
|
||||
value_name: CHAIN_SPEC
|
||||
help: Specify the chain specification.
|
||||
takes_value: true
|
||||
- dev:
|
||||
long: dev
|
||||
help: Specify the development chain
|
||||
takes_value: false
|
||||
- base-path:
|
||||
long: base-path
|
||||
short: d
|
||||
value_name: PATH
|
||||
help: Specify custom base path.
|
||||
takes_value: true
|
||||
- from:
|
||||
long: from
|
||||
value_name: BLOCK
|
||||
help: Specify starting block number. 1 by default.
|
||||
takes_value: true
|
||||
- to:
|
||||
long: to
|
||||
value_name: BLOCK
|
||||
help: Specify last block number. Best block by default.
|
||||
takes_value: true
|
||||
- json:
|
||||
long: json
|
||||
help: Use JSON output rather than binary.
|
||||
takes_value: false
|
||||
- import-blocks:
|
||||
about: Import blocks from file.
|
||||
args:
|
||||
- INPUT:
|
||||
index: 1
|
||||
help: Input file or stdin if unspecified.
|
||||
required: false
|
||||
- chain:
|
||||
long: chain
|
||||
value_name: CHAIN_SPEC
|
||||
help: Specify the chain specification.
|
||||
takes_value: true
|
||||
- dev:
|
||||
long: dev
|
||||
help: Specify the development chain
|
||||
takes_value: false
|
||||
- base-path:
|
||||
long: base-path
|
||||
short: d
|
||||
value_name: PATH
|
||||
help: Specify custom base path.
|
||||
takes_value: true
|
||||
- execution:
|
||||
long: execution
|
||||
value_name: STRATEGY
|
||||
help: The means of execution used when executing blocks. Can be either wasm, native or both.
|
||||
- api-execution:
|
||||
long: api-execution
|
||||
value_name: STRATEGY
|
||||
help: The means of execution used when calling into the runtime. Can be either wasm, native or both.
|
||||
- max-heap-pages:
|
||||
long: max-heap-pages
|
||||
value_name: COUNT
|
||||
help: The maximum number of 64KB pages to ever allocate for Wasm execution. Don't alter this unless you know what you're doing.
|
||||
- revert:
|
||||
about: Revert chain to the previous state
|
||||
args:
|
||||
- NUM:
|
||||
index: 1
|
||||
help: Number of blocks to revert. Default is 256.
|
||||
- chain:
|
||||
long: chain
|
||||
value_name: CHAIN_SPEC
|
||||
help: Specify the chain specification.
|
||||
takes_value: true
|
||||
- dev:
|
||||
long: dev
|
||||
help: Specify the development chain
|
||||
takes_value: false
|
||||
- base-path:
|
||||
long: base-path
|
||||
short: d
|
||||
value_name: PATH
|
||||
help: Specify custom base path.
|
||||
takes_value: true
|
||||
- purge-chain:
|
||||
about: Remove the whole chain data.
|
||||
args:
|
||||
- chain:
|
||||
long: chain
|
||||
value_name: CHAIN_SPEC
|
||||
help: Specify the chain specification.
|
||||
takes_value: true
|
||||
- dev:
|
||||
long: dev
|
||||
help: Specify the development chain
|
||||
takes_value: false
|
||||
- base-path:
|
||||
long: base-path
|
||||
short: d
|
||||
value_name: PATH
|
||||
help: Specify custom base path.
|
||||
takes_value: true
|
||||
@@ -50,7 +50,10 @@ extern crate clap;
|
||||
extern crate error_chain;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate structopt;
|
||||
|
||||
mod params;
|
||||
pub mod error;
|
||||
pub mod informant;
|
||||
mod panic_hook;
|
||||
@@ -72,6 +75,8 @@ use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use names::{Generator, Name};
|
||||
use regex::Regex;
|
||||
use structopt::StructOpt;
|
||||
pub use params::{CoreParams, CoreCommands, ExecutionStrategy};
|
||||
|
||||
use futures::Future;
|
||||
|
||||
@@ -90,11 +95,11 @@ pub struct VersionInfo {
|
||||
}
|
||||
|
||||
/// CLI Action
|
||||
pub enum Action<F: ServiceFactory, E: IntoExit> {
|
||||
pub enum Action<E> {
|
||||
/// Substrate handled the command. No need to do anything.
|
||||
ExecutedInternally,
|
||||
/// Service mode requested. Caller should start the service.
|
||||
RunService((FactoryFullConfiguration<F>, E)),
|
||||
RunService(E),
|
||||
}
|
||||
|
||||
/// Something that can be converted into an exit signal.
|
||||
@@ -117,7 +122,7 @@ fn load_spec<F, G>(matches: &clap::ArgMatches, factory: F) -> Result<ChainSpec<G
|
||||
}
|
||||
|
||||
fn base_path(matches: &clap::ArgMatches) -> PathBuf {
|
||||
matches.value_of("base-path")
|
||||
matches.value_of("base_path")
|
||||
.map(|x| Path::new(x).to_owned())
|
||||
.unwrap_or_else(default_base_path)
|
||||
}
|
||||
@@ -145,84 +150,42 @@ fn is_node_name_valid(_name: &str) -> Result<(), &str> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse command line arguments and execute commands or return service configuration.
|
||||
///
|
||||
/// IANA unassigned port ranges that we could use:
|
||||
/// 6717-6766 Unassigned
|
||||
/// 8504-8553 Unassigned
|
||||
/// 9556-9591 Unassigned
|
||||
/// 9803-9874 Unassigned
|
||||
/// 9926-9949 Unassigned
|
||||
pub fn prepare_execution<F, I, T, E, S>(
|
||||
args: I,
|
||||
exit: E,
|
||||
version: VersionInfo,
|
||||
spec_factory: S,
|
||||
impl_name: &'static str,
|
||||
) -> error::Result<Action<F, E>>
|
||||
/// Parse command line arguments
|
||||
pub fn parse_args_default<'a, I, T>(args: I, version: VersionInfo) -> clap::ArgMatches<'a>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<std::ffi::OsString> + Clone,
|
||||
E: IntoExit,
|
||||
F: ServiceFactory,
|
||||
S: FnOnce(&str) -> Result<Option<ChainSpec<FactoryGenesis<F>>>, String>,
|
||||
{
|
||||
panic_hook::set();
|
||||
|
||||
let full_version = service::config::full_version_from_strs(
|
||||
version.version,
|
||||
version.commit
|
||||
);
|
||||
let yaml = format!(include_str!("./cli.yml"),
|
||||
name = version.executable_name,
|
||||
description = version.description,
|
||||
author = version.author,
|
||||
);
|
||||
let yaml = &clap::YamlLoader::load_from_str(&yaml).expect("Invalid yml file")[0];
|
||||
let matches = match clap::App::from_yaml(yaml)
|
||||
|
||||
match CoreParams::clap()
|
||||
.name(version.executable_name)
|
||||
.author(version.author)
|
||||
.about(version.description)
|
||||
.version(&(full_version + "\n")[..])
|
||||
.get_matches_from_safe(args) {
|
||||
Ok(m) => m,
|
||||
Err(e) => e.exit(),
|
||||
};
|
||||
|
||||
// TODO [ToDr] Split parameters parsing from actual execution.
|
||||
let log_pattern = matches.value_of("log").unwrap_or("");
|
||||
init_logger(log_pattern);
|
||||
fdlimit::raise_fd_limit();
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("build-spec") {
|
||||
let spec = load_spec(&matches, spec_factory)?;
|
||||
build_spec::<F>(matches, spec)?;
|
||||
return Ok(Action::ExecutedInternally);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("export-blocks") {
|
||||
let spec = load_spec(&matches, spec_factory)?;
|
||||
export_blocks::<F, _>(matches, spec, exit.into_exit())?;
|
||||
return Ok(Action::ExecutedInternally);
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("import-blocks") {
|
||||
let spec = load_spec(&matches, spec_factory)?;
|
||||
import_blocks::<F, _>(matches, spec, exit.into_exit())?;
|
||||
return Ok(Action::ExecutedInternally);
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("revert") {
|
||||
let spec = load_spec(&matches, spec_factory)?;
|
||||
revert_chain::<F>(matches, spec)?;
|
||||
return Ok(Action::ExecutedInternally);
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("purge-chain") {
|
||||
let spec = load_spec(&matches, spec_factory)?;
|
||||
purge_chain::<F>(matches, spec)?;
|
||||
return Ok(Action::ExecutedInternally);
|
||||
}
|
||||
/// Parse clap::Matches into config and chain specification
|
||||
pub fn parse_matches<'a, F, S>(
|
||||
spec_factory: S,
|
||||
version: VersionInfo,
|
||||
impl_name: &'static str,
|
||||
matches: &clap::ArgMatches<'a>
|
||||
) -> error::Result<(ChainSpec<<F as service::ServiceFactory>::Genesis>, FactoryFullConfiguration<F>)>
|
||||
where
|
||||
F: ServiceFactory,
|
||||
S: FnOnce(&str) -> Result<Option<ChainSpec<FactoryGenesis<F>>>, String>,
|
||||
|
||||
{
|
||||
let spec = load_spec(&matches, spec_factory)?;
|
||||
let mut config = service::Configuration::default_with_spec(spec);
|
||||
let mut config = service::Configuration::default_with_spec(spec.clone());
|
||||
|
||||
config.impl_name = impl_name;
|
||||
config.impl_commit = version.commit;
|
||||
@@ -284,14 +247,14 @@ where
|
||||
config.network.config_path = Some(network_path(&base_path, config.chain_spec.id()).to_string_lossy().into());
|
||||
config.network.net_config_path = config.network.config_path.clone();
|
||||
config.network.reserved_nodes.extend(matches
|
||||
.values_of("reserved-nodes")
|
||||
.values_of("reserved_nodes")
|
||||
.map_or(Default::default(), |v| v.map(|n| n.to_owned()).collect::<Vec<_>>()));
|
||||
if !config.network.reserved_nodes.is_empty() {
|
||||
config.network.non_reserved_mode = NonReservedPeerMode::Deny;
|
||||
}
|
||||
|
||||
config.network.listen_addresses = Vec::new();
|
||||
for addr in matches.values_of("listen-addr").unwrap_or_default() {
|
||||
for addr in matches.values_of("listen_addr").unwrap_or_default() {
|
||||
let addr = addr.parse().map_err(|_| "Invalid listen multiaddress")?;
|
||||
config.network.listen_addresses.push(addr);
|
||||
}
|
||||
@@ -310,17 +273,17 @@ where
|
||||
config.network.public_addresses = Vec::new();
|
||||
|
||||
config.network.client_version = config.client_id();
|
||||
config.network.use_secret = match matches.value_of("node-key").map(H256::from_str) {
|
||||
config.network.use_secret = match matches.value_of("node_key").map(H256::from_str) {
|
||||
Some(Ok(secret)) => Some(secret.into()),
|
||||
Some(Err(err)) => return Err(format!("Error parsing node key: {}", err).into()),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let in_peers = match matches.value_of("in-peers") {
|
||||
let in_peers = match matches.value_of("in_peers") {
|
||||
Some(in_peers) => in_peers.parse().map_err(|_| "Invalid in-peers value specified.")?,
|
||||
None => 25,
|
||||
};
|
||||
let out_peers = match matches.value_of("out-peers") {
|
||||
let out_peers = match matches.value_of("out_peers") {
|
||||
Some(out_peers) => out_peers.parse().map_err(|_| "Invalid out-peers value specified.")?,
|
||||
None => 25,
|
||||
};
|
||||
@@ -334,20 +297,73 @@ where
|
||||
config.keys.push("Alice".into());
|
||||
}
|
||||
|
||||
let rpc_interface: &str = if matches.is_present("rpc-external") { "0.0.0.0" } else { "127.0.0.1" };
|
||||
let ws_interface: &str = if matches.is_present("ws-external") { "0.0.0.0" } else { "127.0.0.1" };
|
||||
let rpc_interface: &str = if matches.is_present("rpc_external") { "0.0.0.0" } else { "127.0.0.1" };
|
||||
let ws_interface: &str = if matches.is_present("ws_external") { "0.0.0.0" } else { "127.0.0.1" };
|
||||
|
||||
config.rpc_http = Some(parse_address(&format!("{}:{}", rpc_interface, 9933), "rpc-port", &matches)?);
|
||||
config.rpc_ws = Some(parse_address(&format!("{}:{}", ws_interface, 9944), "ws-port", &matches)?);
|
||||
config.rpc_http = Some(parse_address(&format!("{}:{}", rpc_interface, 9933), "rpc_port", &matches)?);
|
||||
config.rpc_ws = Some(parse_address(&format!("{}:{}", ws_interface, 9944), "ws_port", &matches)?);
|
||||
|
||||
// Override telemetry
|
||||
if matches.is_present("no-telemetry") {
|
||||
if matches.is_present("no_telemetry") {
|
||||
config.telemetry_url = None;
|
||||
} else if let Some(url) = matches.value_of("telemetry-url") {
|
||||
} else if let Some(url) = matches.value_of("telemetry_url") {
|
||||
config.telemetry_url = Some(url.to_owned());
|
||||
}
|
||||
|
||||
Ok(Action::RunService((config, exit)))
|
||||
Ok((spec, config))
|
||||
}
|
||||
|
||||
//
|
||||
// IANA unassigned port ranges that we could use:
|
||||
// 6717-6766 Unassigned
|
||||
// 8504-8553 Unassigned
|
||||
// 9556-9591 Unassigned
|
||||
// 9803-9874 Unassigned
|
||||
// 9926-9949 Unassigned
|
||||
|
||||
/// execute default commands or return service configuration
|
||||
pub fn execute_default<'a, F, E>(
|
||||
spec: ChainSpec<FactoryGenesis<F>>,
|
||||
exit: E,
|
||||
matches: &clap::ArgMatches<'a>
|
||||
) -> error::Result<Action<E>>
|
||||
where
|
||||
E: IntoExit,
|
||||
F: ServiceFactory,
|
||||
{
|
||||
|
||||
panic_hook::set();
|
||||
|
||||
let log_pattern = matches.value_of("log").unwrap_or("");
|
||||
init_logger(log_pattern);
|
||||
fdlimit::raise_fd_limit();
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("build_spec") {
|
||||
build_spec::<F>(matches, spec)?;
|
||||
return Ok(Action::ExecutedInternally);
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("export_blocks") {
|
||||
export_blocks::<F, _>(matches, spec, exit.into_exit())?;
|
||||
return Ok(Action::ExecutedInternally);
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("import_blocks") {
|
||||
import_blocks::<F, _>(matches, spec, exit.into_exit())?;
|
||||
return Ok(Action::ExecutedInternally);
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("revert") {
|
||||
revert_chain::<F>(matches, spec)?;
|
||||
return Ok(Action::ExecutedInternally);
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("purge_chain") {
|
||||
purge_chain::<F>(matches, spec)?;
|
||||
return Ok(Action::ExecutedInternally);
|
||||
}
|
||||
|
||||
Ok(Action::RunService(exit))
|
||||
}
|
||||
|
||||
fn build_spec<F>(matches: &clap::ArgMatches, spec: ChainSpec<FactoryGenesis<F>>) -> error::Result<()>
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
// Copyright 2018 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::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(name = "Substrate")]
|
||||
pub struct CoreParams {
|
||||
#[structopt(short = "l", long = "log", value_name = "LOG_PATTERN", help = "Sets a custom logging filter")]
|
||||
log: Option<String>,
|
||||
|
||||
#[structopt(long = "base-path", short = "d", value_name = "PATH", help = "Specify custom base path", parse(from_os_str))]
|
||||
base_path: Option<PathBuf>,
|
||||
|
||||
#[structopt(long = "keystore-path", value_name = "PATH", help = "Specify custom keystore path", parse(from_os_str))]
|
||||
keystore_path: Option<PathBuf>,
|
||||
|
||||
#[structopt(long = "key", value_name = "STRING", help = "Specify additional key seed")]
|
||||
key: Option<String>,
|
||||
|
||||
#[structopt(long = "node-key", value_name = "KEY", help = "Specify node secret key (64-character hex string)")]
|
||||
node_key: Option<String>,
|
||||
|
||||
#[structopt(long = "validator",help = "Enable validator mode")]
|
||||
validator: bool,
|
||||
|
||||
#[structopt(long = "light", help = "Run in light client mode")]
|
||||
light: bool,
|
||||
|
||||
#[structopt(long = "dev", help = "Run in development mode; implies --chain=dev --validator --key Alice")]
|
||||
dev: bool,
|
||||
|
||||
#[structopt(long = "listen-addr", value_name = "LISTEN_ADDR", help = "Listen on this multiaddress")]
|
||||
listen_addr: Vec<String>,
|
||||
|
||||
#[structopt(long = "port", value_name = "PORT", help = "Specify p2p protocol TCP port. Only used if --listen-addr is not specified.")]
|
||||
port: Option<u32>,
|
||||
|
||||
#[structopt(long = "rpc-external", help = "Listen to all RPC interfaces (default is local)")]
|
||||
rpc_external: bool,
|
||||
|
||||
#[structopt(long = "ws-external", help = "Listen to all Websocket interfaces (default is local)")]
|
||||
ws_external: bool,
|
||||
|
||||
#[structopt(long = "rpc-port", value_name = "PORT", help = "Specify HTTP RPC server TCP port")]
|
||||
rpc_port: Option<u32>,
|
||||
|
||||
#[structopt(long = "ws-port", value_name = "PORT", help = "Specify WebSockets RPC server TCP port")]
|
||||
ws_port: Option<u32>,
|
||||
|
||||
#[structopt(long = "bootnodes", value_name = "URL", help = "Specify a list of bootnodes")]
|
||||
bootnodes: Vec<String>,
|
||||
|
||||
#[structopt(long = "reserved-nodes", value_name = "URL", help = "Specify a list of reserved node addresses")]
|
||||
reserved_nodes: Vec<String>,
|
||||
|
||||
#[structopt(long = "out-peers", value_name = "OUT_PEERS", help = "Specify the number of outgoing connections we're trying to maintain")]
|
||||
out_peers: Option<u8>,
|
||||
|
||||
#[structopt(long = "in-peers", value_name = "IN_PEERS", help = "Specify the maximum number of incoming connections we're accepting")]
|
||||
in_peers: Option<u8>,
|
||||
|
||||
#[structopt(long = "chain", value_name = "CHAIN_SPEC", help = "Specify the chain specification (one of dev, local or staging)")]
|
||||
chain: Option<String>,
|
||||
|
||||
#[structopt(long = "pruning", value_name = "PRUNING_MODE", help = "Specify the pruning mode, a number of blocks to keep or 'archive'. Default is 256.")]
|
||||
pruning: Option<u32>,
|
||||
|
||||
#[structopt(long = "name", value_name = "NAME", help = "The human-readable name for this node, as reported to the telemetry server, if enabled")]
|
||||
name: Option<String>,
|
||||
|
||||
#[structopt(short = "t", long = "telemetry", help = "Should connect to the Substrate telemetry server (telemetry is off by default on local chains)")]
|
||||
telemetry: bool,
|
||||
|
||||
#[structopt(long = "no-telemetry", help = "Should not connect to the Substrate telemetry server (telemetry is on by default on global chains)")]
|
||||
no_telemetry: bool,
|
||||
|
||||
#[structopt(long = "telemetry-url", value_name = "TELEMETRY_URL", help = "The URL of the telemetry server. Implies --telemetry")]
|
||||
telemetry_url: Option<String>,
|
||||
|
||||
#[structopt(long = "execution", value_name = "STRATEGY", help = "The means of execution used when calling into the runtime. Can be either wasm, native or both.")]
|
||||
execution: Option<ExecutionStrategy>,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
cmds: Option<CoreCommands>,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum ExecutionStrategy {
|
||||
Native,
|
||||
Wasm,
|
||||
Both,
|
||||
}
|
||||
|
||||
impl Default for ExecutionStrategy {
|
||||
fn default() -> Self {
|
||||
ExecutionStrategy::Both
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for ExecutionStrategy {
|
||||
type Err = String;
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
match input {
|
||||
"native" => Ok(ExecutionStrategy::Native),
|
||||
"wasm" | "webassembly" => Ok(ExecutionStrategy::Wasm),
|
||||
"both" => Ok(ExecutionStrategy::Both),
|
||||
_ => Err("Please specify either 'native', 'wasm' or 'both".to_owned())
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum CoreCommands {
|
||||
#[structopt(name = "build-spec", about = "Build a spec.json file, outputing to stdout")]
|
||||
BuildSpec {
|
||||
#[structopt(long = "raw", help = "Force raw genesis storage output.")]
|
||||
raw: bool,
|
||||
|
||||
#[structopt(long = "chain", value_name = "CHAIN_SPEC", help = "Specify the chain specification (one of dev, local or staging)")]
|
||||
chain: Option<String>,
|
||||
|
||||
#[structopt(long = "dev", help = "Specify the development chain")]
|
||||
dev: bool,
|
||||
},
|
||||
|
||||
#[structopt(name = "export-blocks", about = "Export blocks to a file")]
|
||||
ExportBlocks {
|
||||
#[structopt(help = "Output file name or stdout if unspecified.", parse(from_os_str))]
|
||||
OUTPUT: Option<PathBuf>,
|
||||
|
||||
#[structopt(long = "chain", value_name = "CHAIN_SPEC", help = "Specify the chain specification.")]
|
||||
chain: Option<String>,
|
||||
|
||||
#[structopt(long = "dev", help = "Specify the development chain")]
|
||||
dev: bool,
|
||||
|
||||
#[structopt(long = "base-path", short = "d", value_name = "PATH", help = "Specify custom base path.")]
|
||||
base_path: Option<String>,
|
||||
|
||||
#[structopt(long = "from", value_name = "BLOCK", help = "Specify starting block number. 1 by default.")]
|
||||
from: Option<u128>,
|
||||
|
||||
#[structopt(long = "to", value_name = "BLOCK", help = "Specify last block number. Best block by default.")]
|
||||
to: Option<u128>,
|
||||
|
||||
#[structopt(long = "json", help = "Use JSON output rather than binary.")]
|
||||
json: bool,
|
||||
},
|
||||
|
||||
#[structopt(name = "import-blocks", about = "Import blocks from file.")]
|
||||
ImportBlocks {
|
||||
#[structopt(help = "Input file or stdin if unspecified.", parse(from_os_str))]
|
||||
INPUT: Option<PathBuf>,
|
||||
|
||||
#[structopt(long = "chain", value_name = "CHAIN_SPEC", help = "Specify the chain specification.")]
|
||||
chain: Option<String>,
|
||||
|
||||
#[structopt(long = "dev", help = "Specify the development chain")]
|
||||
dev: bool,
|
||||
|
||||
#[structopt(long = "base-path", short = "d", value_name = "PATH", help = "Specify custom base path.", parse(from_os_str))]
|
||||
base_path: Option<PathBuf>,
|
||||
|
||||
#[structopt(long = "execution", value_name = "STRATEGY", help = "The means of execution used when executing blocks. Can be either wasm, native or both.")]
|
||||
execution: ExecutionStrategy,
|
||||
|
||||
#[structopt(long = "api-execution", value_name = "STRATEGY", help = "The means of execution used when calling into the runtime. Can be either wasm, native or both.")]
|
||||
api_execution: ExecutionStrategy,
|
||||
|
||||
#[structopt(long = "max-heap-pages", value_name = "COUNT", help = "The maximum number of 64KB pages to ever allocate for Wasm execution. Don't alter this unless you know what you're doing.")]
|
||||
max_heap_pages: Option<u32>,
|
||||
},
|
||||
|
||||
#[structopt(name = "revert", about = "Revert chain to the previous state")]
|
||||
Revert {
|
||||
#[structopt(help = "Number of blocks to revert. Default is 256.")]
|
||||
NUM: Option<u32>,
|
||||
|
||||
#[structopt(long = "chain", value_name = "CHAIN_SPEC", help = "Specify the chain specification.")]
|
||||
chain: Option<String>,
|
||||
|
||||
#[structopt(long = "dev", help = "Specify the development chain")]
|
||||
dev: bool,
|
||||
|
||||
#[structopt(long = "base-path", short = "d", value_name = "PATH", help = "Specify custom base path.", parse(from_os_str))]
|
||||
base_path: Option<PathBuf>,
|
||||
},
|
||||
|
||||
#[structopt(name = "purge-chain", about = "Remove the whole chain data.")]
|
||||
PurgeChain {
|
||||
#[structopt(long = "chain", value_name = "CHAIN_SPEC", help = "Specify the chain specification.")]
|
||||
chain: Option<String>,
|
||||
|
||||
#[structopt(long = "dev", help = "Specify the development chain")]
|
||||
dev: bool,
|
||||
|
||||
#[structopt(long = "base-path", short = "d", value_name = "PATH", help = "Specify custom base path.", parse(from_os_str))]
|
||||
base_path: Option<PathBuf>
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,25 @@
|
||||
// Copyright 2017-2018 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/>.
|
||||
|
||||
//! Block import helpers.
|
||||
|
||||
use primitives::AuthorityId;
|
||||
use runtime_primitives::traits::{Block as BlockT, DigestItemFor};
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, DigestItemFor};
|
||||
use runtime_primitives::Justification;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Block import result.
|
||||
#[derive(Debug)]
|
||||
@@ -89,6 +107,24 @@ impl<Block: BlockT> ImportBlock<Block> {
|
||||
self.auxiliary,
|
||||
)
|
||||
}
|
||||
|
||||
/// Get a handle to full header (with post-digests applied).
|
||||
pub fn post_header(&self) -> Cow<Block::Header> {
|
||||
use runtime_primitives::traits::Digest;
|
||||
|
||||
if self.post_digests.is_empty() {
|
||||
Cow::Borrowed(&self.header)
|
||||
} else {
|
||||
Cow::Owned({
|
||||
let mut hdr = self.header.clone();
|
||||
for digest_item in &self.post_digests {
|
||||
hdr.digest_mut().push(digest_item.clone());
|
||||
}
|
||||
|
||||
hdr
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,4 +137,4 @@ pub trait BlockImport<B: BlockT> {
|
||||
block: ImportBlock<B>,
|
||||
new_authorities: Option<Vec<AuthorityId>>
|
||||
) -> Result<ImportResult, Self::Error>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.17"
|
||||
futures = "0.1"
|
||||
parity-codec = "2.1"
|
||||
parity-codec-derive = "2.0"
|
||||
sr-primitives = { path = "../sr-primitives" }
|
||||
substrate-consensus-common = { path = "../consensus/common" }
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
substrate-client = { path = "../client" }
|
||||
substrate-network = { path = "../network" }
|
||||
substrate-service = { path = "../service", optional = true }
|
||||
log = "0.4"
|
||||
parking_lot = "0.4"
|
||||
tokio = "0.1.7"
|
||||
@@ -25,3 +27,7 @@ substrate-network = { path = "../network", features = ["test-helpers"] }
|
||||
substrate-keyring = { path = "../keyring" }
|
||||
substrate-test-client = { path = "../test-client"}
|
||||
env_logger = "0.5"
|
||||
|
||||
[features]
|
||||
default = ["service-integration"]
|
||||
service-integration = ["substrate-service"]
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
//! Primitives for GRANDPA integration, suitable for WASM compilation.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate alloc;
|
||||
|
||||
extern crate substrate_primitives;
|
||||
extern crate sr_primitives;
|
||||
@@ -57,6 +61,14 @@ pub mod id {
|
||||
pub const GRANDPA_API: ApiId = *b"fgrandpa";
|
||||
}
|
||||
|
||||
/// Well-known storage keys for GRANDPA.
|
||||
pub mod well_known_keys {
|
||||
/// The key for the authorities and weights vector in storage.
|
||||
pub const AUTHORITY_PREFIX: &[u8] = b":grandpa:auth:";
|
||||
/// The key for the authorities count.
|
||||
pub const AUTHORITY_COUNT: &[u8] = b":grandpa:auth:len";
|
||||
}
|
||||
|
||||
decl_runtime_apis! {
|
||||
/// APIs for integrating the GRANDPA finality gadget into runtimes.
|
||||
/// This should be implemented on the runtime side.
|
||||
@@ -76,6 +88,10 @@ decl_runtime_apis! {
|
||||
///
|
||||
/// No change should be scheduled if one is already and the delay has not
|
||||
/// passed completely.
|
||||
///
|
||||
/// This should be a pure function: i.e. as long as the runtime can interpret
|
||||
/// the digest type it should return the same result regardless of the current
|
||||
/// state.
|
||||
fn grandpa_pending_change(digest: DigestFor<Block>)
|
||||
-> Option<ScheduledChange<NumberFor<Block>>>;
|
||||
|
||||
|
||||
@@ -50,7 +50,9 @@ impl<H, N> SharedAuthoritySet<H, N> {
|
||||
}
|
||||
|
||||
impl<H: Eq, N> SharedAuthoritySet<H, N>
|
||||
where N: Add<Output=N> + Ord + Clone + Debug
|
||||
where
|
||||
N: Add<Output=N> + Ord + Clone + Debug,
|
||||
H: Debug
|
||||
{
|
||||
/// Get the earliest limit-block number, if any.
|
||||
pub(crate) fn current_limit(&self) -> Option<N> {
|
||||
@@ -103,7 +105,9 @@ impl<H, N> AuthoritySet<H, N> {
|
||||
}
|
||||
|
||||
impl<H: Eq, N> AuthoritySet<H, N>
|
||||
where N: Add<Output=N> + Ord + Clone + Debug,
|
||||
where
|
||||
N: Add<Output=N> + Ord + Clone + Debug,
|
||||
H: Debug
|
||||
{
|
||||
/// Note an upcoming pending transition.
|
||||
pub(crate) fn add_pending_change(&mut self, pending: PendingChange<H, N>) {
|
||||
@@ -152,7 +156,11 @@ impl<H: Eq, N> AuthoritySet<H, N>
|
||||
|
||||
// check if the block that signalled the change is canonical in
|
||||
// our chain.
|
||||
if canonical(change.canon_height.clone())? == change.canon_hash {
|
||||
let canonical_at_height = canonical(change.canon_height.clone())?;
|
||||
debug!(target: "afg", "Evaluating potential set change at block {:?}. Our canonical hash is {:?}",
|
||||
(&change.canon_height, &change.canon_hash), canonical_at_height);
|
||||
|
||||
if canonical_at_height == change.canon_hash {
|
||||
// apply this change: make the set canonical
|
||||
info!(target: "finality", "Applying authority set change scheduled at block #{:?}",
|
||||
change.canon_height);
|
||||
|
||||
@@ -55,6 +55,7 @@ extern crate futures;
|
||||
extern crate substrate_client as client;
|
||||
extern crate sr_primitives as runtime_primitives;
|
||||
extern crate substrate_consensus_common as consensus_common;
|
||||
extern crate substrate_network as network;
|
||||
extern crate substrate_primitives;
|
||||
extern crate tokio;
|
||||
extern crate parking_lot;
|
||||
@@ -64,8 +65,8 @@ extern crate substrate_finality_grandpa_primitives as fg_primitives;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_network as network;
|
||||
#[cfg(feature="service-integration")]
|
||||
extern crate substrate_service as service;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_keyring as keyring;
|
||||
@@ -86,7 +87,7 @@ use client::{Client, error::Error as ClientError, ImportNotifications, backend::
|
||||
use client::blockchain::HeaderBackend;
|
||||
use client::runtime_api::TaggedTransactionQueue;
|
||||
use codec::{Encode, Decode};
|
||||
use consensus_common::{BlockImport, ImportBlock, ImportResult};
|
||||
use consensus_common::{BlockImport, ImportBlock, ImportResult, Authorities};
|
||||
use runtime_primitives::traits::{
|
||||
NumberFor, Block as BlockT, Header as HeaderT, DigestFor, ProvideRuntimeApi
|
||||
};
|
||||
@@ -98,6 +99,8 @@ use tokio::timer::Interval;
|
||||
use grandpa::Error as GrandpaError;
|
||||
use grandpa::{voter, round::State as RoundState, Equivocation, BlockNumberOps};
|
||||
|
||||
use network::{Service as NetworkService, ExHashT};
|
||||
use network::consensus_gossip::{ConsensusMessage};
|
||||
use std::collections::{VecDeque, HashMap};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Instant, Duration};
|
||||
@@ -108,6 +111,11 @@ pub use fg_primitives::ScheduledChange;
|
||||
|
||||
mod authorities;
|
||||
|
||||
#[cfg(feature="service-integration")]
|
||||
mod service_integration;
|
||||
#[cfg(feature="service-integration")]
|
||||
pub use service_integration::{LinkHalfForService, BlockImportForService};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@@ -173,7 +181,7 @@ impl From<GrandpaError> for Error {
|
||||
/// handle to a gossip service or similar.
|
||||
///
|
||||
/// Intended to be a lightweight handle such as an `Arc`.
|
||||
pub trait Network: Clone {
|
||||
pub trait Network : Clone {
|
||||
/// A stream of input messages for a topic.
|
||||
type In: Stream<Item=Vec<u8>,Error=()>;
|
||||
|
||||
@@ -188,6 +196,52 @@ pub trait Network: Clone {
|
||||
fn drop_messages(&self, round: u64, set_id: u64);
|
||||
}
|
||||
|
||||
/// Bridge between NetworkService, gossiping consensus messages and Grandpa
|
||||
pub struct NetworkBridge<B: BlockT, S: network::specialization::NetworkSpecialization<B>, H: ExHashT> {
|
||||
service: Arc<NetworkService<B, S, H>>
|
||||
}
|
||||
|
||||
impl<B: BlockT, S: network::specialization::NetworkSpecialization<B>, H: ExHashT> NetworkBridge<B, S, H> {
|
||||
/// Create a new NetworkBridge to the given NetworkService
|
||||
pub fn new(service: Arc<NetworkService<B, S, H>>) -> Self {
|
||||
NetworkBridge { service }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<B: BlockT, S: network::specialization::NetworkSpecialization<B>, H: ExHashT> Clone for NetworkBridge<B, S, H> {
|
||||
fn clone(&self) -> Self {
|
||||
NetworkBridge {
|
||||
service: Arc::clone(&self.service)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn message_topic<B: BlockT>(round: u64, set_id: u64) -> B::Hash {
|
||||
use runtime_primitives::traits::Hash as HashT;
|
||||
<<B::Header as HeaderT>::Hashing as HashT>::hash(format!("{}-{}", set_id, round).as_bytes())
|
||||
}
|
||||
|
||||
impl<B: BlockT, S: network::specialization::NetworkSpecialization<B>, H: ExHashT> Network for NetworkBridge<B, S, H> {
|
||||
type In = mpsc::UnboundedReceiver<ConsensusMessage>;
|
||||
fn messages_for(&self, round: u64, set_id: u64) -> Self::In {
|
||||
self.service.consensus_gossip().write().messages_for(message_topic::<B>(round, set_id))
|
||||
}
|
||||
|
||||
fn send_message(&self, round: u64, set_id: u64, message: Vec<u8>) {
|
||||
let topic = message_topic::<B>(round, set_id);
|
||||
let gossip = self.service.consensus_gossip();
|
||||
self.service.with_spec(move |_s, context|{
|
||||
gossip.write().multicast(context, topic, message);
|
||||
});
|
||||
}
|
||||
|
||||
fn drop_messages(&self, round: u64, set_id: u64) {
|
||||
let topic = message_topic::<B>(round, set_id);
|
||||
self.service.consensus_gossip().write().collect_garbage(|t| t == &topic);
|
||||
}
|
||||
}
|
||||
|
||||
/// Something which can determine if a block is known.
|
||||
pub trait BlockStatus<Block: BlockT> {
|
||||
/// Return `Ok(Some(number))` or `Ok(None)` depending on whether the block
|
||||
@@ -523,7 +577,7 @@ impl<Block: BlockT<Hash=H256>, B, E, N, RA> grandpa::Chain<Block::Hash, NumberFo
|
||||
// once blocks are finalized that make that transition irrelevant or activate it,
|
||||
// we will proceed onwards. most of the time there will be no pending transition.
|
||||
let limit = self.authority_set.current_limit();
|
||||
trace!(target: "afg", "Finding best chain containing block {:?} with number limit {:?}", block, limit);
|
||||
debug!(target: "afg", "Finding best chain containing block {:?} with number limit {:?}", block, limit);
|
||||
|
||||
match self.inner.best_containing(block, limit) {
|
||||
Ok(Some(hash)) => {
|
||||
@@ -583,22 +637,22 @@ impl<B, E, Block: BlockT<Hash=H256>, N, RA> voter::Environment<Block::Hash, Numb
|
||||
Block: 'static,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Send + Sync,
|
||||
N: Network + 'static,
|
||||
N::In: 'static,
|
||||
N: Network + 'static + Send,
|
||||
N::In: 'static + Send,
|
||||
RA: 'static + Send + Sync,
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
{
|
||||
type Timer = Box<Future<Item = (), Error = Self::Error>>;
|
||||
type Timer = Box<dyn Future<Item = (), Error = Self::Error> + Send>;
|
||||
type Id = AuthorityId;
|
||||
type Signature = ed25519::Signature;
|
||||
type In = Box<Stream<
|
||||
type In = Box<dyn Stream<
|
||||
Item = ::grandpa::SignedMessage<Block::Hash, NumberFor<Block>, Self::Signature, Self::Id>,
|
||||
Error = Self::Error,
|
||||
>>;
|
||||
type Out = Box<Sink<
|
||||
> + Send>;
|
||||
type Out = Box<dyn Sink<
|
||||
SinkItem = ::grandpa::Message<Block::Hash, NumberFor<Block>>,
|
||||
SinkError = Self::Error,
|
||||
>>;
|
||||
> + Send>;
|
||||
type Error = ExitOrError<Block::Hash, NumberFor<Block>>;
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
@@ -689,6 +743,8 @@ impl<B, E, Block: BlockT<Hash=H256>, N, RA> voter::Environment<Block::Hash, Numb
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
debug!(target: "afg", "Finalizing blocks up to ({:?}, {})", number, hash);
|
||||
|
||||
// lock must be held through writing to DB to avoid race
|
||||
let mut authority_set = self.authority_set.inner().write();
|
||||
let client = &self.inner;
|
||||
@@ -768,8 +824,6 @@ impl<B, E, Block: BlockT<Hash=H256>, N, RA> voter::Environment<Block::Hash, Numb
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// A block-import handler for GRANDPA.
|
||||
///
|
||||
/// This scans each imported block for signals of changing authority set.
|
||||
@@ -806,7 +860,7 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> BlockImport<Block>
|
||||
// until the block is written to prevent a race if we need to restore
|
||||
// the old authority set on error.
|
||||
let just_in_case = maybe_change.map(|change| {
|
||||
let hash = block.header.hash();
|
||||
let hash = block.post_header().hash();
|
||||
let number = block.header.number().clone();
|
||||
|
||||
let mut authorities = self.authority_set.inner().write();
|
||||
@@ -834,12 +888,38 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> BlockImport<Block>
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> Authorities<Block> for GrandpaBlockImport<B, E, Block, RA, PRA>
|
||||
where
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
RA: TaggedTransactionQueue<Block>, // necessary for client to import `BlockImport`.
|
||||
{
|
||||
|
||||
type Error = <Client<B, E, Block, RA> as Authorities<Block>>::Error;
|
||||
fn authorities(&self, at: &BlockId<Block>) -> Result<Vec<AuthorityId>, Self::Error> {
|
||||
self.inner.authorities_at(at)
|
||||
}
|
||||
}
|
||||
|
||||
/// Half of a link between a block-import worker and a the background voter.
|
||||
// This should remain non-clone.
|
||||
pub struct LinkHalf<B, E, Block: BlockT<Hash=H256>, RA> {
|
||||
client: Arc<Client<B, E, Block, RA>>,
|
||||
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
}
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA> Clone for LinkHalf<B, E, Block, RA>
|
||||
where
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
RA: TaggedTransactionQueue<Block>, // necessary for client to import `BlockImport`.
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
LinkHalf {
|
||||
client: self.client.clone(),
|
||||
authority_set: self.authority_set.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Make block importer and link half necessary to tie the background voter
|
||||
/// to it.
|
||||
@@ -895,12 +975,12 @@ pub fn run_grandpa<B, E, Block: BlockT<Hash=H256>, N, RA>(
|
||||
config: Config,
|
||||
link: LinkHalf<B, E, Block, RA>,
|
||||
network: N,
|
||||
) -> ::client::error::Result<impl Future<Item=(),Error=()>> where
|
||||
) -> ::client::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
|
||||
Block::Hash: Ord,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
N: Network + 'static,
|
||||
N::In: 'static,
|
||||
N: Network + Send + Sync + 'static,
|
||||
N::In: Send + 'static,
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
DigestFor<Block>: Encode,
|
||||
RA: Send + Sync + 'static,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2018 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/>.
|
||||
|
||||
/// Integrate grandpa finality with substrate service
|
||||
|
||||
use client;
|
||||
use service::{FullBackend, FullExecutor, ServiceFactory};
|
||||
|
||||
pub type BlockImportForService<F> = ::GrandpaBlockImport<
|
||||
FullBackend<F>,
|
||||
FullExecutor<F>,
|
||||
<F as ServiceFactory>::Block,
|
||||
<F as ServiceFactory>::RuntimeApi,
|
||||
client::Client<
|
||||
FullBackend<F>,
|
||||
FullExecutor<F>,
|
||||
<F as ServiceFactory>::Block,
|
||||
<F as ServiceFactory>::RuntimeApi
|
||||
>,
|
||||
>;
|
||||
|
||||
pub type LinkHalfForService<F> = ::LinkHalf<
|
||||
FullBackend<F>,
|
||||
FullExecutor<F>,
|
||||
<F as ServiceFactory>::Block,
|
||||
<F as ServiceFactory>::RuntimeApi
|
||||
>;
|
||||
@@ -158,11 +158,15 @@ fn make_topic(round: u64, set_id: u64) -> Hash {
|
||||
}
|
||||
|
||||
impl Network for MessageRouting {
|
||||
type In = Box<Stream<Item=Vec<u8>,Error=()>>;
|
||||
type In = Box<Stream<Item=Vec<u8>,Error=()> + Send>;
|
||||
|
||||
fn messages_for(&self, round: u64, set_id: u64) -> Self::In {
|
||||
let messages = self.inner.lock().peer(self.peer_id)
|
||||
.with_spec(|spec, _| spec.gossip.messages_for(make_topic(round, set_id)));
|
||||
let inner = self.inner.lock();
|
||||
let peer = inner.peer(self.peer_id);
|
||||
let mut gossip = peer.consensus_gossip().write();
|
||||
let messages = peer.with_spec(move |_, _| {
|
||||
gossip.messages_for(make_topic(round, set_id))
|
||||
});
|
||||
|
||||
let messages = messages.map_err(
|
||||
move |_| panic!("Messages for round {} dropped too early", round)
|
||||
@@ -179,8 +183,12 @@ impl Network for MessageRouting {
|
||||
|
||||
fn drop_messages(&self, round: u64, set_id: u64) {
|
||||
let topic = make_topic(round, set_id);
|
||||
self.inner.lock().peer(self.peer_id)
|
||||
.with_spec(|spec, _| spec.gossip.collect_garbage(|t| t == &topic));
|
||||
let inner = self.inner.lock();
|
||||
let peer = inner.peer(self.peer_id);
|
||||
let mut gossip = peer.consensus_gossip().write();
|
||||
peer.with_spec(move |_, _| {
|
||||
gossip.collect_garbage(|t| t == &topic)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +326,8 @@ fn finalize_3_voters_no_observers() {
|
||||
.take_while(|n| Ok(n.header.number() < &20))
|
||||
.for_each(|_| Ok(()))
|
||||
);
|
||||
fn assert_send<T: Send>(_: &T) { }
|
||||
|
||||
let voter = run_grandpa(
|
||||
Config {
|
||||
gossip_duration: TEST_GOSSIP_DURATION,
|
||||
@@ -328,6 +338,8 @@ fn finalize_3_voters_no_observers() {
|
||||
MessageRouting::new(net.clone(), peer_id),
|
||||
).expect("all in order with client and network");
|
||||
|
||||
assert_send(&voter);
|
||||
|
||||
runtime.spawn(voter);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,14 +24,11 @@ use rand::{self, Rng};
|
||||
use network_libp2p::NodeIndex;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Hash, HashFor};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use message::generic::{Message, ConsensusMessage};
|
||||
pub use message::generic::{Message, ConsensusMessage};
|
||||
use protocol::Context;
|
||||
use config::Roles;
|
||||
use specialization::NetworkSpecialization;
|
||||
use StatusMessage;
|
||||
use generic_message;
|
||||
|
||||
// TODO: Add additional spam/DoS attack protection.
|
||||
// FIXME: Add additional spam/DoS attack protection: https://github.com/paritytech/substrate/issues/1115
|
||||
const MESSAGE_LIFETIME: Duration = Duration::from_secs(600);
|
||||
|
||||
struct PeerConsensus<H> {
|
||||
@@ -55,10 +52,7 @@ pub struct ConsensusGossip<B: BlockT> {
|
||||
session_start: Option<B::Hash>,
|
||||
}
|
||||
|
||||
impl<B: BlockT> ConsensusGossip<B>
|
||||
where
|
||||
B::Header: HeaderT<Number=u64>
|
||||
{
|
||||
impl<B: BlockT> ConsensusGossip<B> {
|
||||
/// Create a new instance.
|
||||
pub fn new() -> Self {
|
||||
ConsensusGossip {
|
||||
@@ -262,52 +256,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> NetworkSpecialization<Block> for ConsensusGossip<Block> where
|
||||
Block::Header: HeaderT<Number=u64>
|
||||
{
|
||||
fn status(&self) -> Vec<u8> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn on_connect(&mut self, ctx: &mut Context<Block>, who: NodeIndex, status: StatusMessage<Block>) {
|
||||
self.new_peer(ctx, who, status.roles);
|
||||
}
|
||||
|
||||
fn on_disconnect(&mut self, ctx: &mut Context<Block>, who: NodeIndex) {
|
||||
self.peer_disconnected(ctx, who);
|
||||
}
|
||||
|
||||
fn on_message(
|
||||
&mut self,
|
||||
ctx: &mut Context<Block>,
|
||||
who: NodeIndex,
|
||||
message: &mut Option<::message::Message<Block>>
|
||||
) {
|
||||
match message.take() {
|
||||
Some(generic_message::Message::Consensus(topic, msg)) => {
|
||||
trace!(target: "gossip", "Consensus message from {}: {:?}", who, msg);
|
||||
self.on_incoming(ctx, who, topic, msg);
|
||||
}
|
||||
r => *message = r,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_abort(&mut self) {
|
||||
self.abort();
|
||||
}
|
||||
|
||||
fn maintain_peers(&mut self, _ctx: &mut Context<Block>) {
|
||||
self.collect_garbage(|_| true);
|
||||
}
|
||||
|
||||
fn on_block_imported(
|
||||
&mut self,
|
||||
_ctx: &mut Context<Block>,
|
||||
_hash: <Block as BlockT>::Hash,
|
||||
_header: &<Block as BlockT>::Header)
|
||||
{}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use runtime_primitives::testing::{H256, Block as RawBlock, ExtrinsicWrapper};
|
||||
|
||||
@@ -65,7 +65,7 @@ pub mod specialization;
|
||||
pub mod test;
|
||||
|
||||
pub use chain::Client as ClientHandle;
|
||||
pub use service::{Service, FetchFuture, TransactionPool, ManageNetwork, SyncProvider};
|
||||
pub use service::{Service, FetchFuture, TransactionPool, ManageNetwork, SyncProvider, ExHashT};
|
||||
pub use protocol::{ProtocolStatus, PeerInfo, Context};
|
||||
pub use sync::{Status as SyncStatus, SyncState};
|
||||
pub use network_libp2p::{NodeIndex, ProtocolId, Severity, Protocol};
|
||||
|
||||
@@ -27,6 +27,7 @@ use codec::{Encode, Decode};
|
||||
|
||||
use message::{self, Message};
|
||||
use message::generic::Message as GenericMessage;
|
||||
use consensus_gossip::ConsensusGossip;
|
||||
use specialization::NetworkSpecialization;
|
||||
use sync::{ChainSync, Status as SyncStatus, SyncState};
|
||||
use service::{TransactionPool, ExHashT};
|
||||
@@ -57,6 +58,7 @@ pub struct Protocol<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> {
|
||||
genesis_hash: B::Hash,
|
||||
sync: Arc<RwLock<ChainSync<B>>>,
|
||||
specialization: RwLock<S>,
|
||||
consensus_gossip: RwLock<ConsensusGossip<B>>,
|
||||
context_data: ContextData<B, H>,
|
||||
// Connected peers pending Status message.
|
||||
handshaking_peers: RwLock<HashMap<NodeIndex, time::Instant>>,
|
||||
@@ -207,6 +209,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
|
||||
genesis_hash: info.chain.genesis_hash,
|
||||
sync: Arc::new(RwLock::new(sync)),
|
||||
specialization: RwLock::new(specialization),
|
||||
consensus_gossip: RwLock::new(ConsensusGossip::new()),
|
||||
handshaking_peers: RwLock::new(HashMap::new()),
|
||||
transaction_pool: transaction_pool,
|
||||
};
|
||||
@@ -221,6 +224,11 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
|
||||
&self.sync
|
||||
}
|
||||
|
||||
|
||||
pub(crate) fn consensus_gossip<'a>(&'a self) -> &'a RwLock<ConsensusGossip<B>> {
|
||||
&self.consensus_gossip
|
||||
}
|
||||
|
||||
/// Returns protocol status
|
||||
pub fn status(&self) -> ProtocolStatus<B> {
|
||||
let sync = self.sync.read();
|
||||
@@ -278,6 +286,9 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
|
||||
GenericMessage::RemoteHeaderResponse(response) => self.on_remote_header_response(io, who, response),
|
||||
GenericMessage::RemoteChangesRequest(request) => self.on_remote_changes_request(io, who, request),
|
||||
GenericMessage::RemoteChangesResponse(response) => self.on_remote_changes_response(io, who, response),
|
||||
GenericMessage::Consensus(topic, msg) => {
|
||||
self.consensus_gossip.write().on_incoming(&mut ProtocolContext::new(&self.context_data, io), who, topic, msg);
|
||||
},
|
||||
other => self.specialization.write().on_message(&mut ProtocolContext::new(&self.context_data, io), who, &mut Some(other)),
|
||||
}
|
||||
}
|
||||
@@ -297,6 +308,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
|
||||
pub fn on_peer_disconnected(&self, io: &mut SyncIo, peer: NodeIndex) {
|
||||
trace!(target: "sync", "Disconnecting {}: {}", peer, io.peer_debug_info(peer));
|
||||
|
||||
|
||||
// lock all the the peer lists so that add/remove peer events are in order
|
||||
let mut sync = self.sync.write();
|
||||
let mut spec = self.specialization.write();
|
||||
@@ -309,6 +321,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
|
||||
};
|
||||
if removed {
|
||||
let mut context = ProtocolContext::new(&self.context_data, io);
|
||||
self.consensus_gossip.write().peer_disconnected(&mut context, peer);
|
||||
sync.peer_disconnected(&mut context, peer);
|
||||
spec.on_disconnect(&mut context, peer);
|
||||
self.on_demand.as_ref().map(|s| s.on_disconnect(peer));
|
||||
@@ -391,6 +404,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
|
||||
|
||||
/// Perform time based maintenance.
|
||||
pub fn tick(&self, io: &mut SyncIo) {
|
||||
self.consensus_gossip.write().collect_garbage(|_| true);
|
||||
self.maintain_peers(io);
|
||||
self.on_demand.as_ref().map(|s| s.maintain_peers(io));
|
||||
}
|
||||
@@ -478,6 +492,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
|
||||
let mut context = ProtocolContext::new(&self.context_data, io);
|
||||
self.on_demand.as_ref().map(|s| s.on_connect(who, status.roles, status.best_number));
|
||||
self.sync.write().new_peer(&mut context, who);
|
||||
self.consensus_gossip.write().new_peer(&mut context, who, status.roles);
|
||||
self.specialization.write().on_connect(&mut context, who, status);
|
||||
}
|
||||
|
||||
@@ -555,10 +570,12 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
|
||||
let mut spec = self.specialization.write();
|
||||
let mut peers = self.context_data.peers.write();
|
||||
let mut handshaking_peers = self.handshaking_peers.write();
|
||||
let mut consensus_gossip = self.consensus_gossip.write();
|
||||
sync.clear();
|
||||
spec.on_abort();
|
||||
peers.clear();
|
||||
handshaking_peers.clear();
|
||||
consensus_gossip.abort();
|
||||
}
|
||||
|
||||
pub fn stop(&self) {
|
||||
|
||||
@@ -19,11 +19,12 @@ use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
use std::time::Duration;
|
||||
use futures::{self, Future, Stream, stream, sync::oneshot};
|
||||
use parking_lot::Mutex;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use network_libp2p::{ProtocolId, PeerId, NetworkConfiguration, ErrorKind};
|
||||
use network_libp2p::{start_service, Service as NetworkService, ServiceEvent as NetworkServiceEvent};
|
||||
use network_libp2p::{RegisteredProtocol, parse_str_addr, Protocol as Libp2pProtocol};
|
||||
use io::NetSyncIo;
|
||||
use consensus_gossip::ConsensusGossip;
|
||||
use protocol::{self, Protocol, ProtocolContext, Context, ProtocolStatus};
|
||||
use config::Params;
|
||||
use error::Error;
|
||||
@@ -44,6 +45,7 @@ pub trait SyncProvider<B: BlockT>: Send + Sync {
|
||||
fn status(&self) -> ProtocolStatus<B>;
|
||||
}
|
||||
|
||||
/// Minimum Requirements for a Hash within Networking
|
||||
pub trait ExHashT: ::std::hash::Hash + Eq + ::std::fmt::Debug + Clone + Send + Sync + 'static {}
|
||||
impl<T> ExHashT for T where T: ::std::hash::Hash + Eq + ::std::fmt::Debug + Clone + Send + Sync + 'static {}
|
||||
|
||||
@@ -82,9 +84,8 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> Service<B, S,
|
||||
pub fn new<I: 'static + ImportQueue<B>>(
|
||||
params: Params<B, S, H>,
|
||||
protocol_id: ProtocolId,
|
||||
import_queue: I,
|
||||
import_queue: Arc<I>,
|
||||
) -> Result<Arc<Service<B, S, H>>, Error> {
|
||||
let import_queue = Arc::new(import_queue);
|
||||
let handler = Arc::new(Protocol::new(
|
||||
params.config,
|
||||
params.chain,
|
||||
@@ -101,7 +102,7 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> Service<B, S,
|
||||
network,
|
||||
protocol_id,
|
||||
handler,
|
||||
bg_thread: Some(thread),
|
||||
bg_thread: Some(thread)
|
||||
});
|
||||
|
||||
// connect the import-queue to the network service.
|
||||
@@ -131,6 +132,11 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> Service<B, S,
|
||||
{
|
||||
self.handler.with_spec(&mut NetSyncIo::new(&self.network, self.protocol_id), f)
|
||||
}
|
||||
|
||||
/// access the underlying consensus gossip handler
|
||||
pub fn consensus_gossip<'a>(&'a self) -> &'a RwLock<ConsensusGossip<B>> {
|
||||
self.handler.consensus_gossip()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> ::consensus::SyncOracle for Service<B, S, H> {
|
||||
|
||||
@@ -58,31 +58,23 @@ impl ExecuteInContext<Block> for DummyContextExecutor {
|
||||
}
|
||||
|
||||
/// The test specialization.
|
||||
pub struct DummySpecialization {
|
||||
/// Consensus gossip handle.
|
||||
pub gossip: ConsensusGossip<Block>,
|
||||
}
|
||||
pub struct DummySpecialization { }
|
||||
|
||||
impl NetworkSpecialization<Block> for DummySpecialization {
|
||||
fn status(&self) -> Vec<u8> { vec![] }
|
||||
|
||||
fn on_connect(&mut self, ctx: &mut Context<Block>, peer_id: NodeIndex, status: ::message::Status<Block>) {
|
||||
self.gossip.new_peer(ctx, peer_id, status.roles);
|
||||
fn on_connect(&mut self, _ctx: &mut Context<Block>, _peer_id: NodeIndex, _status: ::message::Status<Block>) {
|
||||
}
|
||||
|
||||
fn on_disconnect(&mut self, ctx: &mut Context<Block>, peer_id: NodeIndex) {
|
||||
self.gossip.peer_disconnected(ctx, peer_id);
|
||||
fn on_disconnect(&mut self, _ctx: &mut Context<Block>, _peer_id: NodeIndex) {
|
||||
}
|
||||
|
||||
fn on_message(
|
||||
&mut self,
|
||||
ctx: &mut Context<Block>,
|
||||
peer_id: NodeIndex,
|
||||
message: &mut Option<::message::Message<Block>>
|
||||
_ctx: &mut Context<Block>,
|
||||
_peer_id: NodeIndex,
|
||||
_message: &mut Option<::message::Message<Block>>
|
||||
) {
|
||||
if let Some(::message::generic::Message::Consensus(topic, data)) = message.take() {
|
||||
self.gossip.on_incoming(ctx, peer_id, topic, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +171,10 @@ impl<V: 'static + Verifier<Block>, D> Peer<V, D> {
|
||||
self.sync.on_peer_connected(&mut TestIo::new(&self.queue, Some(other)), other);
|
||||
}
|
||||
|
||||
pub fn consensus_gossip(&self) -> &RwLock<ConsensusGossip<Block>> {
|
||||
self.sync.consensus_gossip()
|
||||
}
|
||||
|
||||
/// Called on disconnect from other indicated peer.
|
||||
fn on_disconnect(&self, other: NodeIndex) {
|
||||
let mut io = TestIo::new(&self.queue, Some(other));
|
||||
@@ -233,9 +229,10 @@ impl<V: 'static + Verifier<Block>, D> Peer<V, D> {
|
||||
/// Push a message into the gossip network and relay to peers.
|
||||
/// `TestNet::sync_step` needs to be called to ensure it's propagated.
|
||||
pub fn gossip_message(&self, topic: Hash, data: Vec<u8>) {
|
||||
self.sync.with_spec(&mut TestIo::new(&self.queue, None), |spec, ctx| {
|
||||
spec.gossip.multicast(ctx, topic, data);
|
||||
})
|
||||
let gossip = self.sync.consensus_gossip();
|
||||
self.sync.with_spec(&mut TestIo::new(&self.queue, None), move |_s, context|{
|
||||
gossip.write().multicast(context, topic, data);
|
||||
});
|
||||
}
|
||||
|
||||
/// Add blocks to the peer -- edit the block before adding
|
||||
@@ -363,9 +360,7 @@ pub trait TestNetFactory: Sized {
|
||||
let (block_import, data) = self.make_block_import(client.clone());
|
||||
|
||||
let import_queue = Arc::new(SyncImportQueue::new(verifier, block_import));
|
||||
let specialization = DummySpecialization {
|
||||
gossip: ConsensusGossip::new(),
|
||||
};
|
||||
let specialization = DummySpecialization { };
|
||||
let sync = Protocol::new(
|
||||
config.clone(),
|
||||
client.clone(),
|
||||
|
||||
@@ -87,14 +87,15 @@ pub fn export_blocks<F, E, W>(config: FactoryFullConfiguration<F>, exit: E, mut
|
||||
}
|
||||
|
||||
/// Import blocks from a binary stream.
|
||||
pub fn import_blocks<F, E, R>(config: FactoryFullConfiguration<F>, exit: E, mut input: R) -> error::Result<()>
|
||||
pub fn import_blocks<F, E, R>(mut config: FactoryFullConfiguration<F>, exit: E, mut input: R) -> error::Result<()>
|
||||
where F: ServiceFactory, E: Future<Item=(),Error=()> + Send + 'static, R: Read,
|
||||
{
|
||||
struct DummyLink;
|
||||
impl<B: Block> Link<B> for DummyLink { }
|
||||
|
||||
let client = new_client::<F>(&config)?;
|
||||
let queue = components::FullComponents::<F>::build_import_queue(&config, client.clone())?;
|
||||
// FIXME: this shouldn't need a mutable config. https://github.com/paritytech/substrate/issues/1134
|
||||
let queue = components::FullComponents::<F>::build_import_queue(&mut config, client.clone())?;
|
||||
queue.start(DummyLink)?;
|
||||
|
||||
let (exit_send, exit_recv) = std::sync::mpsc::channel();
|
||||
|
||||
@@ -266,7 +266,7 @@ pub trait ServiceFactory: 'static + Sized {
|
||||
|
||||
/// ImportQueue for a full client
|
||||
fn build_full_import_queue(
|
||||
config: &FactoryFullConfiguration<Self>,
|
||||
config: &mut FactoryFullConfiguration<Self>,
|
||||
_client: Arc<FullClient<Self>>
|
||||
) -> Result<Self::FullImportQueue, error::Error> {
|
||||
if let Some(name) = config.chain_spec.consensus_engine() {
|
||||
@@ -281,7 +281,7 @@ pub trait ServiceFactory: 'static + Sized {
|
||||
|
||||
/// ImportQueue for a light client
|
||||
fn build_light_import_queue(
|
||||
config: &FactoryFullConfiguration<Self>,
|
||||
config: &mut FactoryFullConfiguration<Self>,
|
||||
_client: Arc<LightClient<Self>>
|
||||
) -> Result<Self::LightImportQueue, error::Error> {
|
||||
if let Some(name) = config.chain_spec.consensus_engine() {
|
||||
@@ -336,7 +336,7 @@ pub trait Components: Sized + 'static {
|
||||
|
||||
/// instance of import queue for clients
|
||||
fn build_import_queue(
|
||||
config: &FactoryFullConfiguration<Self::Factory>,
|
||||
config: &mut FactoryFullConfiguration<Self::Factory>,
|
||||
client: Arc<ComponentClient<Self>>
|
||||
) -> Result<Self::ImportQueue, error::Error>;
|
||||
}
|
||||
@@ -409,7 +409,7 @@ impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
|
||||
}
|
||||
|
||||
fn build_import_queue(
|
||||
config: &FactoryFullConfiguration<Self::Factory>,
|
||||
config: &mut FactoryFullConfiguration<Self::Factory>,
|
||||
client: Arc<ComponentClient<Self>>
|
||||
) -> Result<Self::ImportQueue, error::Error> {
|
||||
Factory::build_full_import_queue(config, client)
|
||||
@@ -485,7 +485,7 @@ impl<Factory: ServiceFactory> Components for LightComponents<Factory> {
|
||||
}
|
||||
|
||||
fn build_import_queue(
|
||||
config: &FactoryFullConfiguration<Self::Factory>,
|
||||
config: &mut FactoryFullConfiguration<Self::Factory>,
|
||||
client: Arc<ComponentClient<Self>>
|
||||
) -> Result<Self::ImportQueue, error::Error> {
|
||||
Factory::build_light_import_queue(config, client)
|
||||
|
||||
@@ -103,6 +103,8 @@ pub struct Service<Components: components::Components> {
|
||||
keystore: Keystore,
|
||||
exit: ::exit_future::Exit,
|
||||
signal: Option<Signal>,
|
||||
/// Configuration of this Service
|
||||
pub config: FactoryFullConfiguration<Components::Factory>,
|
||||
proposer: Arc<ProposerFactory<ComponentClient<Components>, Components::TransactionPoolApi>>,
|
||||
_rpc_http: Option<rpc::HttpServer>,
|
||||
_rpc_ws: Option<Mutex<rpc::WsServer>>, // WsServer is not `Sync`, but the service needs to be.
|
||||
@@ -129,7 +131,7 @@ impl<Components> Service<Components>
|
||||
{
|
||||
/// Creates a new service.
|
||||
pub fn new(
|
||||
config: FactoryFullConfiguration<Components::Factory>,
|
||||
mut config: FactoryFullConfiguration<Components::Factory>,
|
||||
task_executor: TaskExecutor,
|
||||
)
|
||||
-> Result<Self, error::Error>
|
||||
@@ -159,7 +161,7 @@ impl<Components> Service<Components>
|
||||
};
|
||||
|
||||
let (client, on_demand) = Components::build_client(&config, executor)?;
|
||||
let import_queue = Components::build_import_queue(&config, client.clone())?;
|
||||
let import_queue = Arc::new(Components::build_import_queue(&mut config, client.clone())?);
|
||||
let best_header = client.best_block_header()?;
|
||||
|
||||
let version = config.full_version();
|
||||
@@ -168,7 +170,7 @@ impl<Components> Service<Components>
|
||||
|
||||
let network_protocol = <Components::Factory>::build_network_protocol(&config)?;
|
||||
let transaction_pool = Arc::new(
|
||||
Components::build_transaction_pool(config.transaction_pool, client.clone())?
|
||||
Components::build_transaction_pool(config.transaction_pool.clone(), client.clone())?
|
||||
);
|
||||
let transaction_pool_adapter = TransactionPoolAdapter::<Components> {
|
||||
imports_external_transactions: !(config.roles == Roles::LIGHT),
|
||||
@@ -177,19 +179,30 @@ impl<Components> Service<Components>
|
||||
};
|
||||
|
||||
let network_params = Components::CreateNetworkParams::create_network_params(
|
||||
client.clone(), config.roles, config.network, on_demand.clone(),
|
||||
transaction_pool_adapter, network_protocol
|
||||
client.clone(),
|
||||
config.roles,
|
||||
config.network.clone(),
|
||||
on_demand.clone(),
|
||||
transaction_pool_adapter,
|
||||
network_protocol,
|
||||
);
|
||||
|
||||
let mut protocol_id = network::ProtocolId::default();
|
||||
let protocol_id_full = config.chain_spec.protocol_id().unwrap_or(DEFAULT_PROTOCOL_ID).as_bytes();
|
||||
if protocol_id_full.len() > protocol_id.len() {
|
||||
warn!("Protocol ID truncated to {} chars", protocol_id.len());
|
||||
}
|
||||
let id_len = protocol_id_full.len().min(protocol_id.len());
|
||||
&mut protocol_id[0..id_len].copy_from_slice(&protocol_id_full[0..id_len]);
|
||||
let protocol_id = {
|
||||
let protocol_id_full = config.chain_spec.protocol_id().unwrap_or(DEFAULT_PROTOCOL_ID).as_bytes();
|
||||
let mut protocol_id = network::ProtocolId::default();
|
||||
if protocol_id_full.len() > protocol_id.len() {
|
||||
warn!("Protocol ID truncated to {} chars", protocol_id.len());
|
||||
}
|
||||
let id_len = protocol_id_full.len().min(protocol_id.len());
|
||||
&mut protocol_id[0..id_len].copy_from_slice(&protocol_id_full[0..id_len]);
|
||||
protocol_id
|
||||
};
|
||||
|
||||
let network = network::Service::new(network_params, protocol_id, import_queue)?;
|
||||
let network = network::Service::new(
|
||||
network_params,
|
||||
protocol_id,
|
||||
import_queue
|
||||
)?;
|
||||
on_demand.map(|on_demand| on_demand.set_service_link(Arc::downgrade(&network)));
|
||||
|
||||
{
|
||||
@@ -244,7 +257,7 @@ impl<Components> Service<Components>
|
||||
});
|
||||
|
||||
// Telemetry
|
||||
let telemetry = match config.telemetry_url {
|
||||
let telemetry = match config.telemetry_url.clone() {
|
||||
Some(url) => {
|
||||
let is_authority = config.roles == Roles::AUTHORITY;
|
||||
let pubkey = format!("{}", public_key);
|
||||
@@ -276,6 +289,7 @@ impl<Components> Service<Components>
|
||||
transaction_pool: transaction_pool,
|
||||
signal: Some(signal),
|
||||
keystore: keystore,
|
||||
config,
|
||||
proposer,
|
||||
exit,
|
||||
_rpc_http: rpc_http,
|
||||
@@ -283,6 +297,23 @@ impl<Components> Service<Components>
|
||||
_telemetry: telemetry,
|
||||
})
|
||||
}
|
||||
|
||||
/// give the authority key, if we are an authority and have a key
|
||||
pub fn authority_key(&self) -> Option<primitives::ed25519::Pair> {
|
||||
if self.config.roles != Roles::AUTHORITY { return None }
|
||||
let keystore = &self.keystore;
|
||||
if let Ok(Some(Ok(key))) = keystore.contents().map(|keys| keys.get(0)
|
||||
.map(|k| keystore.load(k, "")))
|
||||
{
|
||||
Some(key)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config(&self) -> &FactoryFullConfiguration<Components::Factory> {
|
||||
&self.config
|
||||
}
|
||||
}
|
||||
|
||||
impl<Components> Service<Components> where Components: components::Components {
|
||||
@@ -466,6 +497,9 @@ impl<C: Components> network::TransactionPool<ComponentExHash<C>, ComponentBlock<
|
||||
/// Configuration = (),
|
||||
/// FullService = Service<FullComponents<Self>>
|
||||
/// { |config, executor| Service::<FullComponents<Factory>>::new(config, executor) },
|
||||
/// // Setup as Consensus Authority (if the role and key are given)
|
||||
/// AuthoritySetup = {
|
||||
/// |service: Self::FullService, executor: TaskExecutor, key: Arc<Pair>| { Ok(service) }},
|
||||
/// LightService = Service<LightComponents<Self>>
|
||||
/// { |config, executor| Service::<LightComponents<Factory>>::new(config, executor) },
|
||||
/// // Declare the import queue. The import queue is special as it takes two initializers.
|
||||
@@ -491,6 +525,7 @@ macro_rules! construct_service_factory {
|
||||
Genesis = $genesis:ty,
|
||||
Configuration = $config:ty,
|
||||
FullService = $full_service:ty { $( $full_service_init:tt )* },
|
||||
AuthoritySetup = { $( $authority_setup:tt )* },
|
||||
LightService = $light_service:ty { $( $light_service_init:tt )* },
|
||||
FullImportQueue = $full_import_queue:ty
|
||||
{ $( $full_import_queue_init:tt )* },
|
||||
@@ -539,14 +574,14 @@ macro_rules! construct_service_factory {
|
||||
}
|
||||
|
||||
fn build_full_import_queue(
|
||||
config: &$crate::FactoryFullConfiguration<Self>,
|
||||
config: &mut $crate::FactoryFullConfiguration<Self>,
|
||||
client: $crate::Arc<$crate::FullClient<Self>>,
|
||||
) -> $crate::Result<Self::FullImportQueue, $crate::Error> {
|
||||
( $( $full_import_queue_init )* ) (config, client)
|
||||
}
|
||||
|
||||
fn build_light_import_queue(
|
||||
config: &FactoryFullConfiguration<Self>,
|
||||
config: &mut FactoryFullConfiguration<Self>,
|
||||
client: Arc<$crate::LightClient<Self>>,
|
||||
) -> Result<Self::LightImportQueue, $crate::Error> {
|
||||
( $( $light_import_queue_init )* ) (config, client)
|
||||
@@ -565,7 +600,13 @@ macro_rules! construct_service_factory {
|
||||
executor: $crate::TaskExecutor
|
||||
) -> Result<Self::FullService, $crate::Error>
|
||||
{
|
||||
( $( $full_service_init )* ) (config, executor)
|
||||
( $( $full_service_init )* ) (config, executor.clone()).and_then(|service| {
|
||||
if let Some(key) = (&service).authority_key() {
|
||||
($( $authority_setup )*)(service, executor, Arc::new(key))
|
||||
} else {
|
||||
Ok(service)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ use traits::{self, Member, DigestItem as DigestItemT, MaybeSerializeDebug};
|
||||
use substrate_primitives::hash::H512 as Signature;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
pub struct Digest<Item> {
|
||||
pub logs: Vec<Item>,
|
||||
}
|
||||
@@ -57,7 +57,7 @@ impl<Item> traits::Digest for Digest<Item> where
|
||||
/// Digest item that is able to encode/decode 'system' digest items and
|
||||
/// provide opaque access to other items.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
pub enum DigestItem<Hash, AuthorityId> {
|
||||
/// System digest item announcing that authorities set has been changed
|
||||
/// in the block. Contains the new set of authorities.
|
||||
|
||||
@@ -354,7 +354,7 @@ macro_rules! impl_outer_log {
|
||||
/// Wrapper for all possible log entries for the `$trait` runtime. Provides binary-compatible
|
||||
/// `Encode`/`Decode` implementations with the corresponding `generic::DigestItem`.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
$(#[$attr])*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $name($internal);
|
||||
@@ -362,7 +362,7 @@ macro_rules! impl_outer_log {
|
||||
/// All possible log entries for the `$trait` runtime. `Encode`/`Decode` implementations
|
||||
/// are auto-generated => it is not binary-compatible with `generic::DigestItem`.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
$(#[$attr])*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum InternalLog {
|
||||
@@ -482,7 +482,7 @@ mod tests {
|
||||
use super::RuntimeT;
|
||||
pub type Log<R> = RawLog<<R as RuntimeT>::AuthorityId>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Encode, Decode, PartialEq, Eq, Clone)]
|
||||
#[derive(Serialize, Debug, Encode, Decode, PartialEq, Eq, Clone)]
|
||||
pub enum RawLog<AuthorityId> { A1(AuthorityId), AuthoritiesChange(Vec<AuthorityId>), A3(AuthorityId) }
|
||||
}
|
||||
|
||||
@@ -490,7 +490,7 @@ mod tests {
|
||||
use super::RuntimeT;
|
||||
pub type Log<R> = RawLog<<R as RuntimeT>::AuthorityId>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Encode, Decode, PartialEq, Eq, Clone)]
|
||||
#[derive(Serialize, Debug, Encode, Decode, PartialEq, Eq, Clone)]
|
||||
pub enum RawLog<AuthorityId> { B1(AuthorityId), B2(AuthorityId) }
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ pub use substrate_primitives::{H256, AuthorityId};
|
||||
|
||||
pub type DigestItem = GenDigestItem<H256, u64>;
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)]
|
||||
#[derive(Default, PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode)]
|
||||
pub struct Digest {
|
||||
pub logs: Vec<DigestItem>,
|
||||
}
|
||||
@@ -48,7 +48,7 @@ impl traits::Digest for Digest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)]
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Header {
|
||||
@@ -98,15 +98,30 @@ impl traits::Header for Header {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)]
|
||||
impl<'a> Deserialize<'a> for Header {
|
||||
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
|
||||
let r = <Vec<u8>>::deserialize(de)?;
|
||||
Decode::decode(&mut &r[..]).ok_or(DeError::custom("Invalid value passed into decode"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)]
|
||||
pub struct ExtrinsicWrapper<Xt>(Xt);
|
||||
|
||||
impl<Xt> traits::Extrinsic for ExtrinsicWrapper<Xt> where Xt: Serialize {
|
||||
impl<Xt> traits::Extrinsic for ExtrinsicWrapper<Xt> {
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Xt: Encode> serde::Serialize for ExtrinsicWrapper<Xt>
|
||||
{
|
||||
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
|
||||
self.using_encoded(|bytes| seq.serialize_bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Xt> From<Xt> for ExtrinsicWrapper<Xt> {
|
||||
fn from(xt: Xt) -> Self {
|
||||
ExtrinsicWrapper(xt)
|
||||
|
||||
@@ -544,8 +544,8 @@ pub trait Applyable: Sized + Send + Sync {
|
||||
|
||||
/// Something that acts like a `Digest` - it can have `Log`s `push`ed onto it and these `Log`s are
|
||||
/// each `Codec`.
|
||||
pub trait Digest: Member + MaybeSerializeDebug + Default {
|
||||
type Hash: Member + MaybeSerializeDebug;
|
||||
pub trait Digest: Member + MaybeSerializeDebugButNotDeserialize + Default {
|
||||
type Hash: Member + MaybeSerializeDebugButNotDeserialize;
|
||||
type Item: DigestItem<Hash = Self::Hash>;
|
||||
|
||||
/// Get reference to all digest items.
|
||||
@@ -567,9 +567,9 @@ pub trait Digest: Member + MaybeSerializeDebug + Default {
|
||||
/// for casting member to 'system' log items, known to substrate.
|
||||
///
|
||||
/// If the runtime does not supports some 'system' items, use `()` as a stub.
|
||||
pub trait DigestItem: Codec + Member + MaybeSerializeDebug {
|
||||
type Hash: Member + MaybeSerializeDebug;
|
||||
type AuthorityId: Member + MaybeSerializeDebug;
|
||||
pub trait DigestItem: Codec + Member + MaybeSerializeDebugButNotDeserialize {
|
||||
type Hash: Member + MaybeSerializeDebugButNotDeserialize;
|
||||
type AuthorityId: Member + MaybeSerializeDebugButNotDeserialize;
|
||||
|
||||
/// Returns Some if the entry is the `AuthoritiesChange` entry.
|
||||
fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]>;
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
extern crate sr_std as rstd;
|
||||
extern crate parity_codec as codec;
|
||||
extern crate sr_primitives as runtime_primitives;
|
||||
@@ -89,7 +92,7 @@ pub fn native_version() -> NativeVersion {
|
||||
|
||||
/// Calls in transactions.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct Transfer {
|
||||
pub from: AccountId,
|
||||
pub to: AccountId,
|
||||
@@ -99,12 +102,20 @@ pub struct Transfer {
|
||||
|
||||
/// Extrinsic for test-runtime.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct Extrinsic {
|
||||
pub transfer: Transfer,
|
||||
pub signature: Ed25519Signature,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl serde::Serialize for Extrinsic
|
||||
{
|
||||
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
|
||||
self.using_encoded(|bytes| seq.serialize_bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl BlindCheckable for Extrinsic {
|
||||
type Checked = Self;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user