// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] //! Primitives for BEEFY protocol. //! //! The crate contains shared data types used by BEEFY protocol and documentation (in a form of //! code) for building a BEEFY light client. //! //! BEEFY is a gadget that runs alongside another finality gadget (for instance GRANDPA). //! For simplicity (and the initially intended use case) the documentation says GRANDPA in places //! where a more abstract "Finality Gadget" term could be used, but there is no reason why BEEFY //! wouldn't run with some other finality scheme. //! BEEFY validator set is supposed to be tracking the Finality Gadget validator set, but note that //! it will use a different set of keys. For Polkadot use case we plan to use `secp256k1` for BEEFY, //! while GRANDPA uses `ed25519`. mod commitment; pub mod mmr; mod payload; #[cfg(feature = "std")] mod test_utils; pub mod witness; pub use commitment::{Commitment, SignedCommitment, VersionedFinalityProof}; pub use payload::{known_payloads, BeefyPayloadId, Payload, PayloadProvider}; #[cfg(feature = "std")] pub use test_utils::*; use codec::{Codec, Decode, Encode}; use scale_info::TypeInfo; use sp_application_crypto::RuntimeAppPublic; use sp_core::H256; use sp_runtime::traits::{Hash, NumberFor}; use sp_std::prelude::*; /// Key type for BEEFY module. pub const KEY_TYPE: sp_application_crypto::KeyTypeId = sp_application_crypto::KeyTypeId(*b"beef"); /// Trait representing BEEFY authority id, including custom signature verification. /// /// Accepts custom hashing fn for the message and custom convertor fn for the signer. pub trait BeefyAuthorityId: RuntimeAppPublic { /// Verify a signature. /// /// Return `true` if signature over `msg` is valid for this id. fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool; } /// BEEFY cryptographic types /// /// This module basically introduces three crypto types: /// - `crypto::Pair` /// - `crypto::Public` /// - `crypto::Signature` /// /// Your code should use the above types as concrete types for all crypto related /// functionality. /// /// The current underlying crypto scheme used is ECDSA. This can be changed, /// without affecting code restricted against the above listed crypto types. pub mod crypto { use super::{BeefyAuthorityId, Hash, RuntimeAppPublic}; use sp_application_crypto::{app_crypto, ecdsa}; use sp_core::crypto::Wraps; app_crypto!(ecdsa, crate::KEY_TYPE); /// Identity of a BEEFY authority using ECDSA as its crypto. pub type AuthorityId = Public; /// Signature for a BEEFY authority using ECDSA as its crypto. pub type AuthoritySignature = Signature; impl BeefyAuthorityId for AuthorityId where ::Output: Into<[u8; 32]>, { fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool { let msg_hash = ::hash(msg).into(); match sp_io::crypto::secp256k1_ecdsa_recover_compressed( signature.as_inner_ref().as_ref(), &msg_hash, ) { Ok(raw_pubkey) => raw_pubkey.as_ref() == AsRef::<[u8]>::as_ref(self), _ => false, } } } } /// The `ConsensusEngineId` of BEEFY. pub const BEEFY_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"BEEF"; /// Authority set id starts with zero at BEEFY pallet genesis. pub const GENESIS_AUTHORITY_SET_ID: u64 = 0; /// A typedef for validator set id. pub type ValidatorSetId = u64; /// A set of BEEFY authorities, a.k.a. validators. #[derive(Decode, Encode, Debug, PartialEq, Clone, TypeInfo)] pub struct ValidatorSet { /// Public keys of the validator set elements validators: Vec, /// Identifier of the validator set id: ValidatorSetId, } impl ValidatorSet { /// Return a validator set with the given validators and set id. pub fn new(validators: I, id: ValidatorSetId) -> Option where I: IntoIterator, { let validators: Vec = validators.into_iter().collect(); if validators.is_empty() { // No validators; the set would be empty. None } else { Some(Self { validators, id }) } } /// Return a reference to the vec of validators. pub fn validators(&self) -> &[AuthorityId] { &self.validators } /// Return the validator set id. pub fn id(&self) -> ValidatorSetId { self.id } /// Return the number of validators in the set. pub fn len(&self) -> usize { self.validators.len() } } /// The index of an authority. pub type AuthorityIndex = u32; /// The type used to represent an MMR root hash. pub type MmrRootHash = H256; /// A consensus log item for BEEFY. #[derive(Decode, Encode, TypeInfo)] pub enum ConsensusLog { /// The authorities have changed. #[codec(index = 1)] AuthoritiesChange(ValidatorSet), /// Disable the authority with given index. #[codec(index = 2)] OnDisabled(AuthorityIndex), /// MMR root hash. #[codec(index = 3)] MmrRoot(MmrRootHash), } /// BEEFY vote message. /// /// A vote message is a direct vote created by a BEEFY node on every voting round /// and is gossiped to its peers. #[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)] pub struct VoteMessage { /// Commit to information extracted from a finalized block pub commitment: Commitment, /// Node authority id pub id: Id, /// Node signature pub signature: Signature, } /// Proof of voter misbehavior on a given set id. Misbehavior/equivocation in /// BEEFY happens when a voter votes on the same round/block for different payloads. /// Proving is achieved by collecting the signed commitments of conflicting votes. #[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)] pub struct EquivocationProof { /// The first vote in the equivocation. pub first: VoteMessage, /// The second vote in the equivocation. pub second: VoteMessage, } impl EquivocationProof { /// Returns the authority id of the equivocator. pub fn offender_id(&self) -> &Id { &self.first.id } /// Returns the round number at which the equivocation occurred. pub fn round_number(&self) -> &Number { &self.first.commitment.block_number } /// Returns the set id at which the equivocation occurred. pub fn set_id(&self) -> ValidatorSetId { self.first.commitment.validator_set_id } } /// Check a commitment signature by encoding the commitment and /// verifying the provided signature using the expected authority id. pub fn check_commitment_signature( commitment: &Commitment, authority_id: &Id, signature: &::Signature, ) -> bool where Id: BeefyAuthorityId, Number: Clone + Encode + PartialEq, MsgHash: Hash, { let encoded_commitment = commitment.encode(); BeefyAuthorityId::::verify(authority_id, signature, &encoded_commitment) } /// Verifies the equivocation proof by making sure that both votes target /// different blocks and that its signatures are valid. pub fn check_equivocation_proof( report: &EquivocationProof::Signature>, ) -> bool where Id: BeefyAuthorityId + PartialEq, Number: Clone + Encode + PartialEq, MsgHash: Hash, { let first = &report.first; let second = &report.second; // if votes // come from different authorities, // are for different rounds, // have different validator set ids, // or both votes have the same commitment, // --> the equivocation is invalid. if first.id != second.id || first.commitment.block_number != second.commitment.block_number || first.commitment.validator_set_id != second.commitment.validator_set_id || first.commitment.payload == second.commitment.payload { return false } // check signatures on both votes are valid let valid_first = check_commitment_signature(&first.commitment, &first.id, &first.signature); let valid_second = check_commitment_signature(&second.commitment, &second.id, &second.signature); return valid_first && valid_second } /// New BEEFY validator set notification hook. pub trait OnNewValidatorSet { /// Function called by the pallet when BEEFY validator set changes. fn on_new_validator_set( validator_set: &ValidatorSet, next_validator_set: &ValidatorSet, ); } /// No-op implementation of [OnNewValidatorSet]. impl OnNewValidatorSet for () { fn on_new_validator_set(_: &ValidatorSet, _: &ValidatorSet) {} } /// An opaque type used to represent the key ownership proof at the runtime API /// boundary. The inner value is an encoded representation of the actual key /// ownership proof which will be parameterized when defining the runtime. At /// the runtime API boundary this type is unknown and as such we keep this /// opaque representation, implementors of the runtime API will have to make /// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type. #[derive(Decode, Encode, PartialEq, TypeInfo)] pub struct OpaqueKeyOwnershipProof(Vec); impl OpaqueKeyOwnershipProof { /// Create a new `OpaqueKeyOwnershipProof` using the given encoded /// representation. pub fn new(inner: Vec) -> OpaqueKeyOwnershipProof { OpaqueKeyOwnershipProof(inner) } /// Try to decode this `OpaqueKeyOwnershipProof` into the given concrete key /// ownership proof type. pub fn decode(self) -> Option { codec::Decode::decode(&mut &self.0[..]).ok() } } sp_api::decl_runtime_apis! { /// API necessary for BEEFY voters. #[api_version(2)] pub trait BeefyApi { /// Return the block number where BEEFY consensus is enabled/started fn beefy_genesis() -> Option>; /// Return the current active BEEFY validator set fn validator_set() -> Option>; /// Submits an unsigned extrinsic to report an equivocation. The caller /// must provide the equivocation proof and a key ownership proof /// (should be obtained using `generate_key_ownership_proof`). The /// extrinsic will be unsigned and should only be accepted for local /// authorship (not to be broadcast to the network). This method returns /// `None` when creation of the extrinsic fails, e.g. if equivocation /// reporting is disabled for the given runtime (i.e. this method is /// hardcoded to return `None`). Only useful in an offchain context. fn submit_report_equivocation_unsigned_extrinsic( equivocation_proof: EquivocationProof, crypto::AuthorityId, crypto::Signature>, key_owner_proof: OpaqueKeyOwnershipProof, ) -> Option<()>; /// Generates a proof of key ownership for the given authority in the /// given set. An example usage of this module is coupled with the /// session historical module to prove that a given authority key is /// tied to a given staking identity during a specific session. Proofs /// of key ownership are necessary for submitting equivocation reports. /// NOTE: even though the API takes a `set_id` as parameter the current /// implementations ignores this parameter and instead relies on this /// method being called at the correct block height, i.e. any point at /// which the given set id is live on-chain. Future implementations will /// instead use indexed data through an offchain worker, not requiring /// older states to be available. fn generate_key_ownership_proof( set_id: ValidatorSetId, authority_id: crypto::AuthorityId, ) -> Option; } } #[cfg(test)] mod tests { use super::*; use sp_application_crypto::ecdsa::{self, Public}; use sp_core::{blake2_256, crypto::Wraps, keccak_256, Pair}; use sp_runtime::traits::{BlakeTwo256, Keccak256}; #[test] fn validator_set() { // Empty set not allowed. assert_eq!(ValidatorSet::::new(vec![], 0), None); let alice = ecdsa::Pair::from_string("//Alice", None).unwrap(); let set_id = 0; let validators = ValidatorSet::::new(vec![alice.public()], set_id).unwrap(); assert_eq!(validators.id(), set_id); assert_eq!(validators.validators(), &vec![alice.public()]); } #[test] fn beefy_verify_works() { let msg = &b"test-message"[..]; let (pair, _) = crypto::Pair::generate(); let keccak_256_signature: crypto::Signature = pair.as_inner_ref().sign_prehashed(&keccak_256(msg)).into(); let blake2_256_signature: crypto::Signature = pair.as_inner_ref().sign_prehashed(&blake2_256(msg)).into(); // Verification works if same hashing function is used when signing and verifying. assert!(BeefyAuthorityId::::verify(&pair.public(), &keccak_256_signature, msg)); assert!(BeefyAuthorityId::::verify( &pair.public(), &blake2_256_signature, msg )); // Verification fails if distinct hashing functions are used when signing and verifying. assert!(!BeefyAuthorityId::::verify(&pair.public(), &blake2_256_signature, msg)); assert!(!BeefyAuthorityId::::verify( &pair.public(), &keccak_256_signature, msg )); // Other public key doesn't work let (other_pair, _) = crypto::Pair::generate(); assert!(!BeefyAuthorityId::::verify( &other_pair.public(), &keccak_256_signature, msg, )); assert!(!BeefyAuthorityId::::verify( &other_pair.public(), &blake2_256_signature, msg, )); } }