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:
Robert Habermeier
2018-11-21 18:42:50 +01:00
committed by GitHub
parent 84da9d4a02
commit 11fe84a742
59 changed files with 1694 additions and 696 deletions
+2 -5
View File
@@ -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"
-59
View File
@@ -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
----
-253
View File
@@ -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
+93 -77
View File
@@ -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<()>
+216
View File
@@ -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>;
}
}
+7 -1
View File
@@ -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);
+98 -18
View File
@@ -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
>;
+17 -5
View File
@@ -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);
}
+3 -55
View File
@@ -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};
+1 -1
View File
@@ -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};
+17
View File
@@ -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) {
+10 -4
View File
@@ -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> {
+15 -20
View File
@@ -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(),
+3 -2
View File
@@ -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();
+5 -5
View File
@@ -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)
+58 -17
View File
@@ -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.
+4 -4
View File
@@ -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) }
}
+19 -4
View File
@@ -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)
+5 -5
View File
@@ -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]>;
+13 -2
View File
@@ -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;