Skip to main content

subsoil/consensus/beefy/
test_utils.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later WITH Classpath-exception-2.0
6
7#[cfg(feature = "bls-experimental")]
8use super::ecdsa_bls_crypto;
9use super::{
10	ecdsa_crypto, AuthorityIdBound, Commitment, DoubleVotingProof, ForkVotingProof,
11	FutureBlockVotingProof, Payload, ValidatorSetId, VoteMessage,
12};
13use crate::application_crypto::{AppCrypto, AppPair, RuntimeAppPublic, Wraps};
14use crate::core::{ecdsa, Pair};
15use crate::runtime::traits::{BlockNumber, Header as HeaderT};
16
17use crate::crypto_hashing::keccak_256;
18use codec::Encode;
19use std::{collections::HashMap, marker::PhantomData, sync::LazyLock};
20use strum::IntoEnumIterator;
21
22/// Set of test accounts using [`crate::ecdsa_crypto`] types.
23#[allow(missing_docs)]
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)]
25pub enum Keyring<AuthorityId> {
26	Alice,
27	Bob,
28	Charlie,
29	Dave,
30	Eve,
31	Ferdie,
32	One,
33	Two,
34	_Marker(PhantomData<AuthorityId>),
35}
36
37/// Trait representing BEEFY specific generation and signing behavior of authority id.
38///
39/// The trait mimics `BeefyAuthorityId` signing, but uses the private key instead of a keystore.
40/// This is needed for testing purposes.
41pub trait BeefySignerAuthority: AppPair {
42	/// Generate and return signature for `message`.
43	fn sign(&self, message: &[u8]) -> <Self as AppCrypto>::Signature;
44}
45
46impl BeefySignerAuthority for <ecdsa_crypto::AuthorityId as AppCrypto>::Pair {
47	fn sign(&self, message: &[u8]) -> <Self as AppCrypto>::Signature {
48		let hashed_message = keccak_256(message);
49		self.as_inner_ref().sign_prehashed(&hashed_message).into()
50	}
51}
52
53#[cfg(feature = "bls-experimental")]
54impl BeefySignerAuthority for <ecdsa_bls_crypto::AuthorityId as AppCrypto>::Pair {
55	fn sign(&self, message: &[u8]) -> <Self as AppCrypto>::Signature {
56		self.as_inner_ref().sign(&message).into()
57	}
58}
59
60/// Implement Keyring functionalities generically over AuthorityId
61impl<AuthorityId> Keyring<AuthorityId>
62where
63	AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
64	<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority,
65	<AuthorityId as RuntimeAppPublic>::Signature:
66		Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
67{
68	/// Sign `msg`.
69	pub fn sign(&self, msg: &[u8]) -> <AuthorityId as RuntimeAppPublic>::Signature {
70		let key_pair: <AuthorityId as AppCrypto>::Pair = self.pair();
71		BeefySignerAuthority::sign(&key_pair, msg).into()
72	}
73
74	/// Return key pair.
75	pub fn pair(&self) -> <AuthorityId as AppCrypto>::Pair {
76		<AuthorityId as AppCrypto>::Pair::from_string(self.to_seed().as_str(), None)
77			.unwrap()
78			.into()
79	}
80
81	/// Return public key.
82	pub fn public(&self) -> AuthorityId {
83		self.pair().public().into()
84	}
85
86	/// Return seed string.
87	pub fn to_seed(&self) -> String {
88		format!("//{}", self)
89	}
90
91	/// Get Keyring from public key.
92	pub fn from_public(who: &AuthorityId) -> Option<Keyring<AuthorityId>> {
93		Self::iter().find(|k| k.public() == *who)
94	}
95}
96
97static PRIVATE_KEYS: LazyLock<HashMap<Keyring<ecdsa_crypto::AuthorityId>, ecdsa_crypto::Pair>> =
98	LazyLock::new(|| Keyring::iter().map(|i| (i.clone(), i.pair())).collect());
99static PUBLIC_KEYS: LazyLock<HashMap<Keyring<ecdsa_crypto::AuthorityId>, ecdsa_crypto::Public>> =
100	LazyLock::new(|| {
101		PRIVATE_KEYS
102			.iter()
103			.map(|(name, pair)| (name.clone(), crate::application_crypto::Pair::public(pair)))
104			.collect()
105	});
106
107impl From<Keyring<ecdsa_crypto::AuthorityId>> for ecdsa_crypto::Pair {
108	fn from(k: Keyring<ecdsa_crypto::AuthorityId>) -> Self {
109		k.pair()
110	}
111}
112
113impl From<Keyring<ecdsa_crypto::AuthorityId>> for ecdsa::Pair {
114	fn from(k: Keyring<ecdsa_crypto::AuthorityId>) -> Self {
115		k.pair().into()
116	}
117}
118
119impl From<Keyring<ecdsa_crypto::AuthorityId>> for ecdsa_crypto::Public {
120	fn from(k: Keyring<ecdsa_crypto::AuthorityId>) -> Self {
121		(*PUBLIC_KEYS).get(&k).cloned().unwrap()
122	}
123}
124
125/// Create a new `VoteMessage` from commitment primitives and keyring
126pub fn signed_vote<Number: BlockNumber>(
127	block_number: Number,
128	payload: Payload,
129	validator_set_id: ValidatorSetId,
130	keyring: &Keyring<ecdsa_crypto::AuthorityId>,
131) -> VoteMessage<Number, ecdsa_crypto::Public, ecdsa_crypto::Signature> {
132	let commitment = Commitment { validator_set_id, block_number, payload };
133	let signature = keyring.sign(&commitment.encode());
134	VoteMessage { commitment, id: keyring.public(), signature }
135}
136
137/// Create a new `DoubleVotingProof` based on given arguments.
138pub fn generate_double_voting_proof(
139	vote1: (u64, Payload, ValidatorSetId, &Keyring<ecdsa_crypto::AuthorityId>),
140	vote2: (u64, Payload, ValidatorSetId, &Keyring<ecdsa_crypto::AuthorityId>),
141) -> DoubleVotingProof<u64, ecdsa_crypto::Public, ecdsa_crypto::Signature> {
142	let first = signed_vote(vote1.0, vote1.1, vote1.2, vote1.3);
143	let second = signed_vote(vote2.0, vote2.1, vote2.2, vote2.3);
144	DoubleVotingProof { first, second }
145}
146
147/// Create a new `ForkVotingProof` based on vote & canonical header.
148pub fn generate_fork_voting_proof<Header: HeaderT<Number = u64>, AncestryProof>(
149	vote: (u64, Payload, ValidatorSetId, &Keyring<ecdsa_crypto::AuthorityId>),
150	ancestry_proof: AncestryProof,
151	header: Header,
152) -> ForkVotingProof<Header, ecdsa_crypto::Public, AncestryProof> {
153	let signed_vote = signed_vote(vote.0, vote.1, vote.2, vote.3);
154	ForkVotingProof { vote: signed_vote, ancestry_proof, header }
155}
156
157/// Create a new `ForkVotingProof` based on vote & canonical header.
158pub fn generate_future_block_voting_proof(
159	vote: (u64, Payload, ValidatorSetId, &Keyring<ecdsa_crypto::AuthorityId>),
160) -> FutureBlockVotingProof<u64, ecdsa_crypto::Public> {
161	let signed_vote = signed_vote(vote.0, vote.1, vote.2, vote.3);
162	FutureBlockVotingProof { vote: signed_vote }
163}