Generalize the Consensus Infrastructure (#883)

* Split out Consensus
* Supply ImportQueue through network-service
  - simplify ImportQueue.import_blocks
  - remove Deadlock on import_block
  - Adding Verifier-Trait
  - Implement import_queue provisioning in service; allow cli to import
* Allow to actually customize import queue
* Consensus Gossip: Cache Message hash per Topic
This commit is contained in:
Benjamin Kampmann
2018-10-16 13:40:33 +02:00
committed by GitHub
parent a24e61cb29
commit ac4bcf879f
61 changed files with 1937 additions and 3306 deletions
+94
View File
@@ -0,0 +1,94 @@
// 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/>.
//! Error types in the BFT service.
use runtime_version::RuntimeVersion;
error_chain! {
errors {
/// Missing state at block with given descriptor.
StateUnavailable(b: String) {
description("State missing at given block."),
display("State unavailable at block {}", b),
}
/// I/O terminated unexpectedly
IoTerminated {
description("I/O terminated unexpectedly."),
display("I/O terminated unexpectedly."),
}
/// Unable to schedule wakeup.
FaultyTimer(e: ::tokio::timer::Error) {
description("Timer error"),
display("Timer error: {}", e),
}
/// Unable to propose a block.
CannotPropose {
description("Unable to create block proposal."),
display("Unable to create block proposal."),
}
/// Error checking signature
InvalidSignature(s: ::primitives::ed25519::Signature, a: ::primitives::AuthorityId) {
description("Message signature is invalid"),
display("Message signature {:?} by {:?} is invalid.", s, a),
}
/// Account is not an authority.
InvalidAuthority(a: ::primitives::AuthorityId) {
description("Message sender is not a valid authority"),
display("Message sender {:?} is not a valid authority.", a),
}
/// Authoring interface does not match the runtime.
IncompatibleAuthoringRuntime(native: RuntimeVersion, on_chain: RuntimeVersion) {
description("Authoring for current runtime is not supported"),
display("Authoring for current runtime is not supported. Native ({}) cannot author for on-chain ({}).", native, on_chain),
}
/// Authoring interface does not match the runtime.
RuntimeVersionMissing {
description("Current runtime has no version"),
display("Authoring for current runtime is not supported since it has no version."),
}
/// Authoring interface does not match the runtime.
NativeRuntimeMissing {
description("This build has no native runtime"),
display("Authoring in current build is not supported since it has no runtime."),
}
/// Justification requirements not met.
InvalidJustification {
description("Invalid justification"),
display("Invalid justification."),
}
/// Some other error.
Other(e: Box<::std::error::Error + Send>) {
description("Other error")
display("Other error: {}", e.description())
}
}
}
impl From<::rhododendron::InputStreamConcluded> for Error {
fn from(_: ::rhododendron::InputStreamConcluded) -> Error {
ErrorKind::IoTerminated.into()
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,192 @@
// 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/>.
//! Utility for substrate-based runtimes that want to check misbehavior reports.
use codec::{Codec, Encode};
use primitives::{AuthorityId, Signature};
use rhododendron::messages::{Action, Message, MisbehaviorKind};
use runtime_io;
// check a message signature. returns true if signed by that authority.
fn check_message_sig<B: Codec, H: Codec>(
message: Message<B, H>,
signature: &Signature,
from: &AuthorityId
) -> bool {
let msg: Vec<u8> = message.encode();
runtime_io::ed25519_verify(&signature.0, &msg, from)
}
fn prepare<B, H>(parent: H, round_number: u32, hash: H) -> Message<B, H> {
Message {
parent,
action: Action::Prepare(round_number, hash),
}
}
fn commit<B, H>(parent: H, round_number: u32, hash: H) -> Message<B, H> {
Message {
parent,
action: Action::Commit(round_number, hash),
}
}
/// Evaluate misbehavior.
///
/// Doesn't check that the header hash in question is
/// valid or whether the misbehaving authority was part of
/// the set at that block.
pub fn evaluate_misbehavior<B: Codec, H: Codec + Copy>(
misbehaved: &AuthorityId,
parent_hash: H,
kind: &MisbehaviorKind<H>,
) -> bool {
match *kind {
MisbehaviorKind::BftDoublePrepare(round, (h_1, ref s_1), (h_2, ref s_2)) => {
s_1 != s_2 &&
check_message_sig::<B, H>(prepare::<B, H>(parent_hash, round, h_1), s_1, misbehaved) &&
check_message_sig::<B, H>(prepare::<B, H>(parent_hash, round, h_2), s_2, misbehaved)
}
MisbehaviorKind::BftDoubleCommit(round, (h_1, ref s_1), (h_2, ref s_2)) => {
s_1 != s_2 &&
check_message_sig::<B, H>(commit::<B, H>(parent_hash, round, h_1), s_1, misbehaved) &&
check_message_sig::<B, H>(commit::<B, H>(parent_hash, round, h_2), s_2, misbehaved)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use keyring::ed25519;
use keyring::Keyring;
use rhododendron;
use runtime_primitives::testing::{H256, Block as RawBlock};
type Block = RawBlock<u64>;
fn sign_prepare(key: &ed25519::Pair, round: u32, hash: H256, parent_hash: H256) -> (H256, Signature) {
let msg = ::sign_message::<Block>(
rhododendron::Message::Vote(rhododendron::Vote::Prepare(round as _, hash)),
key,
parent_hash
);
match msg {
rhododendron::LocalizedMessage::Vote(vote) => (hash, vote.signature.signature),
_ => panic!("signing vote leads to signed vote"),
}
}
fn sign_commit(key: &ed25519::Pair, round: u32, hash: H256, parent_hash: H256) -> (H256, Signature) {
let msg = ::sign_message::<Block>(
rhododendron::Message::Vote(rhododendron::Vote::Commit(round as _, hash)),
key,
parent_hash
);
match msg {
rhododendron::LocalizedMessage::Vote(vote) => (hash, vote.signature.signature),
_ => panic!("signing vote leads to signed vote"),
}
}
#[test]
fn evaluates_double_prepare() {
let key: ed25519::Pair = Keyring::One.into();
let parent_hash = [0xff; 32].into();
let hash_1 = [0; 32].into();
let hash_2 = [1; 32].into();
assert!(evaluate_misbehavior::<Block, H256>(
&key.public().into(),
parent_hash,
&MisbehaviorKind::BftDoublePrepare(
1,
sign_prepare(&key, 1, hash_1, parent_hash),
sign_prepare(&key, 1, hash_2, parent_hash)
)
));
// same signature twice is not misbehavior.
let signed = sign_prepare(&key, 1, hash_1, parent_hash);
assert!(evaluate_misbehavior::<Block, H256>(
&key.public().into(),
parent_hash,
&MisbehaviorKind::BftDoublePrepare(
1,
signed,
signed,
)
) == false);
// misbehavior has wrong target.
assert!(evaluate_misbehavior::<Block, H256>(
&Keyring::Two.to_raw_public().into(),
parent_hash,
&MisbehaviorKind::BftDoublePrepare(
1,
sign_prepare(&key, 1, hash_1, parent_hash),
sign_prepare(&key, 1, hash_2, parent_hash),
)
) == false);
}
#[test]
fn evaluates_double_commit() {
let key: ed25519::Pair = Keyring::One.into();
let parent_hash = [0xff; 32].into();
let hash_1 = [0; 32].into();
let hash_2 = [1; 32].into();
assert!(evaluate_misbehavior::<Block, H256>(
&key.public().into(),
parent_hash,
&MisbehaviorKind::BftDoubleCommit(
1,
sign_commit(&key, 1, hash_1, parent_hash),
sign_commit(&key, 1, hash_2, parent_hash)
)
));
// same signature twice is not misbehavior.
let signed = sign_commit(&key, 1, hash_1, parent_hash);
assert!(evaluate_misbehavior::<Block, H256>(
&key.public().into(),
parent_hash,
&MisbehaviorKind::BftDoubleCommit(
1,
signed,
signed,
)
) == false);
// misbehavior has wrong target.
assert!(evaluate_misbehavior::<Block, H256>(
&Keyring::Two.to_raw_public().into(),
parent_hash,
&MisbehaviorKind::BftDoubleCommit(
1,
sign_commit(&key, 1, hash_1, parent_hash),
sign_commit(&key, 1, hash_2, parent_hash),
)
) == false);
}
}