Skip to main content

sp_consensus_beefy/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19#![warn(missing_docs)]
20
21//! Primitives for BEEFY protocol.
22//!
23//! The crate contains shared data types used by BEEFY protocol and documentation (in a form of
24//! code) for building a BEEFY light client.
25//!
26//! BEEFY is a gadget that runs alongside another finality gadget (for instance GRANDPA).
27//! For simplicity (and the initially intended use case) the documentation says GRANDPA in places
28//! where a more abstract "Finality Gadget" term could be used, but there is no reason why BEEFY
29//! wouldn't run with some other finality scheme.
30//! BEEFY validator set is supposed to be tracking the Finality Gadget validator set, but note that
31//! it will use a different set of keys. For Polkadot use case we plan to use `secp256k1` for BEEFY,
32//! while GRANDPA uses `ed25519`.
33
34extern crate alloc;
35
36mod commitment;
37mod payload;
38
39pub mod mmr;
40pub mod witness;
41
42/// Test utilities
43#[cfg(feature = "std")]
44pub mod test_utils;
45
46pub use commitment::{Commitment, KnownSignature, SignedCommitment, VersionedFinalityProof};
47pub use payload::{known_payloads, BeefyPayloadId, Payload, PayloadProvider};
48
49use alloc::vec::Vec;
50use codec::{Codec, Decode, DecodeWithMemTracking, Encode};
51use core::fmt::{Debug, Display};
52use scale_info::TypeInfo;
53pub use sp_application_crypto::key_types::BEEFY as KEY_TYPE;
54use sp_application_crypto::{AppPublic, RuntimeAppPublic};
55use sp_core::H256;
56#[cfg(feature = "std")]
57use sp_keystore::KeystorePtr;
58use sp_runtime::{
59	traits::{Header as HeaderT, Keccak256, NumberFor},
60	OpaqueValue,
61};
62use sp_weights::Weight;
63use KEY_TYPE as BEEFY_KEY_TYPE;
64
65/// Trait representing BEEFY authority id, including custom signature verification.
66pub trait BeefyAuthorityId: RuntimeAppPublic {
67	/// Get all the public keys of the current type from a provided `Keystore`.
68	#[cfg(feature = "std")]
69	fn get_all_public_keys_from_store(store: KeystorePtr) -> Vec<impl AsRef<[u8]>>;
70
71	/// Sign a message using the private key associated to the current public key.
72	///
73	/// We can't access the private key directly, so we need to receive the store that contains it.
74	#[cfg(feature = "std")]
75	fn try_sign_with_store(
76		&self,
77		store: KeystorePtr,
78		msg: &[u8],
79	) -> Result<Option<impl AsRef<[u8]> + Debug>, sp_keystore::Error>;
80
81	/// Verify a signature.
82	///
83	/// Return `true` if signature over `msg` is valid for this id.
84	fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool;
85}
86
87/// A trait bound which lists all traits which are required to be implemented by
88/// a BEEFY AuthorityId type in order to be able to be used in BEEFY Keystore
89pub trait AuthorityIdBound:
90	Ord + AppPublic + Display + BeefyAuthorityId<Signature = Self::BoundedSignature>
91{
92	/// Necessary bounds on the Signature associated with the AuthorityId
93	type BoundedSignature: Debug + Eq + PartialEq + Clone + TypeInfo + Codec + Send + Sync;
94}
95
96/// BEEFY cryptographic types for ECDSA crypto
97///
98/// This module basically introduces four crypto types:
99/// - `ecdsa_crypto::Pair`
100/// - `ecdsa_crypto::Public`
101/// - `ecdsa_crypto::Signature`
102/// - `ecdsa_crypto::AuthorityId`
103///
104/// Your code should use the above types as concrete types for all crypto related
105/// functionality.
106pub mod ecdsa_crypto {
107	#[cfg(feature = "std")]
108	use super::Vec;
109	use super::{AuthorityIdBound, BeefyAuthorityId, RuntimeAppPublic, BEEFY_KEY_TYPE};
110	#[cfg(feature = "std")]
111	use core::fmt::Debug;
112	use sp_application_crypto::{app_crypto, ecdsa};
113	use sp_core::crypto::Wraps;
114	#[cfg(feature = "std")]
115	use sp_core::ByteArray;
116	use sp_crypto_hashing::keccak_256;
117	#[cfg(feature = "std")]
118	use sp_keystore::KeystorePtr;
119
120	app_crypto!(ecdsa, BEEFY_KEY_TYPE);
121
122	/// Identity of a BEEFY authority using ECDSA as its crypto.
123	pub type AuthorityId = Public;
124
125	/// Signature for a BEEFY authority using ECDSA as its crypto.
126	pub type AuthoritySignature = Signature;
127
128	impl BeefyAuthorityId for AuthorityId {
129		#[cfg(feature = "std")]
130		fn get_all_public_keys_from_store(store: KeystorePtr) -> Vec<impl AsRef<[u8]>> {
131			store.ecdsa_public_keys(BEEFY_KEY_TYPE)
132		}
133
134		#[cfg(feature = "std")]
135		fn try_sign_with_store(
136			&self,
137			store: sp_keystore::KeystorePtr,
138			msg: &[u8],
139		) -> Result<Option<impl AsRef<[u8]> + Debug>, sp_keystore::Error> {
140			let msg_hash = keccak_256(msg);
141			let public = ecdsa::Public::try_from(self.as_slice()).unwrap();
142			store.ecdsa_sign_prehashed(BEEFY_KEY_TYPE, &public, &msg_hash)
143		}
144
145		fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
146			let msg_hash = keccak_256(msg);
147			match sp_io::crypto::secp256k1_ecdsa_recover_compressed(
148				signature.as_inner_ref().as_ref(),
149				&msg_hash,
150			) {
151				Ok(raw_pubkey) => raw_pubkey.as_ref() == AsRef::<[u8]>::as_ref(self),
152				_ => false,
153			}
154		}
155	}
156	impl AuthorityIdBound for AuthorityId {
157		type BoundedSignature = Signature;
158	}
159}
160
161/// BEEFY cryptographic types for BLS crypto
162///
163/// This module basically introduces four crypto types:
164/// - `bls_crypto::Pair`
165/// - `bls_crypto::Public`
166/// - `bls_crypto::Signature`
167/// - `bls_crypto::AuthorityId`
168///
169/// Your code should use the above types as concrete types for all crypto related
170/// functionality.
171
172#[cfg(feature = "bls-experimental")]
173pub mod bls_crypto {
174	#[cfg(feature = "std")]
175	use super::Vec;
176	use super::{AuthorityIdBound, BeefyAuthorityId, RuntimeAppPublic, BEEFY_KEY_TYPE};
177	#[cfg(feature = "std")]
178	use core::fmt::Debug;
179	use sp_application_crypto::{app_crypto, bls381};
180	use sp_core::{bls381::Pair as BlsPair, crypto::Wraps, ByteArray, Pair as _};
181	#[cfg(feature = "std")]
182	use sp_keystore::KeystorePtr;
183
184	app_crypto!(bls381, BEEFY_KEY_TYPE);
185
186	/// Identity of a BEEFY authority using BLS as its crypto.
187	pub type AuthorityId = Public;
188
189	/// Signature for a BEEFY authority using BLS as its crypto.
190	pub type AuthoritySignature = Signature;
191
192	impl BeefyAuthorityId for AuthorityId {
193		#[cfg(feature = "std")]
194		fn get_all_public_keys_from_store(store: KeystorePtr) -> Vec<impl AsRef<[u8]>> {
195			store.bls381_public_keys(BEEFY_KEY_TYPE)
196		}
197
198		#[cfg(feature = "std")]
199		fn try_sign_with_store(
200			&self,
201			store: sp_keystore::KeystorePtr,
202			msg: &[u8],
203		) -> Result<Option<impl AsRef<[u8]> + Debug>, sp_keystore::Error> {
204			let public = bls381::Public::try_from(self.as_slice()).unwrap();
205			store.bls381_sign(BEEFY_KEY_TYPE, &public, msg)
206		}
207
208		fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
209			// `w3f-bls` library uses IETF hashing standard and as such does not expose
210			// a choice of hash-to-field function.
211			// We are directly calling into the library to avoid introducing new host call.
212			// and because BeefyAuthorityId::verify is being called in the runtime so we don't have
213
214			BlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())
215		}
216	}
217	impl AuthorityIdBound for AuthorityId {
218		type BoundedSignature = Signature;
219	}
220}
221
222/// BEEFY cryptographic types for (ECDSA,BLS) crypto pair
223///
224/// This module basically introduces four crypto types:
225/// - `ecdsa_bls_crypto::Pair`
226/// - `ecdsa_bls_crypto::Public`
227/// - `ecdsa_bls_crypto::Signature`
228/// - `ecdsa_bls_crypto::AuthorityId`
229///
230/// Your code should use the above types as concrete types for all crypto related
231/// functionality.
232#[cfg(feature = "bls-experimental")]
233pub mod ecdsa_bls_crypto {
234	#[cfg(feature = "std")]
235	use super::Vec;
236	use super::{AuthorityIdBound, BeefyAuthorityId, RuntimeAppPublic, BEEFY_KEY_TYPE};
237	#[cfg(feature = "std")]
238	use core::fmt::Debug;
239	use sp_application_crypto::{app_crypto, ecdsa_bls381};
240	use sp_core::{crypto::Wraps, ecdsa_bls381::Pair as EcdsaBlsPair, ByteArray};
241	#[cfg(feature = "std")]
242	use sp_keystore::KeystorePtr;
243	use sp_runtime::traits::Keccak256;
244
245	app_crypto!(ecdsa_bls381, BEEFY_KEY_TYPE);
246
247	/// Identity of a BEEFY authority using (ECDSA,BLS) as its crypto.
248	pub type AuthorityId = Public;
249
250	/// Signature for a BEEFY authority using (ECDSA,BLS) as its crypto.
251	pub type AuthoritySignature = Signature;
252
253	impl BeefyAuthorityId for AuthorityId {
254		#[cfg(feature = "std")]
255		fn get_all_public_keys_from_store(store: KeystorePtr) -> Vec<impl AsRef<[u8]>> {
256			store.ecdsa_bls381_public_keys(BEEFY_KEY_TYPE)
257		}
258
259		#[cfg(feature = "std")]
260		fn try_sign_with_store(
261			&self,
262			store: sp_keystore::KeystorePtr,
263			msg: &[u8],
264		) -> Result<Option<impl AsRef<[u8]> + Debug>, sp_keystore::Error> {
265			let public = ecdsa_bls381::Public::try_from(self.as_slice()).unwrap();
266			store.ecdsa_bls381_sign_with_keccak256(BEEFY_KEY_TYPE, &public, &msg)
267		}
268
269		fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
270			// We can not simply call
271			// `EcdsaBlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())`
272			// because that invokes ECDSA default verification which performs Blake2b hash
273			// which we don't want. This is because ECDSA signatures are meant to be verified
274			// on Ethereum network where Keccak hasher is significantly cheaper than Blake2b.
275			// See Figure 3 of [OnSc21](https://www.scitepress.org/Papers/2021/106066/106066.pdf)
276			// for comparison.
277			EcdsaBlsPair::verify_with_hasher::<Keccak256>(
278				signature.as_inner_ref(),
279				msg,
280				self.as_inner_ref(),
281			)
282		}
283	}
284
285	impl AuthorityIdBound for AuthorityId {
286		type BoundedSignature = Signature;
287	}
288}
289
290/// The `ConsensusEngineId` of BEEFY.
291pub const BEEFY_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"BEEF";
292
293/// Authority set id starts with zero at BEEFY pallet genesis.
294pub const GENESIS_AUTHORITY_SET_ID: u64 = 0;
295
296/// A typedef for validator set id.
297pub type ValidatorSetId = u64;
298
299/// A set of BEEFY authorities, a.k.a. validators.
300#[derive(Decode, Encode, Debug, PartialEq, Clone, TypeInfo)]
301pub struct ValidatorSet<AuthorityId> {
302	/// Public keys of the validator set elements
303	validators: Vec<AuthorityId>,
304	/// Identifier of the validator set
305	id: ValidatorSetId,
306}
307
308impl<AuthorityId> ValidatorSet<AuthorityId> {
309	/// Return a validator set with the given validators and set id.
310	pub fn new<I>(validators: I, id: ValidatorSetId) -> Option<Self>
311	where
312		I: IntoIterator<Item = AuthorityId>,
313	{
314		let validators: Vec<AuthorityId> = validators.into_iter().collect();
315		if validators.is_empty() {
316			// No validators; the set would be empty.
317			None
318		} else {
319			Some(Self { validators, id })
320		}
321	}
322
323	/// Return a reference to the vec of validators.
324	pub fn validators(&self) -> &[AuthorityId] {
325		&self.validators
326	}
327
328	/// Return the validator set id.
329	pub fn id(&self) -> ValidatorSetId {
330		self.id
331	}
332
333	/// Return the number of validators in the set.
334	pub fn len(&self) -> usize {
335		self.validators.len()
336	}
337}
338
339/// The index of an authority.
340pub type AuthorityIndex = u32;
341
342/// The Hashing used within MMR.
343pub type MmrHashing = Keccak256;
344/// The type used to represent an MMR root hash.
345pub type MmrRootHash = H256;
346
347/// A consensus log item for BEEFY.
348#[derive(Decode, Encode, TypeInfo)]
349pub enum ConsensusLog<AuthorityId: Codec> {
350	/// The authorities have changed.
351	#[codec(index = 1)]
352	AuthoritiesChange(ValidatorSet<AuthorityId>),
353	/// Disable the authority with given index.
354	#[codec(index = 2)]
355	OnDisabled(AuthorityIndex),
356	/// MMR root hash.
357	#[codec(index = 3)]
358	MmrRoot(MmrRootHash),
359}
360
361/// BEEFY vote message.
362///
363/// A vote message is a direct vote created by a BEEFY node on every voting round
364/// and is gossiped to its peers.
365// TODO: Remove `Signature` generic type, instead get it from `Id::Signature`.
366#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
367pub struct VoteMessage<Number, Id, Signature> {
368	/// Commit to information extracted from a finalized block
369	pub commitment: Commitment<Number>,
370	/// Node authority id
371	pub id: Id,
372	/// Node signature
373	pub signature: Signature,
374}
375
376/// Proof showing that an authority voted twice in the same round.
377///
378/// One type of misbehavior in BEEFY happens when an authority votes in the same round/block
379/// for different payloads.
380/// Proving is achieved by collecting the signed commitments of conflicting votes.
381#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
382pub struct DoubleVotingProof<Number, Id, Signature> {
383	/// The first vote in the equivocation.
384	pub first: VoteMessage<Number, Id, Signature>,
385	/// The second vote in the equivocation.
386	pub second: VoteMessage<Number, Id, Signature>,
387}
388
389impl<Number, Id, Signature> DoubleVotingProof<Number, Id, Signature> {
390	/// Returns the authority id of the equivocator.
391	pub fn offender_id(&self) -> &Id {
392		&self.first.id
393	}
394	/// Returns the round number at which the equivocation occurred.
395	pub fn round_number(&self) -> &Number {
396		&self.first.commitment.block_number
397	}
398	/// Returns the set id at which the equivocation occurred.
399	pub fn set_id(&self) -> ValidatorSetId {
400		self.first.commitment.validator_set_id
401	}
402}
403
404/// Proof showing that an authority voted for a non-canonical chain.
405///
406/// Proving is achieved by providing a proof that contains relevant info about the canonical chain
407/// at `commitment.block_number`. The `commitment` can be checked against this info.
408#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
409pub struct ForkVotingProof<Header: HeaderT, Id: RuntimeAppPublic, AncestryProof> {
410	/// The equivocated vote.
411	pub vote: VoteMessage<Header::Number, Id, Id::Signature>,
412	/// Proof containing info about the canonical chain at `commitment.block_number`.
413	pub ancestry_proof: AncestryProof,
414	/// The header of the block where the ancestry proof was generated
415	pub header: Header,
416}
417
418impl<Header: HeaderT, Id: RuntimeAppPublic> ForkVotingProof<Header, Id, OpaqueValue> {
419	/// Try to decode the `AncestryProof`.
420	pub fn try_into<AncestryProof: Decode>(
421		self,
422	) -> Option<ForkVotingProof<Header, Id, AncestryProof>> {
423		Some(ForkVotingProof::<Header, Id, AncestryProof> {
424			vote: self.vote,
425			ancestry_proof: self.ancestry_proof.decode()?,
426			header: self.header,
427		})
428	}
429}
430
431/// Proof showing that an authority voted for a future block.
432#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
433pub struct FutureBlockVotingProof<Number, Id: RuntimeAppPublic> {
434	/// The equivocated vote.
435	pub vote: VoteMessage<Number, Id, Id::Signature>,
436}
437
438/// Check a commitment signature by encoding the commitment and
439/// verifying the provided signature using the expected authority id.
440pub fn check_commitment_signature<Number, Id>(
441	commitment: &Commitment<Number>,
442	authority_id: &Id,
443	signature: &<Id as RuntimeAppPublic>::Signature,
444) -> bool
445where
446	Id: BeefyAuthorityId,
447	Number: Clone + Encode + PartialEq,
448{
449	let encoded_commitment = commitment.encode();
450	BeefyAuthorityId::verify(authority_id, signature, &encoded_commitment)
451}
452
453/// Verifies the equivocation proof by making sure that both votes target
454/// different blocks and that its signatures are valid.
455pub fn check_double_voting_proof<Number, Id>(
456	report: &DoubleVotingProof<Number, Id, <Id as RuntimeAppPublic>::Signature>,
457) -> bool
458where
459	Id: BeefyAuthorityId + PartialEq,
460	Number: Clone + Encode + PartialEq,
461{
462	let first = &report.first;
463	let second = &report.second;
464
465	// if votes
466	//   come from different authorities,
467	//   are for different rounds,
468	//   have different validator set ids,
469	//   or both votes have the same commitment,
470	//     --> the equivocation is invalid.
471	if first.id != second.id ||
472		first.commitment.block_number != second.commitment.block_number ||
473		first.commitment.validator_set_id != second.commitment.validator_set_id ||
474		first.commitment.payload == second.commitment.payload
475	{
476		return false;
477	}
478
479	// check signatures on both votes are valid
480	let valid_first = check_commitment_signature(&first.commitment, &first.id, &first.signature);
481	let valid_second =
482		check_commitment_signature(&second.commitment, &second.id, &second.signature);
483
484	return valid_first && valid_second;
485}
486
487/// New BEEFY validator set notification hook.
488pub trait OnNewValidatorSet<AuthorityId> {
489	/// Function called by the pallet when BEEFY validator set changes.
490	fn on_new_validator_set(
491		validator_set: &ValidatorSet<AuthorityId>,
492		next_validator_set: &ValidatorSet<AuthorityId>,
493	);
494}
495
496/// No-op implementation of [OnNewValidatorSet].
497impl<AuthorityId> OnNewValidatorSet<AuthorityId> for () {
498	fn on_new_validator_set(_: &ValidatorSet<AuthorityId>, _: &ValidatorSet<AuthorityId>) {}
499}
500
501/// Hook containing helper methods for proving/checking commitment canonicity.
502pub trait AncestryHelper<Header: HeaderT> {
503	/// Type containing proved info about the canonical chain at a certain height.
504	type Proof: Clone + Debug + Decode + Encode + PartialEq + TypeInfo;
505	/// The data needed for validating the proof.
506	type ValidationContext;
507
508	/// Check if the proof is optimal.
509	fn is_proof_optimal(proof: &Self::Proof) -> bool;
510
511	/// Extract the validation context from the provided header.
512	fn extract_validation_context(header: Header) -> Option<Self::ValidationContext>;
513
514	/// Check if a commitment is pointing to a header on a non-canonical chain
515	/// against a canonicity proof generated at the same header height.
516	fn is_non_canonical(
517		commitment: &Commitment<Header::Number>,
518		proof: Self::Proof,
519		context: Self::ValidationContext,
520	) -> bool;
521}
522
523/// Weight information for the logic in `AncestryHelper`.
524pub trait AncestryHelperWeightInfo<Header: HeaderT>: AncestryHelper<Header> {
525	/// Weight info for the `AncestryHelper::is_proof_optimal()` method.
526	fn is_proof_optimal(proof: &<Self as AncestryHelper<Header>>::Proof) -> Weight;
527
528	/// Weight info for the `AncestryHelper::extract_validation_context()` method.
529	fn extract_validation_context() -> Weight;
530
531	/// Weight info for the `AncestryHelper::is_non_canonical()` method.
532	fn is_non_canonical(proof: &<Self as AncestryHelper<Header>>::Proof) -> Weight;
533}
534
535/// An opaque type used to represent the key ownership proof at the runtime API
536/// boundary. The inner value is an encoded representation of the actual key
537/// ownership proof which will be parameterized when defining the runtime. At
538/// the runtime API boundary this type is unknown and as such we keep this
539/// opaque representation, implementors of the runtime API will have to make
540/// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type.
541pub type OpaqueKeyOwnershipProof = OpaqueValue;
542
543sp_api::decl_runtime_apis! {
544	/// API necessary for BEEFY voters.
545	#[api_version(6)]
546	pub trait BeefyApi<AuthorityId> where
547		AuthorityId : Codec + RuntimeAppPublic,
548	{
549		/// Return the block number where BEEFY consensus is enabled/started
550		fn beefy_genesis() -> Option<NumberFor<Block>>;
551
552		/// Return the current active BEEFY validator set
553		fn validator_set() -> Option<ValidatorSet<AuthorityId>>;
554
555		/// Submits an unsigned extrinsic to report a double voting equivocation. The caller
556		/// must provide the double voting proof and a key ownership proof
557		/// (should be obtained using `generate_key_ownership_proof`). The
558		/// extrinsic will be unsigned and should only be accepted for local
559		/// authorship (not to be broadcast to the network). This method returns
560		/// `None` when creation of the extrinsic fails, e.g. if equivocation
561		/// reporting is disabled for the given runtime (i.e. this method is
562		/// hardcoded to return `None`). Only useful in an offchain context.
563		fn submit_report_double_voting_unsigned_extrinsic(
564			equivocation_proof:
565				DoubleVotingProof<NumberFor<Block>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
566			key_owner_proof: OpaqueKeyOwnershipProof,
567		) -> Option<()>;
568
569		/// Submits an unsigned extrinsic to report a fork voting equivocation. The caller
570		/// must provide the fork voting proof (the ancestry proof should be obtained using
571		/// `generate_ancestry_proof`) and a key ownership proof (should be obtained using
572		/// `generate_key_ownership_proof`). The extrinsic will be unsigned and should only
573		/// be accepted for local authorship (not to be broadcast to the network). This method
574		/// returns `None` when creation of the extrinsic fails, e.g. if equivocation
575		/// reporting is disabled for the given runtime (i.e. this method is
576		/// hardcoded to return `None`). Only useful in an offchain context.
577		fn submit_report_fork_voting_unsigned_extrinsic(
578			equivocation_proof:
579				ForkVotingProof<Block::Header, AuthorityId, OpaqueValue>,
580			key_owner_proof: OpaqueKeyOwnershipProof,
581		) -> Option<()>;
582
583		/// Submits an unsigned extrinsic to report a future block voting equivocation. The caller
584		/// must provide the future block voting proof and a key ownership proof
585		/// (should be obtained using `generate_key_ownership_proof`).
586		/// The extrinsic will be unsigned and should only be accepted for local
587		/// authorship (not to be broadcast to the network). This method returns
588		/// `None` when creation of the extrinsic fails, e.g. if equivocation
589		/// reporting is disabled for the given runtime (i.e. this method is
590		/// hardcoded to return `None`). Only useful in an offchain context.
591		fn submit_report_future_block_voting_unsigned_extrinsic(
592			equivocation_proof:
593				FutureBlockVotingProof<NumberFor<Block>, AuthorityId>,
594			key_owner_proof: OpaqueKeyOwnershipProof,
595		) -> Option<()>;
596
597		/// Generates a proof of key ownership for the given authority in the
598		/// given set. An example usage of this module is coupled with the
599		/// session historical module to prove that a given authority key is
600		/// tied to a given staking identity during a specific session. Proofs
601		/// of key ownership are necessary for submitting equivocation reports.
602		/// NOTE: even though the API takes a `set_id` as parameter the current
603		/// implementations ignores this parameter and instead relies on this
604		/// method being called at the correct block height, i.e. any point at
605		/// which the given set id is live on-chain. Future implementations will
606		/// instead use indexed data through an offchain worker, not requiring
607		/// older states to be available.
608		fn generate_key_ownership_proof(
609			set_id: ValidatorSetId,
610			authority_id: AuthorityId,
611		) -> Option<OpaqueKeyOwnershipProof>;
612	}
613
614}
615
616#[cfg(test)]
617mod tests {
618	use super::*;
619	use sp_application_crypto::ecdsa::{self, Public};
620	use sp_core::crypto::{Pair, Wraps};
621	use sp_crypto_hashing::keccak_256;
622
623	#[test]
624	fn validator_set() {
625		// Empty set not allowed.
626		assert_eq!(ValidatorSet::<Public>::new(vec![], 0), None);
627
628		let alice = ecdsa::Pair::from_string("//Alice", None).unwrap();
629		let set_id = 0;
630		let validators = ValidatorSet::<Public>::new(vec![alice.public()], set_id).unwrap();
631
632		assert_eq!(validators.id(), set_id);
633		assert_eq!(validators.validators(), &vec![alice.public()]);
634	}
635
636	#[test]
637	fn ecdsa_beefy_verify_works() {
638		let msg = &b"test-message"[..];
639		let (pair, _) = ecdsa_crypto::Pair::generate();
640
641		let signature: ecdsa_crypto::Signature =
642			pair.as_inner_ref().sign_prehashed(&keccak_256(msg)).into();
643
644		// Verification works if same key is used when signing and verifying.
645		assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
646
647		// Other public key doesn't work
648		let (other_pair, _) = ecdsa_crypto::Pair::generate();
649		assert!(!BeefyAuthorityId::verify(&other_pair.public(), &signature, msg,));
650	}
651
652	#[test]
653	#[cfg(feature = "bls-experimental")]
654	fn bls_beefy_verify_works() {
655		let msg = &b"test-message"[..];
656		let (pair, _) = bls_crypto::Pair::generate();
657
658		let signature: bls_crypto::Signature = pair.as_inner_ref().sign(&msg).into();
659
660		// Verification works if same hashing function is used when signing and verifying.
661		assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
662
663		// Other public key doesn't work
664		let (other_pair, _) = bls_crypto::Pair::generate();
665		assert!(!BeefyAuthorityId::verify(&other_pair.public(), &signature, msg,));
666	}
667
668	#[test]
669	#[cfg(feature = "bls-experimental")]
670	fn ecdsa_bls_beefy_verify_works() {
671		let msg = &b"test-message"[..];
672		let (pair, _) = ecdsa_bls_crypto::Pair::generate();
673
674		let signature: ecdsa_bls_crypto::Signature =
675			pair.as_inner_ref().sign_with_hasher::<Keccak256>(&msg).into();
676
677		// Verification works if same hashing function is used when signing and verifying.
678		assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
679
680		// Verification doesn't work if we verify function provided by pair_crypto implementation
681		assert!(!ecdsa_bls_crypto::Pair::verify(&signature, msg, &pair.public()));
682
683		// Other public key doesn't work
684		let (other_pair, _) = ecdsa_bls_crypto::Pair::generate();
685		assert!(!BeefyAuthorityId::verify(&other_pair.public(), &signature, msg,));
686	}
687}