1use codec::Decode;
19use log::warn;
20
21use sp_application_crypto::{key_types::BEEFY as BEEFY_KEY_TYPE, AppCrypto, RuntimeAppPublic};
22#[cfg(feature = "bls-experimental")]
23use sp_core::ecdsa_bls381;
24use sp_core::{ecdsa, keccak_256};
25
26use sp_keystore::KeystorePtr;
27use std::marker::PhantomData;
28
29use sp_consensus_beefy::{AuthorityIdBound, BeefyAuthorityId, BeefySignatureHasher};
30
31use crate::{error, LOG_TARGET};
32
33pub(crate) struct BeefyKeystore<AuthorityId: AuthorityIdBound>(
37 Option<KeystorePtr>,
38 PhantomData<fn() -> AuthorityId>,
39);
40
41impl<AuthorityId: AuthorityIdBound> BeefyKeystore<AuthorityId> {
42 pub fn authority_id(&self, keys: &[AuthorityId]) -> Option<AuthorityId> {
49 let store = self.0.clone()?;
50
51 let public: Vec<AuthorityId> = keys
53 .iter()
54 .filter(|k| {
55 store
56 .has_keys(&[(<AuthorityId as RuntimeAppPublic>::to_raw_vec(k), BEEFY_KEY_TYPE)])
57 })
58 .cloned()
59 .collect();
60
61 if public.len() > 1 {
62 warn!(
63 target: LOG_TARGET,
64 "🥩 Multiple private keys found for: {:?} ({})",
65 public,
66 public.len()
67 );
68 }
69
70 public.get(0).cloned()
71 }
72
73 pub fn sign(
79 &self,
80 public: &AuthorityId,
81 message: &[u8],
82 ) -> Result<<AuthorityId as RuntimeAppPublic>::Signature, error::Error> {
83 let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into()))?;
84
85 let signature_byte_array: Vec<u8> = match <AuthorityId as AppCrypto>::CRYPTO_ID {
88 ecdsa::CRYPTO_ID => {
89 let msg_hash = keccak_256(message);
90 let public: ecdsa::Public = ecdsa::Public::try_from(public.as_slice()).unwrap();
91
92 let sig = store
93 .ecdsa_sign_prehashed(BEEFY_KEY_TYPE, &public, &msg_hash)
94 .map_err(|e| error::Error::Keystore(e.to_string()))?
95 .ok_or_else(|| {
96 error::Error::Signature("ecdsa_sign_prehashed() failed".to_string())
97 })?;
98 let sig_ref: &[u8] = sig.as_ref();
99 sig_ref.to_vec()
100 },
101
102 #[cfg(feature = "bls-experimental")]
103 ecdsa_bls381::CRYPTO_ID => {
104 let public: ecdsa_bls381::Public =
105 ecdsa_bls381::Public::try_from(public.as_slice()).unwrap();
106 let sig = store
107 .ecdsa_bls381_sign_with_keccak256(BEEFY_KEY_TYPE, &public, &message)
108 .map_err(|e| error::Error::Keystore(e.to_string()))?
109 .ok_or_else(|| error::Error::Signature("bls381_sign() failed".to_string()))?;
110 let sig_ref: &[u8] = sig.as_ref();
111 sig_ref.to_vec()
112 },
113
114 _ => Err(error::Error::Keystore("key type is not supported by BEEFY Keystore".into()))?,
115 };
116
117 let signature = <AuthorityId as RuntimeAppPublic>::Signature::decode(
119 &mut signature_byte_array.as_slice(),
120 )
121 .map_err(|_| {
122 error::Error::Signature(format!(
123 "invalid signature {:?} for key {:?}",
124 signature_byte_array, public
125 ))
126 })?;
127
128 Ok(signature)
129 }
130
131 pub fn public_keys(&self) -> Result<Vec<AuthorityId>, error::Error> {
134 let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into()))?;
135
136 let pk = match <AuthorityId as AppCrypto>::CRYPTO_ID {
137 ecdsa::CRYPTO_ID => store
138 .ecdsa_public_keys(BEEFY_KEY_TYPE)
139 .drain(..)
140 .map(|pk| AuthorityId::try_from(pk.as_ref()))
141 .collect::<Result<Vec<_>, _>>()
142 .or_else(|_| {
143 Err(error::Error::Keystore(
144 "unable to convert public key into authority id".into(),
145 ))
146 }),
147
148 #[cfg(feature = "bls-experimental")]
149 ecdsa_bls381::CRYPTO_ID => store
150 .ecdsa_bls381_public_keys(BEEFY_KEY_TYPE)
151 .drain(..)
152 .map(|pk| AuthorityId::try_from(pk.as_ref()))
153 .collect::<Result<Vec<_>, _>>()
154 .or_else(|_| {
155 Err(error::Error::Keystore(
156 "unable to convert public key into authority id".into(),
157 ))
158 }),
159
160 _ => Err(error::Error::Keystore("key type is not supported by BEEFY Keystore".into())),
161 };
162
163 pk
164 }
165
166 pub fn verify(
170 public: &AuthorityId,
171 sig: &<AuthorityId as RuntimeAppPublic>::Signature,
172 message: &[u8],
173 ) -> bool {
174 BeefyAuthorityId::<BeefySignatureHasher>::verify(public, sig, message)
175 }
176}
177
178impl<AuthorityId: AuthorityIdBound> From<Option<KeystorePtr>> for BeefyKeystore<AuthorityId> {
179 fn from(store: Option<KeystorePtr>) -> BeefyKeystore<AuthorityId> {
180 BeefyKeystore(store, PhantomData)
181 }
182}
183
184#[cfg(test)]
185pub mod tests {
186 #[cfg(feature = "bls-experimental")]
187 use sp_consensus_beefy::ecdsa_bls_crypto;
188 use sp_consensus_beefy::{
189 ecdsa_crypto,
190 test_utils::{BeefySignerAuthority, Keyring},
191 };
192 use sp_core::Pair as PairT;
193 use sp_keystore::{testing::MemoryKeystore, Keystore};
194
195 use super::*;
196 use crate::error::Error;
197
198 fn keystore() -> KeystorePtr {
199 MemoryKeystore::new().into()
200 }
201
202 fn pair_verify_should_work<
203 AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
204 >()
205 where
206 <AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
207 Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
208 <AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
209 {
210 let msg = b"I am Alice!";
211 let sig = Keyring::<AuthorityId>::Alice.sign(b"I am Alice!");
212
213 assert!(<AuthorityId as BeefyAuthorityId<BeefySignatureHasher>>::verify(
214 &Keyring::Alice.public(),
215 &sig,
216 &msg.as_slice(),
217 ));
218
219 assert!(!<AuthorityId as BeefyAuthorityId<BeefySignatureHasher>>::verify(
221 &Keyring::Bob.public(),
222 &sig,
223 &msg.as_slice(),
224 ));
225
226 let msg = b"I am not Alice!";
227
228 assert!(!<AuthorityId as BeefyAuthorityId<BeefySignatureHasher>>::verify(
230 &Keyring::Alice.public(),
231 &sig,
232 &msg.as_slice(),
233 ));
234 }
235
236 fn generate_in_store<AuthorityId>(
238 store: KeystorePtr,
239 key_type: sp_application_crypto::KeyTypeId,
240 owner: Option<Keyring<AuthorityId>>,
241 ) -> AuthorityId
242 where
243 AuthorityId:
244 AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
245 <AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<BeefySignatureHasher>,
246 <AuthorityId as RuntimeAppPublic>::Signature:
247 Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
248 {
249 let optional_seed: Option<String> = owner.map(|owner| owner.to_seed());
250
251 match <AuthorityId as AppCrypto>::CRYPTO_ID {
252 ecdsa::CRYPTO_ID => {
253 let pk = store.ecdsa_generate_new(key_type, optional_seed.as_deref()).ok().unwrap();
254 AuthorityId::decode(&mut pk.as_ref()).unwrap()
255 },
256 #[cfg(feature = "bls-experimental")]
257 ecdsa_bls381::CRYPTO_ID => {
258 let pk = store
259 .ecdsa_bls381_generate_new(key_type, optional_seed.as_deref())
260 .ok()
261 .unwrap();
262 AuthorityId::decode(&mut pk.as_ref()).unwrap()
263 },
264 _ => panic!("Requested CRYPTO_ID is not supported by the BEEFY Keyring"),
265 }
266 }
267
268 #[test]
269 fn pair_verify_should_work_ecdsa() {
270 pair_verify_should_work::<ecdsa_crypto::AuthorityId>();
271 }
272
273 #[cfg(feature = "bls-experimental")]
274 #[test]
275 fn pair_verify_should_work_ecdsa_n_bls() {
276 pair_verify_should_work::<ecdsa_bls_crypto::AuthorityId>();
277 }
278
279 fn pair_works<
280 AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
281 >()
282 where
283 <AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
284 Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
285 <AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
286 {
287 let want = <AuthorityId as AppCrypto>::Pair::from_string("//Alice", None)
288 .expect("Pair failed")
289 .to_raw_vec();
290 let got = Keyring::<AuthorityId>::Alice.pair().to_raw_vec();
291 assert_eq!(want, got);
292
293 let want = <AuthorityId as AppCrypto>::Pair::from_string("//Bob", None)
294 .expect("Pair failed")
295 .to_raw_vec();
296 let got = Keyring::<AuthorityId>::Bob.pair().to_raw_vec();
297 assert_eq!(want, got);
298
299 let want = <AuthorityId as AppCrypto>::Pair::from_string("//Charlie", None)
300 .expect("Pair failed")
301 .to_raw_vec();
302 let got = Keyring::<AuthorityId>::Charlie.pair().to_raw_vec();
303 assert_eq!(want, got);
304
305 let want = <AuthorityId as AppCrypto>::Pair::from_string("//Dave", None)
306 .expect("Pair failed")
307 .to_raw_vec();
308 let got = Keyring::<AuthorityId>::Dave.pair().to_raw_vec();
309 assert_eq!(want, got);
310
311 let want = <AuthorityId as AppCrypto>::Pair::from_string("//Eve", None)
312 .expect("Pair failed")
313 .to_raw_vec();
314 let got = Keyring::<AuthorityId>::Eve.pair().to_raw_vec();
315 assert_eq!(want, got);
316
317 let want = <AuthorityId as AppCrypto>::Pair::from_string("//Ferdie", None)
318 .expect("Pair failed")
319 .to_raw_vec();
320 let got = Keyring::<AuthorityId>::Ferdie.pair().to_raw_vec();
321 assert_eq!(want, got);
322
323 let want = <AuthorityId as AppCrypto>::Pair::from_string("//One", None)
324 .expect("Pair failed")
325 .to_raw_vec();
326 let got = Keyring::<AuthorityId>::One.pair().to_raw_vec();
327 assert_eq!(want, got);
328
329 let want = <AuthorityId as AppCrypto>::Pair::from_string("//Two", None)
330 .expect("Pair failed")
331 .to_raw_vec();
332 let got = Keyring::<AuthorityId>::Two.pair().to_raw_vec();
333 assert_eq!(want, got);
334 }
335
336 #[test]
337 fn ecdsa_pair_works() {
338 pair_works::<ecdsa_crypto::AuthorityId>();
339 }
340
341 #[cfg(feature = "bls-experimental")]
342 #[test]
343 fn ecdsa_n_bls_pair_works() {
344 pair_works::<ecdsa_bls_crypto::AuthorityId>();
345 }
346
347 fn authority_id_works<
348 AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
349 >()
350 where
351 <AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
352 Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
353 <AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
354 {
355 let store = keystore();
356
357 generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Alice));
358
359 let alice = Keyring::<AuthorityId>::Alice.public();
360
361 let bob = Keyring::Bob.public();
362 let charlie = Keyring::Charlie.public();
363
364 let beefy_store: BeefyKeystore<AuthorityId> = Some(store).into();
365
366 let mut keys = vec![bob, charlie];
367
368 let id = beefy_store.authority_id(keys.as_slice());
369 assert!(id.is_none());
370
371 keys.push(alice.clone());
372
373 let id = beefy_store.authority_id(keys.as_slice()).unwrap();
374 assert_eq!(id, alice);
375 }
376
377 #[test]
378 fn authority_id_works_for_ecdsa() {
379 authority_id_works::<ecdsa_crypto::AuthorityId>();
380 }
381
382 #[cfg(feature = "bls-experimental")]
383 #[test]
384 fn authority_id_works_for_ecdsa_n_bls() {
385 authority_id_works::<ecdsa_bls_crypto::AuthorityId>();
386 }
387
388 fn sign_works<
389 AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
390 >()
391 where
392 <AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
393 Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
394 <AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
395 {
396 let store = keystore();
397
398 generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Alice));
399
400 let alice = Keyring::Alice.public();
401
402 let store: BeefyKeystore<AuthorityId> = Some(store).into();
403
404 let msg = b"are you involved or committed?";
405
406 let sig1 = store.sign(&alice, msg).unwrap();
407 let sig2 = Keyring::<AuthorityId>::Alice.sign(msg);
408
409 assert_eq!(sig1, sig2);
410 }
411
412 #[test]
413 fn sign_works_for_ecdsa() {
414 sign_works::<ecdsa_crypto::AuthorityId>();
415 }
416
417 #[cfg(feature = "bls-experimental")]
418 #[test]
419 fn sign_works_for_ecdsa_n_bls() {
420 sign_works::<ecdsa_bls_crypto::AuthorityId>();
421 }
422
423 fn sign_error<
424 AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
425 >(
426 expected_error_message: &str,
427 ) where
428 <AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
429 Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
430 <AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
431 {
432 let store = keystore();
433
434 generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Bob));
435
436 let store: BeefyKeystore<AuthorityId> = Some(store).into();
437
438 let alice = Keyring::Alice.public();
439
440 let msg = b"are you involved or committed?";
441 let sig = store.sign(&alice, msg).err().unwrap();
442 let err = Error::Signature(expected_error_message.to_string());
443
444 assert_eq!(sig, err);
445 }
446
447 #[test]
448 fn sign_error_for_ecdsa() {
449 sign_error::<ecdsa_crypto::AuthorityId>("ecdsa_sign_prehashed() failed");
450 }
451
452 #[cfg(feature = "bls-experimental")]
453 #[test]
454 fn sign_error_for_ecdsa_n_bls() {
455 sign_error::<ecdsa_bls_crypto::AuthorityId>("bls381_sign() failed");
456 }
457
458 #[test]
459 fn sign_no_keystore() {
460 let store: BeefyKeystore<ecdsa_crypto::Public> = None.into();
461
462 let alice = Keyring::Alice.public();
463 let msg = b"are you involved or committed";
464
465 let sig = store.sign(&alice, msg).err().unwrap();
466 let err = Error::Keystore("no Keystore".to_string());
467 assert_eq!(sig, err);
468 }
469
470 fn verify_works<
471 AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
472 >()
473 where
474 <AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
475 Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
476 <AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
477 {
478 let store = keystore();
479
480 generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Alice));
481
482 let store: BeefyKeystore<AuthorityId> = Some(store).into();
483
484 let alice = Keyring::Alice.public();
485
486 let msg = b"are you involved or committed?";
488 let sig = store.sign(&alice, msg).unwrap();
489 assert!(BeefyKeystore::verify(&alice, &sig, msg));
490
491 let msg = b"you are just involved";
493 assert!(!BeefyKeystore::verify(&alice, &sig, msg));
494 }
495
496 #[test]
497 fn verify_works_for_ecdsa() {
498 verify_works::<ecdsa_crypto::AuthorityId>();
499 }
500
501 #[cfg(feature = "bls-experimental")]
502 #[test]
503
504 fn verify_works_for_ecdsa_n_bls() {
505 verify_works::<ecdsa_bls_crypto::AuthorityId>();
506 }
507
508 fn public_keys_works<
510 AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
511 >()
512 where
513 <AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
514 Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
515 <AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
516 {
517 const TEST_TYPE: sp_application_crypto::KeyTypeId =
518 sp_application_crypto::KeyTypeId(*b"test");
519
520 let store = keystore();
521
522 let _ = generate_in_store::<AuthorityId>(store.clone(), TEST_TYPE, Some(Keyring::Alice));
524 let _ = generate_in_store::<AuthorityId>(store.clone(), TEST_TYPE, Some(Keyring::Bob));
525
526 let _ =
528 generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Dave));
529 let _ = generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Eve));
530
531 let _ = generate_in_store::<AuthorityId>(store.clone(), TEST_TYPE, None);
532 let _ = generate_in_store::<AuthorityId>(store.clone(), TEST_TYPE, None);
533
534 let key1 = generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, None);
535 let key2 = generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, None);
536
537 let store: BeefyKeystore<AuthorityId> = Some(store).into();
538
539 let keys = store.public_keys().ok().unwrap();
540
541 assert!(keys.len() == 4);
542 assert!(keys.contains(&Keyring::Dave.public()));
543 assert!(keys.contains(&Keyring::Eve.public()));
544 assert!(keys.contains(&key1));
545 assert!(keys.contains(&key2));
546 }
547
548 #[test]
549 fn public_keys_works_for_ecdsa_keystore() {
550 public_keys_works::<ecdsa_crypto::AuthorityId>();
551 }
552
553 #[cfg(feature = "bls-experimental")]
554 #[test]
555
556 fn public_keys_works_for_ecdsa_n_bls() {
557 public_keys_works::<ecdsa_bls_crypto::AuthorityId>();
558 }
559}