referrerpolicy=no-referrer-when-downgrade

sp_consensus_beefy/
test_utils.rs

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