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, BeefySignatureHasher, Commitment, DoubleVotingProof,
22	ForkVotingProof, FutureBlockVotingProof, Payload, ValidatorSetId, VoteMessage,
23};
24use sp_application_crypto::{AppCrypto, AppPair, RuntimeAppPublic, Wraps};
25use sp_core::{ecdsa, Pair};
26use sp_runtime::traits::{BlockNumber, Hash, Header as HeaderT};
27
28use codec::Encode;
29use std::{collections::HashMap, marker::PhantomData, sync::LazyLock};
30use strum::IntoEnumIterator;
31
32/// Set of test accounts using [`crate::ecdsa_crypto`] types.
33#[allow(missing_docs)]
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)]
35pub enum Keyring<AuthorityId> {
36	Alice,
37	Bob,
38	Charlie,
39	Dave,
40	Eve,
41	Ferdie,
42	One,
43	Two,
44	_Marker(PhantomData<AuthorityId>),
45}
46
47/// Trait representing BEEFY specific generation and signing behavior of authority id
48///
49/// Accepts custom hashing fn for the message and custom convertor fn for the signer.
50pub trait BeefySignerAuthority<MsgHash: Hash>: AppPair {
51	/// Generate and return signature for `message` using custom hashing `MsgHash`
52	fn sign_with_hasher(&self, message: &[u8]) -> <Self as AppCrypto>::Signature;
53}
54
55impl<MsgHash> BeefySignerAuthority<MsgHash> for <ecdsa_crypto::AuthorityId as AppCrypto>::Pair
56where
57	MsgHash: Hash,
58	<MsgHash as Hash>::Output: Into<[u8; 32]>,
59{
60	fn sign_with_hasher(&self, message: &[u8]) -> <Self as AppCrypto>::Signature {
61		let hashed_message = <MsgHash as Hash>::hash(message).into();
62		self.as_inner_ref().sign_prehashed(&hashed_message).into()
63	}
64}
65
66#[cfg(feature = "bls-experimental")]
67impl<MsgHash> BeefySignerAuthority<MsgHash> for <ecdsa_bls_crypto::AuthorityId as AppCrypto>::Pair
68where
69	MsgHash: Hash,
70	<MsgHash as Hash>::Output: Into<[u8; 32]>,
71{
72	fn sign_with_hasher(&self, message: &[u8]) -> <Self as AppCrypto>::Signature {
73		self.as_inner_ref().sign_with_hasher::<MsgHash>(&message).into()
74	}
75}
76
77/// Implement Keyring functionalities generically over AuthorityId
78impl<AuthorityId> Keyring<AuthorityId>
79where
80	AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
81	<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<BeefySignatureHasher>,
82	<AuthorityId as RuntimeAppPublic>::Signature:
83		Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
84{
85	/// Sign `msg`.
86	pub fn sign(&self, msg: &[u8]) -> <AuthorityId as RuntimeAppPublic>::Signature {
87		let key_pair: <AuthorityId as AppCrypto>::Pair = self.pair();
88		key_pair.sign_with_hasher(&msg).into()
89	}
90
91	/// Return key pair.
92	pub fn pair(&self) -> <AuthorityId as AppCrypto>::Pair {
93		<AuthorityId as AppCrypto>::Pair::from_string(self.to_seed().as_str(), None)
94			.unwrap()
95			.into()
96	}
97
98	/// Return public key.
99	pub fn public(&self) -> AuthorityId {
100		self.pair().public().into()
101	}
102
103	/// Return seed string.
104	pub fn to_seed(&self) -> String {
105		format!("//{}", self)
106	}
107
108	/// Get Keyring from public key.
109	pub fn from_public(who: &AuthorityId) -> Option<Keyring<AuthorityId>> {
110		Self::iter().find(|k| k.public() == *who)
111	}
112}
113
114static PRIVATE_KEYS: LazyLock<HashMap<Keyring<ecdsa_crypto::AuthorityId>, ecdsa_crypto::Pair>> =
115	LazyLock::new(|| Keyring::iter().map(|i| (i.clone(), i.pair())).collect());
116static PUBLIC_KEYS: LazyLock<HashMap<Keyring<ecdsa_crypto::AuthorityId>, ecdsa_crypto::Public>> =
117	LazyLock::new(|| {
118		PRIVATE_KEYS
119			.iter()
120			.map(|(name, pair)| (name.clone(), sp_application_crypto::Pair::public(pair)))
121			.collect()
122	});
123
124impl From<Keyring<ecdsa_crypto::AuthorityId>> for ecdsa_crypto::Pair {
125	fn from(k: Keyring<ecdsa_crypto::AuthorityId>) -> Self {
126		k.pair()
127	}
128}
129
130impl From<Keyring<ecdsa_crypto::AuthorityId>> for ecdsa::Pair {
131	fn from(k: Keyring<ecdsa_crypto::AuthorityId>) -> Self {
132		k.pair().into()
133	}
134}
135
136impl From<Keyring<ecdsa_crypto::AuthorityId>> for ecdsa_crypto::Public {
137	fn from(k: Keyring<ecdsa_crypto::AuthorityId>) -> Self {
138		(*PUBLIC_KEYS).get(&k).cloned().unwrap()
139	}
140}
141
142/// Create a new `VoteMessage` from commitment primitives and keyring
143pub fn signed_vote<Number: BlockNumber>(
144	block_number: Number,
145	payload: Payload,
146	validator_set_id: ValidatorSetId,
147	keyring: &Keyring<ecdsa_crypto::AuthorityId>,
148) -> VoteMessage<Number, ecdsa_crypto::Public, ecdsa_crypto::Signature> {
149	let commitment = Commitment { validator_set_id, block_number, payload };
150	let signature = keyring.sign(&commitment.encode());
151	VoteMessage { commitment, id: keyring.public(), signature }
152}
153
154/// Create a new `DoubleVotingProof` based on given arguments.
155pub fn generate_double_voting_proof(
156	vote1: (u64, Payload, ValidatorSetId, &Keyring<ecdsa_crypto::AuthorityId>),
157	vote2: (u64, Payload, ValidatorSetId, &Keyring<ecdsa_crypto::AuthorityId>),
158) -> DoubleVotingProof<u64, ecdsa_crypto::Public, ecdsa_crypto::Signature> {
159	let first = signed_vote(vote1.0, vote1.1, vote1.2, vote1.3);
160	let second = signed_vote(vote2.0, vote2.1, vote2.2, vote2.3);
161	DoubleVotingProof { first, second }
162}
163
164/// Create a new `ForkVotingProof` based on vote & canonical header.
165pub fn generate_fork_voting_proof<Header: HeaderT<Number = u64>, AncestryProof>(
166	vote: (u64, Payload, ValidatorSetId, &Keyring<ecdsa_crypto::AuthorityId>),
167	ancestry_proof: AncestryProof,
168	header: Header,
169) -> ForkVotingProof<Header, ecdsa_crypto::Public, AncestryProof> {
170	let signed_vote = signed_vote(vote.0, vote.1, vote.2, vote.3);
171	ForkVotingProof { vote: signed_vote, ancestry_proof, header }
172}
173
174/// Create a new `ForkVotingProof` based on vote & canonical header.
175pub fn generate_future_block_voting_proof(
176	vote: (u64, Payload, ValidatorSetId, &Keyring<ecdsa_crypto::AuthorityId>),
177) -> FutureBlockVotingProof<u64, ecdsa_crypto::Public> {
178	let signed_vote = signed_vote(vote.0, vote.1, vote.2, vote.3);
179	FutureBlockVotingProof { vote: signed_vote }
180}