1#![cfg_attr(not(feature = "std"), no_std)]
19#![warn(missing_docs)]
20
21extern crate alloc;
35
36mod commitment;
37mod payload;
38
39pub mod mmr;
40pub mod witness;
41
42#[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;
53use sp_application_crypto::{AppPublic, RuntimeAppPublic};
54use sp_core::H256;
55use sp_runtime::{
56 traits::{Hash, Header as HeaderT, Keccak256, NumberFor},
57 OpaqueValue,
58};
59use sp_weights::Weight;
60
61pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::BEEFY;
63
64pub trait BeefyAuthorityId<MsgHash: Hash>: RuntimeAppPublic {
68 fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool;
72}
73
74pub type BeefySignatureHasher = sp_runtime::traits::Keccak256;
76
77pub trait AuthorityIdBound:
80 Ord
81 + AppPublic
82 + Display
83 + BeefyAuthorityId<BeefySignatureHasher, Signature = Self::BoundedSignature>
84{
85 type BoundedSignature: Debug + Eq + PartialEq + Clone + TypeInfo + Codec + Send + Sync;
87}
88
89pub mod ecdsa_crypto {
100 use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
101 use sp_application_crypto::{app_crypto, ecdsa};
102 use sp_core::crypto::Wraps;
103
104 app_crypto!(ecdsa, KEY_TYPE);
105
106 pub type AuthorityId = Public;
108
109 pub type AuthoritySignature = Signature;
111
112 impl<MsgHash: Hash> BeefyAuthorityId<MsgHash> for AuthorityId
113 where
114 <MsgHash as Hash>::Output: Into<[u8; 32]>,
115 {
116 fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
117 let msg_hash = <MsgHash as Hash>::hash(msg).into();
118 match sp_io::crypto::secp256k1_ecdsa_recover_compressed(
119 signature.as_inner_ref().as_ref(),
120 &msg_hash,
121 ) {
122 Ok(raw_pubkey) => raw_pubkey.as_ref() == AsRef::<[u8]>::as_ref(self),
123 _ => false,
124 }
125 }
126 }
127 impl AuthorityIdBound for AuthorityId {
128 type BoundedSignature = Signature;
129 }
130}
131
132#[cfg(feature = "bls-experimental")]
144pub mod bls_crypto {
145 use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
146 use sp_application_crypto::{app_crypto, bls381};
147 use sp_core::{bls381::Pair as BlsPair, crypto::Wraps, Pair as _};
148
149 app_crypto!(bls381, KEY_TYPE);
150
151 pub type AuthorityId = Public;
153
154 pub type AuthoritySignature = Signature;
156
157 impl<MsgHash: Hash> BeefyAuthorityId<MsgHash> for AuthorityId
158 where
159 <MsgHash as Hash>::Output: Into<[u8; 32]>,
160 {
161 fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
162 BlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())
168 }
169 }
170 impl AuthorityIdBound for AuthorityId {
171 type BoundedSignature = Signature;
172 }
173}
174
175#[cfg(feature = "bls-experimental")]
186pub mod ecdsa_bls_crypto {
187 use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
188 use sp_application_crypto::{app_crypto, ecdsa_bls381};
189 use sp_core::{crypto::Wraps, ecdsa_bls381::Pair as EcdsaBlsPair};
190
191 app_crypto!(ecdsa_bls381, KEY_TYPE);
192
193 pub type AuthorityId = Public;
195
196 pub type AuthoritySignature = Signature;
198
199 impl<H> BeefyAuthorityId<H> for AuthorityId
200 where
201 H: Hash,
202 H::Output: Into<[u8; 32]>,
203 {
204 fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
205 EcdsaBlsPair::verify_with_hasher::<H>(
213 signature.as_inner_ref(),
214 msg,
215 self.as_inner_ref(),
216 )
217 }
218 }
219
220 impl AuthorityIdBound for AuthorityId {
221 type BoundedSignature = Signature;
222 }
223}
224
225pub const BEEFY_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"BEEF";
227
228pub const GENESIS_AUTHORITY_SET_ID: u64 = 0;
230
231pub type ValidatorSetId = u64;
233
234#[derive(Decode, Encode, Debug, PartialEq, Clone, TypeInfo)]
236pub struct ValidatorSet<AuthorityId> {
237 validators: Vec<AuthorityId>,
239 id: ValidatorSetId,
241}
242
243impl<AuthorityId> ValidatorSet<AuthorityId> {
244 pub fn new<I>(validators: I, id: ValidatorSetId) -> Option<Self>
246 where
247 I: IntoIterator<Item = AuthorityId>,
248 {
249 let validators: Vec<AuthorityId> = validators.into_iter().collect();
250 if validators.is_empty() {
251 None
253 } else {
254 Some(Self { validators, id })
255 }
256 }
257
258 pub fn validators(&self) -> &[AuthorityId] {
260 &self.validators
261 }
262
263 pub fn id(&self) -> ValidatorSetId {
265 self.id
266 }
267
268 pub fn len(&self) -> usize {
270 self.validators.len()
271 }
272}
273
274pub type AuthorityIndex = u32;
276
277pub type MmrHashing = Keccak256;
279pub type MmrRootHash = H256;
281
282#[derive(Decode, Encode, TypeInfo)]
284pub enum ConsensusLog<AuthorityId: Codec> {
285 #[codec(index = 1)]
287 AuthoritiesChange(ValidatorSet<AuthorityId>),
288 #[codec(index = 2)]
290 OnDisabled(AuthorityIndex),
291 #[codec(index = 3)]
293 MmrRoot(MmrRootHash),
294}
295
296#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
302pub struct VoteMessage<Number, Id, Signature> {
303 pub commitment: Commitment<Number>,
305 pub id: Id,
307 pub signature: Signature,
309}
310
311#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
317pub struct DoubleVotingProof<Number, Id, Signature> {
318 pub first: VoteMessage<Number, Id, Signature>,
320 pub second: VoteMessage<Number, Id, Signature>,
322}
323
324impl<Number, Id, Signature> DoubleVotingProof<Number, Id, Signature> {
325 pub fn offender_id(&self) -> &Id {
327 &self.first.id
328 }
329 pub fn round_number(&self) -> &Number {
331 &self.first.commitment.block_number
332 }
333 pub fn set_id(&self) -> ValidatorSetId {
335 self.first.commitment.validator_set_id
336 }
337}
338
339#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
344pub struct ForkVotingProof<Header: HeaderT, Id: RuntimeAppPublic, AncestryProof> {
345 pub vote: VoteMessage<Header::Number, Id, Id::Signature>,
347 pub ancestry_proof: AncestryProof,
349 pub header: Header,
351}
352
353impl<Header: HeaderT, Id: RuntimeAppPublic> ForkVotingProof<Header, Id, OpaqueValue> {
354 pub fn try_into<AncestryProof: Decode>(
356 self,
357 ) -> Option<ForkVotingProof<Header, Id, AncestryProof>> {
358 Some(ForkVotingProof::<Header, Id, AncestryProof> {
359 vote: self.vote,
360 ancestry_proof: self.ancestry_proof.decode()?,
361 header: self.header,
362 })
363 }
364}
365
366#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
368pub struct FutureBlockVotingProof<Number, Id: RuntimeAppPublic> {
369 pub vote: VoteMessage<Number, Id, Id::Signature>,
371}
372
373pub fn check_commitment_signature<Number, Id, MsgHash>(
376 commitment: &Commitment<Number>,
377 authority_id: &Id,
378 signature: &<Id as RuntimeAppPublic>::Signature,
379) -> bool
380where
381 Id: BeefyAuthorityId<MsgHash>,
382 Number: Clone + Encode + PartialEq,
383 MsgHash: Hash,
384{
385 let encoded_commitment = commitment.encode();
386 BeefyAuthorityId::<MsgHash>::verify(authority_id, signature, &encoded_commitment)
387}
388
389pub fn check_double_voting_proof<Number, Id, MsgHash>(
392 report: &DoubleVotingProof<Number, Id, <Id as RuntimeAppPublic>::Signature>,
393) -> bool
394where
395 Id: BeefyAuthorityId<MsgHash> + PartialEq,
396 Number: Clone + Encode + PartialEq,
397 MsgHash: Hash,
398{
399 let first = &report.first;
400 let second = &report.second;
401
402 if first.id != second.id ||
409 first.commitment.block_number != second.commitment.block_number ||
410 first.commitment.validator_set_id != second.commitment.validator_set_id ||
411 first.commitment.payload == second.commitment.payload
412 {
413 return false
414 }
415
416 let valid_first = check_commitment_signature(&first.commitment, &first.id, &first.signature);
418 let valid_second =
419 check_commitment_signature(&second.commitment, &second.id, &second.signature);
420
421 return valid_first && valid_second
422}
423
424pub trait OnNewValidatorSet<AuthorityId> {
426 fn on_new_validator_set(
428 validator_set: &ValidatorSet<AuthorityId>,
429 next_validator_set: &ValidatorSet<AuthorityId>,
430 );
431}
432
433impl<AuthorityId> OnNewValidatorSet<AuthorityId> for () {
435 fn on_new_validator_set(_: &ValidatorSet<AuthorityId>, _: &ValidatorSet<AuthorityId>) {}
436}
437
438pub trait AncestryHelper<Header: HeaderT> {
440 type Proof: Clone + Debug + Decode + Encode + PartialEq + TypeInfo;
442 type ValidationContext;
444
445 fn generate_proof(
448 prev_block_number: Header::Number,
449 best_known_block_number: Option<Header::Number>,
450 ) -> Option<Self::Proof>;
451
452 fn is_proof_optimal(proof: &Self::Proof) -> bool;
454
455 fn extract_validation_context(header: Header) -> Option<Self::ValidationContext>;
457
458 fn is_non_canonical(
461 commitment: &Commitment<Header::Number>,
462 proof: Self::Proof,
463 context: Self::ValidationContext,
464 ) -> bool;
465}
466
467pub trait AncestryHelperWeightInfo<Header: HeaderT>: AncestryHelper<Header> {
469 fn is_proof_optimal(proof: &<Self as AncestryHelper<Header>>::Proof) -> Weight;
471
472 fn extract_validation_context() -> Weight;
474
475 fn is_non_canonical(proof: &<Self as AncestryHelper<Header>>::Proof) -> Weight;
477}
478
479pub type OpaqueKeyOwnershipProof = OpaqueValue;
486
487sp_api::decl_runtime_apis! {
488 #[api_version(5)]
490 pub trait BeefyApi<AuthorityId> where
491 AuthorityId : Codec + RuntimeAppPublic,
492 {
493 fn beefy_genesis() -> Option<NumberFor<Block>>;
495
496 fn validator_set() -> Option<ValidatorSet<AuthorityId>>;
498
499 fn submit_report_double_voting_unsigned_extrinsic(
508 equivocation_proof:
509 DoubleVotingProof<NumberFor<Block>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
510 key_owner_proof: OpaqueKeyOwnershipProof,
511 ) -> Option<()>;
512
513 fn submit_report_fork_voting_unsigned_extrinsic(
522 equivocation_proof:
523 ForkVotingProof<Block::Header, AuthorityId, OpaqueValue>,
524 key_owner_proof: OpaqueKeyOwnershipProof,
525 ) -> Option<()>;
526
527 fn submit_report_future_block_voting_unsigned_extrinsic(
536 equivocation_proof:
537 FutureBlockVotingProof<NumberFor<Block>, AuthorityId>,
538 key_owner_proof: OpaqueKeyOwnershipProof,
539 ) -> Option<()>;
540
541 fn generate_key_ownership_proof(
553 set_id: ValidatorSetId,
554 authority_id: AuthorityId,
555 ) -> Option<OpaqueKeyOwnershipProof>;
556
557 fn generate_ancestry_proof(
560 prev_block_number: NumberFor<Block>,
561 best_known_block_number: Option<NumberFor<Block>>,
562 ) -> Option<OpaqueValue>;
563 }
564
565}
566
567#[cfg(test)]
568mod tests {
569 use super::*;
570 use sp_application_crypto::ecdsa::{self, Public};
571 use sp_core::crypto::{Pair, Wraps};
572 use sp_crypto_hashing::{blake2_256, keccak_256};
573 use sp_runtime::traits::{BlakeTwo256, Keccak256};
574
575 #[test]
576 fn validator_set() {
577 assert_eq!(ValidatorSet::<Public>::new(vec![], 0), None);
579
580 let alice = ecdsa::Pair::from_string("//Alice", None).unwrap();
581 let set_id = 0;
582 let validators = ValidatorSet::<Public>::new(vec![alice.public()], set_id).unwrap();
583
584 assert_eq!(validators.id(), set_id);
585 assert_eq!(validators.validators(), &vec![alice.public()]);
586 }
587
588 #[test]
589 fn ecdsa_beefy_verify_works() {
590 let msg = &b"test-message"[..];
591 let (pair, _) = ecdsa_crypto::Pair::generate();
592
593 let keccak_256_signature: ecdsa_crypto::Signature =
594 pair.as_inner_ref().sign_prehashed(&keccak_256(msg)).into();
595
596 let blake2_256_signature: ecdsa_crypto::Signature =
597 pair.as_inner_ref().sign_prehashed(&blake2_256(msg)).into();
598
599 assert!(BeefyAuthorityId::<Keccak256>::verify(&pair.public(), &keccak_256_signature, msg));
601 assert!(BeefyAuthorityId::<BlakeTwo256>::verify(
602 &pair.public(),
603 &blake2_256_signature,
604 msg
605 ));
606 assert!(!BeefyAuthorityId::<Keccak256>::verify(&pair.public(), &blake2_256_signature, msg));
608 assert!(!BeefyAuthorityId::<BlakeTwo256>::verify(
609 &pair.public(),
610 &keccak_256_signature,
611 msg
612 ));
613
614 let (other_pair, _) = ecdsa_crypto::Pair::generate();
616 assert!(!BeefyAuthorityId::<Keccak256>::verify(
617 &other_pair.public(),
618 &keccak_256_signature,
619 msg,
620 ));
621 assert!(!BeefyAuthorityId::<BlakeTwo256>::verify(
622 &other_pair.public(),
623 &blake2_256_signature,
624 msg,
625 ));
626 }
627
628 #[test]
629 #[cfg(feature = "bls-experimental")]
630 fn bls_beefy_verify_works() {
631 let msg = &b"test-message"[..];
632 let (pair, _) = bls_crypto::Pair::generate();
633
634 let signature: bls_crypto::Signature = pair.as_inner_ref().sign(&msg).into();
635
636 assert!(BeefyAuthorityId::<Keccak256>::verify(&pair.public(), &signature, msg));
638
639 let (other_pair, _) = bls_crypto::Pair::generate();
641 assert!(!BeefyAuthorityId::<Keccak256>::verify(&other_pair.public(), &signature, msg,));
642 }
643
644 #[test]
645 #[cfg(feature = "bls-experimental")]
646 fn ecdsa_bls_beefy_verify_works() {
647 let msg = &b"test-message"[..];
648 let (pair, _) = ecdsa_bls_crypto::Pair::generate();
649
650 let signature: ecdsa_bls_crypto::Signature =
651 pair.as_inner_ref().sign_with_hasher::<Keccak256>(&msg).into();
652
653 assert!(BeefyAuthorityId::<Keccak256>::verify(&pair.public(), &signature, msg));
655
656 assert!(!ecdsa_bls_crypto::Pair::verify(&signature, msg, &pair.public()));
658
659 let (other_pair, _) = ecdsa_bls_crypto::Pair::generate();
661 assert!(!BeefyAuthorityId::<Keccak256>::verify(&other_pair.public(), &signature, msg,));
662 }
663}