// 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); } }