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_attr(not(feature = "std"), no_std)]
19
#![warn(missing_docs)]
20

            
21
//! A BEEFY+MMR pallet combo.
22
//!
23
//! While both BEEFY and Merkle Mountain Range (MMR) can be used separately,
24
//! these tools were designed to work together in unison.
25
//!
26
//! The pallet provides a standardized MMR Leaf format that can be used
27
//! to bridge BEEFY+MMR-based networks (both standalone and Polkadot-like).
28
//!
29
//! The MMR leaf contains:
30
//! 1. Block number and parent block hash.
31
//! 2. Merkle Tree Root Hash of next BEEFY validator set.
32
//! 3. Arbitrary extra leaf data to be used by downstream pallets to include custom data.
33
//!
34
//! and thanks to versioning can be easily updated in the future.
35

            
36
extern crate alloc;
37

            
38
use sp_runtime::traits::{Convert, Header, Member};
39

            
40
use alloc::vec::Vec;
41
use codec::Decode;
42
use pallet_mmr::{primitives::AncestryProof, LeafDataProvider, ParentNumberAndHash};
43
use sp_consensus_beefy::{
44
	known_payloads,
45
	mmr::{BeefyAuthoritySet, BeefyDataProvider, BeefyNextAuthoritySet, MmrLeaf, MmrLeafVersion},
46
	AncestryHelper, Commitment, ConsensusLog, ValidatorSet as BeefyValidatorSet,
47
};
48

            
49
use frame_support::{crypto::ecdsa::ECDSAExt, traits::Get};
50
use frame_system::pallet_prelude::{BlockNumberFor, HeaderFor};
51

            
52
pub use pallet::*;
53
use sp_runtime::generic::OpaqueDigestItemId;
54

            
55
#[cfg(test)]
56
mod mock;
57
#[cfg(test)]
58
mod tests;
59

            
60
/// A BEEFY consensus digest item with MMR root hash.
61
pub struct DepositBeefyDigest<T>(core::marker::PhantomData<T>);
62

            
63
impl<T> pallet_mmr::primitives::OnNewRoot<sp_consensus_beefy::MmrRootHash> for DepositBeefyDigest<T>
64
where
65
	T: pallet_mmr::Config<Hashing = sp_consensus_beefy::MmrHashing>,
66
	T: pallet_beefy::Config,
67
{
68
194763
	fn on_new_root(root: &sp_consensus_beefy::MmrRootHash) {
69
194763
		let digest = sp_runtime::generic::DigestItem::Consensus(
70
194763
			sp_consensus_beefy::BEEFY_ENGINE_ID,
71
194763
			codec::Encode::encode(&sp_consensus_beefy::ConsensusLog::<
72
194763
				<T as pallet_beefy::Config>::BeefyId,
73
194763
			>::MmrRoot(*root)),
74
194763
		);
75
194763
		frame_system::Pallet::<T>::deposit_log(digest);
76
194763
	}
77
}
78

            
79
/// Convert BEEFY secp256k1 public keys into Ethereum addresses
80
pub struct BeefyEcdsaToEthereum;
81
impl Convert<sp_consensus_beefy::ecdsa_crypto::AuthorityId, Vec<u8>> for BeefyEcdsaToEthereum {
82
16
	fn convert(beefy_id: sp_consensus_beefy::ecdsa_crypto::AuthorityId) -> Vec<u8> {
83
16
		sp_core::ecdsa::Public::from(beefy_id)
84
16
			.to_eth_address()
85
16
			.map(|v| v.to_vec())
86
16
			.map_err(|_| {
87
				log::debug!(target: "runtime::beefy", "Failed to convert BEEFY PublicKey to ETH address!");
88
16
			})
89
16
			.unwrap_or_default()
90
16
	}
91
}
92

            
93
type MerkleRootOf<T> = <<T as pallet_mmr::Config>::Hashing as sp_runtime::traits::Hash>::Output;
94

            
95
#[frame_support::pallet]
96
pub mod pallet {
97
	#![allow(missing_docs)]
98

            
99
	use super::*;
100
	use frame_support::pallet_prelude::*;
101

            
102
	/// BEEFY-MMR pallet.
103
554790
	#[pallet::pallet]
104
	pub struct Pallet<T>(_);
105

            
106
	/// The module's configuration trait.
107
	#[pallet::config]
108
	#[pallet::disable_frame_system_supertrait_check]
109
	pub trait Config: pallet_mmr::Config + pallet_beefy::Config {
110
		/// Current leaf version.
111
		///
112
		/// Specifies the version number added to every leaf that get's appended to the MMR.
113
		/// Read more in [`MmrLeafVersion`] docs about versioning leaves.
114
		type LeafVersion: Get<MmrLeafVersion>;
115

            
116
		/// Convert BEEFY AuthorityId to a form that would end up in the Merkle Tree.
117
		///
118
		/// For instance for ECDSA (secp256k1) we want to store uncompressed public keys (65 bytes)
119
		/// and later to Ethereum Addresses (160 bits) to simplify using them on Ethereum chain,
120
		/// but the rest of the Substrate codebase is storing them compressed (33 bytes) for
121
		/// efficiency reasons.
122
		type BeefyAuthorityToMerkleLeaf: Convert<<Self as pallet_beefy::Config>::BeefyId, Vec<u8>>;
123

            
124
		/// The type expected for the leaf extra data
125
		type LeafExtra: Member + codec::FullCodec;
126

            
127
		/// Retrieve arbitrary data that should be added to the mmr leaf
128
		type BeefyDataProvider: BeefyDataProvider<Self::LeafExtra>;
129
	}
130

            
131
	/// Details of current BEEFY authority set.
132
6
	#[pallet::storage]
133
	pub type BeefyAuthorities<T: Config> =
134
		StorageValue<_, BeefyAuthoritySet<MerkleRootOf<T>>, ValueQuery>;
135

            
136
	/// Details of next BEEFY authority set.
137
	///
138
	/// This storage entry is used as cache for calls to `update_beefy_next_authority_set`.
139
389532
	#[pallet::storage]
140
	pub type BeefyNextAuthorities<T: Config> =
141
		StorageValue<_, BeefyNextAuthoritySet<MerkleRootOf<T>>, ValueQuery>;
142
}
143

            
144
impl<T: Config> LeafDataProvider for Pallet<T> {
145
	type LeafData = MmrLeaf<
146
		BlockNumberFor<T>,
147
		<T as frame_system::Config>::Hash,
148
		MerkleRootOf<T>,
149
		T::LeafExtra,
150
	>;
151

            
152
194763
	fn leaf_data() -> Self::LeafData {
153
194763
		MmrLeaf {
154
194763
			version: T::LeafVersion::get(),
155
194763
			parent_number_and_hash: ParentNumberAndHash::<T>::leaf_data(),
156
194763
			leaf_extra: T::BeefyDataProvider::extra_data(),
157
194763
			beefy_next_authority_set: BeefyNextAuthorities::<T>::get(),
158
194763
		}
159
194763
	}
160
}
161

            
162
impl<T> sp_consensus_beefy::OnNewValidatorSet<<T as pallet_beefy::Config>::BeefyId> for Pallet<T>
163
where
164
	T: pallet::Config,
165
{
166
	/// Compute and cache BEEFY authority sets based on updated BEEFY validator sets.
167
3
	fn on_new_validator_set(
168
3
		current_set: &BeefyValidatorSet<<T as pallet_beefy::Config>::BeefyId>,
169
3
		next_set: &BeefyValidatorSet<<T as pallet_beefy::Config>::BeefyId>,
170
3
	) {
171
3
		let current = Pallet::<T>::compute_authority_set(current_set);
172
3
		let next = Pallet::<T>::compute_authority_set(next_set);
173
3
		// cache the result
174
3
		BeefyAuthorities::<T>::put(&current);
175
3
		BeefyNextAuthorities::<T>::put(&next);
176
3
	}
177
}
178

            
179
impl<T: Config> AncestryHelper<HeaderFor<T>> for Pallet<T>
180
where
181
	T: pallet_mmr::Config<Hashing = sp_consensus_beefy::MmrHashing>,
182
{
183
	type Proof = AncestryProof<MerkleRootOf<T>>;
184
	type ValidationContext = MerkleRootOf<T>;
185

            
186
	fn extract_validation_context(header: HeaderFor<T>) -> Option<Self::ValidationContext> {
187
		// Check if the provided header is canonical.
188
		let expected_hash = frame_system::Pallet::<T>::block_hash(header.number());
189
		if expected_hash != header.hash() {
190
			return None;
191
		}
192

            
193
		// Extract the MMR root from the header digest
194
		header.digest().convert_first(|l| {
195
			l.try_to(OpaqueDigestItemId::Consensus(&sp_consensus_beefy::BEEFY_ENGINE_ID))
196
				.and_then(|log: ConsensusLog<<T as pallet_beefy::Config>::BeefyId>| match log {
197
					ConsensusLog::MmrRoot(mmr_root) => Some(mmr_root),
198
					_ => None,
199
				})
200
		})
201
	}
202

            
203
	fn is_non_canonical(
204
		commitment: &Commitment<BlockNumberFor<T>>,
205
		proof: Self::Proof,
206
		context: Self::ValidationContext,
207
	) -> bool {
208
		let commitment_leaf_count =
209
			match pallet_mmr::Pallet::<T>::block_num_to_leaf_count(commitment.block_number) {
210
				Ok(commitment_leaf_count) => commitment_leaf_count,
211
				Err(_) => {
212
					// We can't prove that the commitment is non-canonical if the
213
					// `commitment.block_number` is invalid.
214
					return false
215
				},
216
			};
217
		if commitment_leaf_count != proof.prev_leaf_count {
218
			// Can't prove that the commitment is non-canonical if the `commitment.block_number`
219
			// doesn't match the ancestry proof.
220
			return false;
221
		}
222

            
223
		let canonical_mmr_root = context;
224
		let canonical_prev_root =
225
			match pallet_mmr::Pallet::<T>::verify_ancestry_proof(canonical_mmr_root, proof) {
226
				Ok(canonical_prev_root) => canonical_prev_root,
227
				Err(_) => {
228
					// Can't prove that the commitment is non-canonical if the proof
229
					// is invalid.
230
					return false
231
				},
232
			};
233

            
234
		let commitment_root =
235
			match commitment.payload.get_decoded::<MerkleRootOf<T>>(&known_payloads::MMR_ROOT_ID) {
236
				Some(commitment_root) => commitment_root,
237
				None => {
238
					// If the commitment doesn't contain any MMR root, while the proof is valid,
239
					// the commitment is invalid
240
					return true
241
				},
242
			};
243

            
244
		canonical_prev_root != commitment_root
245
	}
246
}
247

            
248
impl<T: Config> Pallet<T> {
249
	/// Return the currently active BEEFY authority set proof.
250
	pub fn authority_set_proof() -> BeefyAuthoritySet<MerkleRootOf<T>> {
251
		BeefyAuthorities::<T>::get()
252
	}
253

            
254
	/// Return the next/queued BEEFY authority set proof.
255
	pub fn next_authority_set_proof() -> BeefyNextAuthoritySet<MerkleRootOf<T>> {
256
		BeefyNextAuthorities::<T>::get()
257
	}
258

            
259
	/// Returns details of a BEEFY authority set.
260
	///
261
	/// Details contain authority set id, authority set length and a merkle root,
262
	/// constructed from uncompressed secp256k1 public keys converted to Ethereum addresses
263
	/// of the next BEEFY authority set.
264
6
	fn compute_authority_set(
265
6
		validator_set: &BeefyValidatorSet<<T as pallet_beefy::Config>::BeefyId>,
266
6
	) -> BeefyAuthoritySet<MerkleRootOf<T>> {
267
6
		let id = validator_set.id();
268
6
		let beefy_addresses = validator_set
269
6
			.validators()
270
6
			.into_iter()
271
6
			.cloned()
272
6
			.map(T::BeefyAuthorityToMerkleLeaf::convert)
273
6
			.collect::<Vec<_>>();
274
6
		let default_eth_addr = [0u8; 20];
275
6
		let len = beefy_addresses.len() as u32;
276
6
		let uninitialized_addresses = beefy_addresses
277
6
			.iter()
278
24
			.filter(|&addr| addr.as_slice().eq(&default_eth_addr))
279
6
			.count();
280
6
		if uninitialized_addresses > 0 {
281
			log::error!(
282
				target: "runtime::beefy",
283
				"Failed to convert {} out of {} BEEFY PublicKeys to ETH addresses!",
284
				uninitialized_addresses,
285
				len,
286
			);
287
6
		}
288
6
		let keyset_commitment = binary_merkle_tree::merkle_root::<
289
6
			<T as pallet_mmr::Config>::Hashing,
290
6
			_,
291
6
		>(beefy_addresses)
292
6
		.into();
293
6
		BeefyAuthoritySet { id, len, keyset_commitment }
294
6
	}
295
}
296

            
297
sp_api::decl_runtime_apis! {
298
	/// API useful for BEEFY light clients.
299
	pub trait BeefyMmrApi<H>
300
	where
301
		BeefyAuthoritySet<H>: Decode,
302
	{
303
		/// Return the currently active BEEFY authority set proof.
304
		fn authority_set_proof() -> BeefyAuthoritySet<H>;
305

            
306
		/// Return the next/queued BEEFY authority set proof.
307
		fn next_authority_set_proof() -> BeefyNextAuthoritySet<H>;
308
	}
309
}