referrerpolicy=no-referrer-when-downgrade

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 sig_bytes: &[u8] = signature.as_inner_ref().as_ref();
147			// Reject high-S signatures (malleability protection, Ethereum compatibility)
148			let sig_array: &[u8; 65] = match sig_bytes.try_into() {
149				Ok(arr) => arr,
150				Err(_) => return false,
151			};
152			if !sp_core::ecdsa::is_signature_normalized(sig_array) {
153				return false;
154			}
155			let msg_hash = keccak_256(msg);
156			match sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig_array, &msg_hash) {
157				Ok(raw_pubkey) => raw_pubkey.as_ref() == AsRef::<[u8]>::as_ref(self),
158				_ => false,
159			}
160		}
161	}
162	impl AuthorityIdBound for AuthorityId {
163		type BoundedSignature = Signature;
164	}
165}
166
167/// BEEFY cryptographic types for BLS crypto
168///
169/// This module basically introduces four crypto types:
170/// - `bls_crypto::Pair`
171/// - `bls_crypto::Public`
172/// - `bls_crypto::Signature`
173/// - `bls_crypto::AuthorityId`
174///
175/// Your code should use the above types as concrete types for all crypto related
176/// functionality.
177
178#[cfg(feature = "bls-experimental")]
179pub mod bls_crypto {
180	#[cfg(feature = "std")]
181	use super::Vec;
182	use super::{AuthorityIdBound, BeefyAuthorityId, RuntimeAppPublic, BEEFY_KEY_TYPE};
183	#[cfg(feature = "std")]
184	use core::fmt::Debug;
185	use sp_application_crypto::{app_crypto, bls381};
186	use sp_core::{bls381::Pair as BlsPair, crypto::Wraps, ByteArray, Pair as _};
187	#[cfg(feature = "std")]
188	use sp_keystore::KeystorePtr;
189
190	app_crypto!(bls381, BEEFY_KEY_TYPE);
191
192	/// Identity of a BEEFY authority using BLS as its crypto.
193	pub type AuthorityId = Public;
194
195	/// Signature for a BEEFY authority using BLS as its crypto.
196	pub type AuthoritySignature = Signature;
197
198	impl BeefyAuthorityId for AuthorityId {
199		#[cfg(feature = "std")]
200		fn get_all_public_keys_from_store(store: KeystorePtr) -> Vec<impl AsRef<[u8]>> {
201			store.bls381_public_keys(BEEFY_KEY_TYPE)
202		}
203
204		#[cfg(feature = "std")]
205		fn try_sign_with_store(
206			&self,
207			store: sp_keystore::KeystorePtr,
208			msg: &[u8],
209		) -> Result<Option<impl AsRef<[u8]> + Debug>, sp_keystore::Error> {
210			let public = bls381::Public::try_from(self.as_slice()).unwrap();
211			store.bls381_sign(BEEFY_KEY_TYPE, &public, msg)
212		}
213
214		fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
215			// `w3f-bls` library uses IETF hashing standard and as such does not expose
216			// a choice of hash-to-field function.
217			// We are directly calling into the library to avoid introducing new host call.
218			// and because BeefyAuthorityId::verify is being called in the runtime so we don't have
219
220			BlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())
221		}
222	}
223	impl AuthorityIdBound for AuthorityId {
224		type BoundedSignature = Signature;
225	}
226}
227
228/// BEEFY cryptographic types for (ECDSA,BLS) crypto pair
229///
230/// This module basically introduces four crypto types:
231/// - `ecdsa_bls_crypto::Pair`
232/// - `ecdsa_bls_crypto::Public`
233/// - `ecdsa_bls_crypto::Signature`
234/// - `ecdsa_bls_crypto::AuthorityId`
235///
236/// Your code should use the above types as concrete types for all crypto related
237/// functionality.
238#[cfg(feature = "bls-experimental")]
239pub mod ecdsa_bls_crypto {
240	#[cfg(feature = "std")]
241	use super::Vec;
242	use super::{AuthorityIdBound, BeefyAuthorityId, RuntimeAppPublic, BEEFY_KEY_TYPE};
243	#[cfg(feature = "std")]
244	use core::fmt::Debug;
245	use sp_application_crypto::{app_crypto, ecdsa_bls381};
246	use sp_core::{crypto::Wraps, ecdsa_bls381::Pair as EcdsaBlsPair, ByteArray};
247	#[cfg(feature = "std")]
248	use sp_keystore::KeystorePtr;
249	use sp_runtime::traits::Keccak256;
250
251	app_crypto!(ecdsa_bls381, BEEFY_KEY_TYPE);
252
253	/// Identity of a BEEFY authority using (ECDSA,BLS) as its crypto.
254	pub type AuthorityId = Public;
255
256	/// Signature for a BEEFY authority using (ECDSA,BLS) as its crypto.
257	pub type AuthoritySignature = Signature;
258
259	impl BeefyAuthorityId for AuthorityId {
260		#[cfg(feature = "std")]
261		fn get_all_public_keys_from_store(store: KeystorePtr) -> Vec<impl AsRef<[u8]>> {
262			store.ecdsa_bls381_public_keys(BEEFY_KEY_TYPE)
263		}
264
265		#[cfg(feature = "std")]
266		fn try_sign_with_store(
267			&self,
268			store: sp_keystore::KeystorePtr,
269			msg: &[u8],
270		) -> Result<Option<impl AsRef<[u8]> + Debug>, sp_keystore::Error> {
271			let public = ecdsa_bls381::Public::try_from(self.as_slice()).unwrap();
272			store.ecdsa_bls381_sign_with_keccak256(BEEFY_KEY_TYPE, &public, &msg)
273		}
274
275		fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
276			// We can not simply call
277			// `EcdsaBlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())`
278			// because that invokes ECDSA default verification which performs Blake2b hash
279			// which we don't want. This is because ECDSA signatures are meant to be verified
280			// on Ethereum network where Keccak hasher is significantly cheaper than Blake2b.
281			// See Figure 3 of [OnSc21](https://www.scitepress.org/Papers/2021/106066/106066.pdf)
282			// for comparison.
283			EcdsaBlsPair::verify_with_hasher::<Keccak256>(
284				signature.as_inner_ref(),
285				msg,
286				self.as_inner_ref(),
287			)
288		}
289	}
290
291	impl AuthorityIdBound for AuthorityId {
292		type BoundedSignature = Signature;
293	}
294}
295
296/// The `ConsensusEngineId` of BEEFY.
297pub const BEEFY_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"BEEF";
298
299/// Authority set id starts with zero at BEEFY pallet genesis.
300pub const GENESIS_AUTHORITY_SET_ID: u64 = 0;
301
302/// A typedef for validator set id.
303pub type ValidatorSetId = u64;
304
305/// A set of BEEFY authorities, a.k.a. validators.
306#[derive(Decode, Encode, Debug, PartialEq, Clone, TypeInfo)]
307pub struct ValidatorSet<AuthorityId> {
308	/// Public keys of the validator set elements
309	validators: Vec<AuthorityId>,
310	/// Identifier of the validator set
311	id: ValidatorSetId,
312}
313
314impl<AuthorityId> ValidatorSet<AuthorityId> {
315	/// Return a validator set with the given validators and set id.
316	pub fn new<I>(validators: I, id: ValidatorSetId) -> Option<Self>
317	where
318		I: IntoIterator<Item = AuthorityId>,
319	{
320		let validators: Vec<AuthorityId> = validators.into_iter().collect();
321		if validators.is_empty() {
322			// No validators; the set would be empty.
323			None
324		} else {
325			Some(Self { validators, id })
326		}
327	}
328
329	/// Return a reference to the vec of validators.
330	pub fn validators(&self) -> &[AuthorityId] {
331		&self.validators
332	}
333
334	/// Return the validator set id.
335	pub fn id(&self) -> ValidatorSetId {
336		self.id
337	}
338
339	/// Return the number of validators in the set.
340	pub fn len(&self) -> usize {
341		self.validators.len()
342	}
343}
344
345/// The index of an authority.
346pub type AuthorityIndex = u32;
347
348/// The Hashing used within MMR.
349pub type MmrHashing = Keccak256;
350/// The type used to represent an MMR root hash.
351pub type MmrRootHash = H256;
352
353/// A consensus log item for BEEFY.
354#[derive(Decode, Encode, TypeInfo)]
355pub enum ConsensusLog<AuthorityId: Codec> {
356	/// The authorities have changed.
357	#[codec(index = 1)]
358	AuthoritiesChange(ValidatorSet<AuthorityId>),
359	/// Disable the authority with given index.
360	#[codec(index = 2)]
361	OnDisabled(AuthorityIndex),
362	/// MMR root hash.
363	#[codec(index = 3)]
364	MmrRoot(MmrRootHash),
365}
366
367/// BEEFY vote message.
368///
369/// A vote message is a direct vote created by a BEEFY node on every voting round
370/// and is gossiped to its peers.
371// TODO: Remove `Signature` generic type, instead get it from `Id::Signature`.
372#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
373pub struct VoteMessage<Number, Id, Signature> {
374	/// Commit to information extracted from a finalized block
375	pub commitment: Commitment<Number>,
376	/// Node authority id
377	pub id: Id,
378	/// Node signature
379	pub signature: Signature,
380}
381
382/// Proof showing that an authority voted twice in the same round.
383///
384/// One type of misbehavior in BEEFY happens when an authority votes in the same round/block
385/// for different payloads.
386/// Proving is achieved by collecting the signed commitments of conflicting votes.
387#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
388pub struct DoubleVotingProof<Number, Id, Signature> {
389	/// The first vote in the equivocation.
390	pub first: VoteMessage<Number, Id, Signature>,
391	/// The second vote in the equivocation.
392	pub second: VoteMessage<Number, Id, Signature>,
393}
394
395impl<Number, Id, Signature> DoubleVotingProof<Number, Id, Signature> {
396	/// Returns the authority id of the equivocator.
397	pub fn offender_id(&self) -> &Id {
398		&self.first.id
399	}
400	/// Returns the round number at which the equivocation occurred.
401	pub fn round_number(&self) -> &Number {
402		&self.first.commitment.block_number
403	}
404	/// Returns the set id at which the equivocation occurred.
405	pub fn set_id(&self) -> ValidatorSetId {
406		self.first.commitment.validator_set_id
407	}
408}
409
410/// Proof showing that an authority voted for a non-canonical chain.
411///
412/// Proving is achieved by providing a proof that contains relevant info about the canonical chain
413/// at `commitment.block_number`. The `commitment` can be checked against this info.
414#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
415pub struct ForkVotingProof<Header: HeaderT, Id: RuntimeAppPublic, AncestryProof> {
416	/// The equivocated vote.
417	pub vote: VoteMessage<Header::Number, Id, Id::Signature>,
418	/// Proof containing info about the canonical chain at `commitment.block_number`.
419	pub ancestry_proof: AncestryProof,
420	/// The header of the block where the ancestry proof was generated
421	pub header: Header,
422}
423
424impl<Header: HeaderT, Id: RuntimeAppPublic> ForkVotingProof<Header, Id, OpaqueValue> {
425	/// Try to decode the `AncestryProof`.
426	pub fn try_into<AncestryProof: Decode>(
427		self,
428	) -> Option<ForkVotingProof<Header, Id, AncestryProof>> {
429		Some(ForkVotingProof::<Header, Id, AncestryProof> {
430			vote: self.vote,
431			ancestry_proof: self.ancestry_proof.decode()?,
432			header: self.header,
433		})
434	}
435}
436
437/// Proof showing that an authority voted for a future block.
438#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
439pub struct FutureBlockVotingProof<Number, Id: RuntimeAppPublic> {
440	/// The equivocated vote.
441	pub vote: VoteMessage<Number, Id, Id::Signature>,
442}
443
444/// Check a commitment signature by encoding the commitment and
445/// verifying the provided signature using the expected authority id.
446pub fn check_commitment_signature<Number, Id>(
447	commitment: &Commitment<Number>,
448	authority_id: &Id,
449	signature: &<Id as RuntimeAppPublic>::Signature,
450) -> bool
451where
452	Id: BeefyAuthorityId,
453	Number: Clone + Encode + PartialEq,
454{
455	let encoded_commitment = commitment.encode();
456	BeefyAuthorityId::verify(authority_id, signature, &encoded_commitment)
457}
458
459/// Verifies the equivocation proof by making sure that both votes target
460/// different blocks and that its signatures are valid.
461pub fn check_double_voting_proof<Number, Id>(
462	report: &DoubleVotingProof<Number, Id, <Id as RuntimeAppPublic>::Signature>,
463) -> bool
464where
465	Id: BeefyAuthorityId + PartialEq,
466	Number: Clone + Encode + PartialEq,
467{
468	let first = &report.first;
469	let second = &report.second;
470
471	// if votes
472	//   come from different authorities,
473	//   are for different rounds,
474	//   have different validator set ids,
475	//   or both votes have the same commitment,
476	//     --> the equivocation is invalid.
477	if first.id != second.id ||
478		first.commitment.block_number != second.commitment.block_number ||
479		first.commitment.validator_set_id != second.commitment.validator_set_id ||
480		first.commitment.payload == second.commitment.payload
481	{
482		return false;
483	}
484
485	// check signatures on both votes are valid
486	let valid_first = check_commitment_signature(&first.commitment, &first.id, &first.signature);
487	let valid_second =
488		check_commitment_signature(&second.commitment, &second.id, &second.signature);
489
490	return valid_first && valid_second;
491}
492
493/// New BEEFY validator set notification hook.
494pub trait OnNewValidatorSet<AuthorityId> {
495	/// Function called by the pallet when BEEFY validator set changes.
496	fn on_new_validator_set(
497		validator_set: &ValidatorSet<AuthorityId>,
498		next_validator_set: &ValidatorSet<AuthorityId>,
499	);
500}
501
502/// No-op implementation of [OnNewValidatorSet].
503impl<AuthorityId> OnNewValidatorSet<AuthorityId> for () {
504	fn on_new_validator_set(_: &ValidatorSet<AuthorityId>, _: &ValidatorSet<AuthorityId>) {}
505}
506
507/// Hook containing helper methods for proving/checking commitment canonicity.
508pub trait AncestryHelper<Header: HeaderT> {
509	/// Type containing proved info about the canonical chain at a certain height.
510	type Proof: Clone + Debug + Decode + Encode + PartialEq + TypeInfo;
511	/// The data needed for validating the proof.
512	type ValidationContext;
513
514	/// Check if the proof is optimal.
515	fn is_proof_optimal(proof: &Self::Proof) -> bool;
516
517	/// Extract the validation context from the provided header.
518	fn extract_validation_context(header: Header) -> Option<Self::ValidationContext>;
519
520	/// Check if a commitment is pointing to a header on a non-canonical chain
521	/// against a canonicity proof generated at the same header height.
522	fn is_non_canonical(
523		commitment: &Commitment<Header::Number>,
524		proof: Self::Proof,
525		context: Self::ValidationContext,
526	) -> bool;
527}
528
529/// Weight information for the logic in `AncestryHelper`.
530pub trait AncestryHelperWeightInfo<Header: HeaderT>: AncestryHelper<Header> {
531	/// Weight info for the `AncestryHelper::is_proof_optimal()` method.
532	fn is_proof_optimal(proof: &<Self as AncestryHelper<Header>>::Proof) -> Weight;
533
534	/// Weight info for the `AncestryHelper::extract_validation_context()` method.
535	fn extract_validation_context() -> Weight;
536
537	/// Weight info for the `AncestryHelper::is_non_canonical()` method.
538	fn is_non_canonical(proof: &<Self as AncestryHelper<Header>>::Proof) -> Weight;
539}
540
541/// An opaque type used to represent the key ownership proof at the runtime API
542/// boundary. The inner value is an encoded representation of the actual key
543/// ownership proof which will be parameterized when defining the runtime. At
544/// the runtime API boundary this type is unknown and as such we keep this
545/// opaque representation, implementors of the runtime API will have to make
546/// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type.
547pub type OpaqueKeyOwnershipProof = OpaqueValue;
548
549sp_api::decl_runtime_apis! {
550	/// API necessary for BEEFY voters.
551	#[api_version(6)]
552	pub trait BeefyApi<AuthorityId> where
553		AuthorityId : Codec + RuntimeAppPublic,
554	{
555		/// Return the block number where BEEFY consensus is enabled/started
556		fn beefy_genesis() -> Option<NumberFor<Block>>;
557
558		/// Return the current active BEEFY validator set
559		fn validator_set() -> Option<ValidatorSet<AuthorityId>>;
560
561		/// Submits an unsigned extrinsic to report a double voting equivocation. The caller
562		/// must provide the double voting proof and a key ownership proof
563		/// (should be obtained using `generate_key_ownership_proof`). The
564		/// extrinsic will be unsigned and should only be accepted for local
565		/// authorship (not to be broadcast to the network). This method returns
566		/// `None` when creation of the extrinsic fails, e.g. if equivocation
567		/// reporting is disabled for the given runtime (i.e. this method is
568		/// hardcoded to return `None`). Only useful in an offchain context.
569		fn submit_report_double_voting_unsigned_extrinsic(
570			equivocation_proof:
571				DoubleVotingProof<NumberFor<Block>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
572			key_owner_proof: OpaqueKeyOwnershipProof,
573		) -> Option<()>;
574
575		/// Submits an unsigned extrinsic to report a fork voting equivocation. The caller
576		/// must provide the fork voting proof (the ancestry proof should be obtained using
577		/// `generate_ancestry_proof`) and a key ownership proof (should be obtained using
578		/// `generate_key_ownership_proof`). The extrinsic will be unsigned and should only
579		/// be accepted for local authorship (not to be broadcast to the network). This method
580		/// returns `None` when creation of the extrinsic fails, e.g. if equivocation
581		/// reporting is disabled for the given runtime (i.e. this method is
582		/// hardcoded to return `None`). Only useful in an offchain context.
583		fn submit_report_fork_voting_unsigned_extrinsic(
584			equivocation_proof:
585				ForkVotingProof<Block::Header, AuthorityId, OpaqueValue>,
586			key_owner_proof: OpaqueKeyOwnershipProof,
587		) -> Option<()>;
588
589		/// Submits an unsigned extrinsic to report a future block voting equivocation. The caller
590		/// must provide the future block voting proof and a key ownership proof
591		/// (should be obtained using `generate_key_ownership_proof`).
592		/// The extrinsic will be unsigned and should only be accepted for local
593		/// authorship (not to be broadcast to the network). This method returns
594		/// `None` when creation of the extrinsic fails, e.g. if equivocation
595		/// reporting is disabled for the given runtime (i.e. this method is
596		/// hardcoded to return `None`). Only useful in an offchain context.
597		fn submit_report_future_block_voting_unsigned_extrinsic(
598			equivocation_proof:
599				FutureBlockVotingProof<NumberFor<Block>, AuthorityId>,
600			key_owner_proof: OpaqueKeyOwnershipProof,
601		) -> Option<()>;
602
603		/// Generates a proof of key ownership for the given authority in the
604		/// given set. An example usage of this module is coupled with the
605		/// session historical module to prove that a given authority key is
606		/// tied to a given staking identity during a specific session. Proofs
607		/// of key ownership are necessary for submitting equivocation reports.
608		/// NOTE: even though the API takes a `set_id` as parameter the current
609		/// implementations ignores this parameter and instead relies on this
610		/// method being called at the correct block height, i.e. any point at
611		/// which the given set id is live on-chain. Future implementations will
612		/// instead use indexed data through an offchain worker, not requiring
613		/// older states to be available.
614		fn generate_key_ownership_proof(
615			set_id: ValidatorSetId,
616			authority_id: AuthorityId,
617		) -> Option<OpaqueKeyOwnershipProof>;
618	}
619
620}
621
622#[cfg(test)]
623mod tests {
624	use super::*;
625	use sp_application_crypto::ecdsa::{self, Public};
626	use sp_core::crypto::{Pair, Wraps};
627	use sp_crypto_hashing::keccak_256;
628
629	#[test]
630	fn validator_set() {
631		// Empty set not allowed.
632		assert_eq!(ValidatorSet::<Public>::new(vec![], 0), None);
633
634		let alice = ecdsa::Pair::from_string("//Alice", None).unwrap();
635		let set_id = 0;
636		let validators = ValidatorSet::<Public>::new(vec![alice.public()], set_id).unwrap();
637
638		assert_eq!(validators.id(), set_id);
639		assert_eq!(validators.validators(), &vec![alice.public()]);
640	}
641
642	#[test]
643	fn ecdsa_beefy_verify_works() {
644		let msg = &b"test-message"[..];
645		let (pair, _) = ecdsa_crypto::Pair::generate();
646
647		let signature: ecdsa_crypto::Signature =
648			pair.as_inner_ref().sign_prehashed(&keccak_256(msg)).into();
649
650		// Verification works if same key is used when signing and verifying.
651		assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
652
653		// Other public key doesn't work
654		let (other_pair, _) = ecdsa_crypto::Pair::generate();
655		assert!(!BeefyAuthorityId::verify(&other_pair.public(), &signature, msg,));
656	}
657
658	#[test]
659	fn ecdsa_beefy_rejects_high_s_signature() {
660		// secp256k1 curve order N
661		let order: [u8; 32] = [
662			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
663			0xff, 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c,
664			0xd0, 0x36, 0x41, 0x41,
665		];
666
667		let msg = &b"test-message"[..];
668		let (pair, _) = ecdsa_crypto::Pair::generate();
669
670		let signature: ecdsa_crypto::Signature =
671			pair.as_inner_ref().sign_prehashed(&keccak_256(msg)).into();
672
673		// Valid low-S signature should verify
674		assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
675
676		// Construct a high-S malleable variant: s' = order - s, v' = v ^ 1
677		let sig_bytes: &[u8] = signature.as_inner_ref().as_ref();
678		let s_bytes: [u8; 32] = sig_bytes[32..64].try_into().unwrap();
679		let mut s_prime = [0u8; 32];
680		let mut borrow = 0i16;
681		for i in (0..32).rev() {
682			let diff = order[i] as i16 - s_bytes[i] as i16 - borrow;
683			if diff < 0 {
684				s_prime[i] = (diff + 256) as u8;
685				borrow = 1;
686			} else {
687				s_prime[i] = diff as u8;
688				borrow = 0;
689			}
690		}
691
692		let mut malleable_bytes = [0u8; 65];
693		malleable_bytes[0..32].copy_from_slice(&sig_bytes[0..32]);
694		malleable_bytes[32..64].copy_from_slice(&s_prime);
695		malleable_bytes[64] = sig_bytes[64] ^ 1;
696
697		let malleable_sig =
698			ecdsa_crypto::Signature::from(sp_core::ecdsa::Signature::from_raw(malleable_bytes));
699
700		// High-S signature should be rejected
701		assert!(
702			!BeefyAuthorityId::verify(&pair.public(), &malleable_sig, msg),
703			"high-S BEEFY signature should be rejected"
704		);
705	}
706
707	#[test]
708	#[cfg(feature = "bls-experimental")]
709	fn bls_beefy_verify_works() {
710		let msg = &b"test-message"[..];
711		let (pair, _) = bls_crypto::Pair::generate();
712
713		let signature: bls_crypto::Signature = pair.as_inner_ref().sign(&msg).into();
714
715		// Verification works if same hashing function is used when signing and verifying.
716		assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
717
718		// Other public key doesn't work
719		let (other_pair, _) = bls_crypto::Pair::generate();
720		assert!(!BeefyAuthorityId::verify(&other_pair.public(), &signature, msg,));
721	}
722
723	#[test]
724	#[cfg(feature = "bls-experimental")]
725	fn ecdsa_bls_beefy_verify_works() {
726		let msg = &b"test-message"[..];
727		let (pair, _) = ecdsa_bls_crypto::Pair::generate();
728
729		let signature: ecdsa_bls_crypto::Signature =
730			pair.as_inner_ref().sign_with_hasher::<Keccak256>(&msg).into();
731
732		// Verification works if same hashing function is used when signing and verifying.
733		assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
734
735		// Verification doesn't work if we verify function provided by pair_crypto implementation
736		assert!(!ecdsa_bls_crypto::Pair::verify(&signature, msg, &pair.public()));
737
738		// Other public key doesn't work
739		let (other_pair, _) = ecdsa_bls_crypto::Pair::generate();
740		assert!(!BeefyAuthorityId::verify(&other_pair.public(), &signature, msg,));
741	}
742}