// Copyright 2017 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 .
// tag::description[]
//! Utility for substrate-based runtimes that want to check misbehavior reports.
// end::description[]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate parity_codec as codec;
extern crate substrate_primitives as primitives;
extern crate sr_io as runtime_io;
extern crate sr_primitives as runtime_primitives;
#[cfg(test)]
extern crate substrate_bft;
#[cfg(test)]
extern crate substrate_keyring as keyring;
#[cfg(test)]
extern crate rhododendron;
use codec::{Codec, Encode};
use primitives::{AuthorityId, Signature};
use runtime_primitives::bft::{Action, Message, MisbehaviorKind};
// check a message signature. returns true if signed by that authority.
fn check_message_sig(
message: Message,
signature: &Signature,
from: &AuthorityId
) -> bool {
let msg: Vec = message.encode();
runtime_io::ed25519_verify(&signature.0, &msg, from)
}
fn prepare(parent: H, round_number: u32, hash: H) -> Message {
Message {
parent,
action: Action::Prepare(round_number, hash),
}
}
fn commit(parent: H, round_number: u32, hash: H) -> Message {
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(
misbehaved: &AuthorityId,
parent_hash: H,
kind: &MisbehaviorKind,
) -> bool {
match *kind {
MisbehaviorKind::BftDoublePrepare(round, (h_1, ref s_1), (h_2, ref s_2)) => {
s_1 != s_2 &&
check_message_sig::(prepare::(parent_hash, round, h_1), s_1, misbehaved) &&
check_message_sig::(prepare::(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::(commit::(parent_hash, round, h_1), s_1, misbehaved) &&
check_message_sig::(commit::(parent_hash, round, h_2), s_2, misbehaved)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use keyring::ed25519;
use keyring::Keyring;
use runtime_primitives::testing::{H256, Block as RawBlock};
type Block = RawBlock;
fn sign_prepare(key: &ed25519::Pair, round: u32, hash: H256, parent_hash: H256) -> (H256, Signature) {
let msg = substrate_bft::sign_message::(
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 = substrate_bft::sign_message::(
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::(
&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::(
&key.public().into(),
parent_hash,
&MisbehaviorKind::BftDoublePrepare(
1,
signed,
signed,
)
) == false);
// misbehavior has wrong target.
assert!(evaluate_misbehavior::(
&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::(
&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::(
&key.public().into(),
parent_hash,
&MisbehaviorKind::BftDoubleCommit(
1,
signed,
signed,
)
) == false);
// misbehavior has wrong target.
assert!(evaluate_misbehavior::(
&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);
}
}