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 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#[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 pub type AuthorityId = Public;
188
189 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 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#[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 pub type AuthorityId = Public;
249
250 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 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
290pub const BEEFY_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"BEEF";
292
293pub const GENESIS_AUTHORITY_SET_ID: u64 = 0;
295
296pub type ValidatorSetId = u64;
298
299#[derive(Decode, Encode, Debug, PartialEq, Clone, TypeInfo)]
301pub struct ValidatorSet<AuthorityId> {
302 validators: Vec<AuthorityId>,
304 id: ValidatorSetId,
306}
307
308impl<AuthorityId> ValidatorSet<AuthorityId> {
309 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 None
318 } else {
319 Some(Self { validators, id })
320 }
321 }
322
323 pub fn validators(&self) -> &[AuthorityId] {
325 &self.validators
326 }
327
328 pub fn id(&self) -> ValidatorSetId {
330 self.id
331 }
332
333 pub fn len(&self) -> usize {
335 self.validators.len()
336 }
337}
338
339pub type AuthorityIndex = u32;
341
342pub type MmrHashing = Keccak256;
344pub type MmrRootHash = H256;
346
347#[derive(Decode, Encode, TypeInfo)]
349pub enum ConsensusLog<AuthorityId: Codec> {
350 #[codec(index = 1)]
352 AuthoritiesChange(ValidatorSet<AuthorityId>),
353 #[codec(index = 2)]
355 OnDisabled(AuthorityIndex),
356 #[codec(index = 3)]
358 MmrRoot(MmrRootHash),
359}
360
361#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
367pub struct VoteMessage<Number, Id, Signature> {
368 pub commitment: Commitment<Number>,
370 pub id: Id,
372 pub signature: Signature,
374}
375
376#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
382pub struct DoubleVotingProof<Number, Id, Signature> {
383 pub first: VoteMessage<Number, Id, Signature>,
385 pub second: VoteMessage<Number, Id, Signature>,
387}
388
389impl<Number, Id, Signature> DoubleVotingProof<Number, Id, Signature> {
390 pub fn offender_id(&self) -> &Id {
392 &self.first.id
393 }
394 pub fn round_number(&self) -> &Number {
396 &self.first.commitment.block_number
397 }
398 pub fn set_id(&self) -> ValidatorSetId {
400 self.first.commitment.validator_set_id
401 }
402}
403
404#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
409pub struct ForkVotingProof<Header: HeaderT, Id: RuntimeAppPublic, AncestryProof> {
410 pub vote: VoteMessage<Header::Number, Id, Id::Signature>,
412 pub ancestry_proof: AncestryProof,
414 pub header: Header,
416}
417
418impl<Header: HeaderT, Id: RuntimeAppPublic> ForkVotingProof<Header, Id, OpaqueValue> {
419 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#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
433pub struct FutureBlockVotingProof<Number, Id: RuntimeAppPublic> {
434 pub vote: VoteMessage<Number, Id, Id::Signature>,
436}
437
438pub 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
453pub 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 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 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
487pub trait OnNewValidatorSet<AuthorityId> {
489 fn on_new_validator_set(
491 validator_set: &ValidatorSet<AuthorityId>,
492 next_validator_set: &ValidatorSet<AuthorityId>,
493 );
494}
495
496impl<AuthorityId> OnNewValidatorSet<AuthorityId> for () {
498 fn on_new_validator_set(_: &ValidatorSet<AuthorityId>, _: &ValidatorSet<AuthorityId>) {}
499}
500
501pub trait AncestryHelper<Header: HeaderT> {
503 type Proof: Clone + Debug + Decode + Encode + PartialEq + TypeInfo;
505 type ValidationContext;
507
508 fn is_proof_optimal(proof: &Self::Proof) -> bool;
510
511 fn extract_validation_context(header: Header) -> Option<Self::ValidationContext>;
513
514 fn is_non_canonical(
517 commitment: &Commitment<Header::Number>,
518 proof: Self::Proof,
519 context: Self::ValidationContext,
520 ) -> bool;
521}
522
523pub trait AncestryHelperWeightInfo<Header: HeaderT>: AncestryHelper<Header> {
525 fn is_proof_optimal(proof: &<Self as AncestryHelper<Header>>::Proof) -> Weight;
527
528 fn extract_validation_context() -> Weight;
530
531 fn is_non_canonical(proof: &<Self as AncestryHelper<Header>>::Proof) -> Weight;
533}
534
535pub type OpaqueKeyOwnershipProof = OpaqueValue;
542
543sp_api::decl_runtime_apis! {
544 #[api_version(6)]
546 pub trait BeefyApi<AuthorityId> where
547 AuthorityId : Codec + RuntimeAppPublic,
548 {
549 fn beefy_genesis() -> Option<NumberFor<Block>>;
551
552 fn validator_set() -> Option<ValidatorSet<AuthorityId>>;
554
555 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 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 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 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 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 assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
646
647 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 assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
662
663 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 assert!(BeefyAuthorityId::verify(&pair.public(), &signature, msg));
679
680 assert!(!ecdsa_bls_crypto::Pair::verify(&signature, msg, &pair.public()));
682
683 let (other_pair, _) = ecdsa_bls_crypto::Pair::generate();
685 assert!(!BeefyAuthorityId::verify(&other_pair.public(), &signature, msg,));
686 }
687}