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;
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
65pub trait BeefyAuthorityId: RuntimeAppPublic {
67 #[cfg(feature = "std")]
69 fn get_all_public_keys_from_store(store: KeystorePtr) -> Vec<impl AsRef<[u8]>>;
70
71 #[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 fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool;
85}
86
87pub trait AuthorityIdBound:
90 Ord + AppPublic + Display + BeefyAuthorityId<Signature = Self::BoundedSignature>
91{
92 type BoundedSignature: Debug + Eq + PartialEq + Clone + TypeInfo + Codec + Send + Sync;
94}
95
96pub 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 pub type AuthorityId = Public;
124
125 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 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#[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 pub type AuthorityId = Public;
194
195 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 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#[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 pub type AuthorityId = Public;
255
256 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 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
296pub const BEEFY_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"BEEF";
298
299pub const GENESIS_AUTHORITY_SET_ID: u64 = 0;
301
302pub type ValidatorSetId = u64;
304
305#[derive(Decode, Encode, Debug, PartialEq, Clone, TypeInfo)]
307pub struct ValidatorSet<AuthorityId> {
308 validators: Vec<AuthorityId>,
310 id: ValidatorSetId,
312}
313
314impl<AuthorityId> ValidatorSet<AuthorityId> {
315 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 None
324 } else {
325 Some(Self { validators, id })
326 }
327 }
328
329 pub fn validators(&self) -> &[AuthorityId] {
331 &self.validators
332 }
333
334 pub fn id(&self) -> ValidatorSetId {
336 self.id
337 }
338
339 pub fn len(&self) -> usize {
341 self.validators.len()
342 }
343}
344
345pub type AuthorityIndex = u32;
347
348pub type MmrHashing = Keccak256;
350pub type MmrRootHash = H256;
352
353#[derive(Decode, Encode, TypeInfo)]
355pub enum ConsensusLog<AuthorityId: Codec> {
356 #[codec(index = 1)]
358 AuthoritiesChange(ValidatorSet<AuthorityId>),
359 #[codec(index = 2)]
361 OnDisabled(AuthorityIndex),
362 #[codec(index = 3)]
364 MmrRoot(MmrRootHash),
365}
366
367#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
373pub struct VoteMessage<Number, Id, Signature> {
374 pub commitment: Commitment<Number>,
376 pub id: Id,
378 pub signature: Signature,
380}
381
382#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
388pub struct DoubleVotingProof<Number, Id, Signature> {
389 pub first: VoteMessage<Number, Id, Signature>,
391 pub second: VoteMessage<Number, Id, Signature>,
393}
394
395impl<Number, Id, Signature> DoubleVotingProof<Number, Id, Signature> {
396 pub fn offender_id(&self) -> &Id {
398 &self.first.id
399 }
400 pub fn round_number(&self) -> &Number {
402 &self.first.commitment.block_number
403 }
404 pub fn set_id(&self) -> ValidatorSetId {
406 self.first.commitment.validator_set_id
407 }
408}
409
410#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
415pub struct ForkVotingProof<Header: HeaderT, Id: RuntimeAppPublic, AncestryProof> {
416 pub vote: VoteMessage<Header::Number, Id, Id::Signature>,
418 pub ancestry_proof: AncestryProof,
420 pub header: Header,
422}
423
424impl<Header: HeaderT, Id: RuntimeAppPublic> ForkVotingProof<Header, Id, OpaqueValue> {
425 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#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
439pub struct FutureBlockVotingProof<Number, Id: RuntimeAppPublic> {
440 pub vote: VoteMessage<Number, Id, Id::Signature>,
442}
443
444pub 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
459pub 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 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 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
493pub trait OnNewValidatorSet<AuthorityId> {
495 fn on_new_validator_set(
497 validator_set: &ValidatorSet<AuthorityId>,
498 next_validator_set: &ValidatorSet<AuthorityId>,
499 );
500}
501
502impl<AuthorityId> OnNewValidatorSet<AuthorityId> for () {
504 fn on_new_validator_set(_: &ValidatorSet<AuthorityId>, _: &ValidatorSet<AuthorityId>) {}
505}
506
507pub trait AncestryHelper<Header: HeaderT> {
509 type Proof: Clone + Debug + Decode + Encode + PartialEq + TypeInfo;
511 type ValidationContext;
513
514 fn is_proof_optimal(proof: &Self::Proof) -> bool;
516
517 fn extract_validation_context(header: Header) -> Option<Self::ValidationContext>;
519
520 fn is_non_canonical(
523 commitment: &Commitment<Header::Number>,
524 proof: Self::Proof,
525 context: Self::ValidationContext,
526 ) -> bool;
527}
528
529pub trait AncestryHelperWeightInfo<Header: HeaderT>: AncestryHelper<Header> {
531 fn is_proof_optimal(proof: &<Self as AncestryHelper<Header>>::Proof) -> Weight;
533
534 fn extract_validation_context() -> Weight;
536
537 fn is_non_canonical(proof: &<Self as AncestryHelper<Header>>::Proof) -> Weight;
539}
540
541pub type OpaqueKeyOwnershipProof = OpaqueValue;
548
549sp_api::decl_runtime_apis! {
550 #[api_version(6)]
552 pub trait BeefyApi<AuthorityId> where
553 AuthorityId : Codec + RuntimeAppPublic,
554 {
555 fn beefy_genesis() -> Option<NumberFor<Block>>;
557
558 fn validator_set() -> Option<ValidatorSet<AuthorityId>>;
560
561 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 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 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 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 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 assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
652
653 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 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 assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
675
676 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 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 assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
717
718 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 assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
734
735 assert!(!ecdsa_bls_crypto::Pair::verify(&signature, msg, &pair.public()));
737
738 let (other_pair, _) = ecdsa_bls_crypto::Pair::generate();
740 assert!(!BeefyAuthorityId::verify(&other_pair.public(), &signature, msg,));
741 }
742}