1use crate::{ecdsa_crypto::AuthorityId, ConsensusLog, MmrRootHash, BEEFY_ENGINE_ID};
30use alloc::vec::Vec;
31use codec::{Decode, Encode, MaxEncodedLen};
32use scale_info::TypeInfo;
33use sp_runtime::{
34 generic::OpaqueDigestItemId,
35 traits::{Block, Header},
36};
37
38pub trait BeefyDataProvider<ExtraData> {
40 fn extra_data() -> ExtraData;
42}
43
44impl BeefyDataProvider<Vec<u8>> for () {
46 fn extra_data() -> Vec<u8> {
47 Vec::new()
48 }
49}
50
51#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
53pub struct MmrLeaf<BlockNumber, Hash, MerkleRoot, ExtraData> {
54 pub version: MmrLeafVersion,
59 pub parent_number_and_hash: (BlockNumber, Hash),
61 pub beefy_next_authority_set: BeefyNextAuthoritySet<MerkleRoot>,
63 pub leaf_extra: ExtraData,
66}
67
68#[derive(Debug, Default, PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
82pub struct MmrLeafVersion(u8);
83impl MmrLeafVersion {
84 pub fn new(major: u8, minor: u8) -> Self {
88 if major > 0b111 || minor > 0b11111 {
89 panic!("Version components are too big.");
90 }
91 let version = (major << 5) + minor;
92 Self(version)
93 }
94
95 pub fn split(&self) -> (u8, u8) {
97 let major = self.0 >> 5;
98 let minor = self.0 & 0b11111;
99 (major, minor)
100 }
101}
102
103#[derive(Debug, Default, PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106pub struct BeefyAuthoritySet<AuthoritySetCommitment> {
107 pub id: crate::ValidatorSetId,
113 pub len: u32,
119
120 pub keyset_commitment: AuthoritySetCommitment,
132}
133
134pub type BeefyNextAuthoritySet<MerkleRoot> = BeefyAuthoritySet<MerkleRoot>;
136
137pub fn find_mmr_root_digest<B: Block>(header: &B::Header) -> Option<MmrRootHash> {
139 let id = OpaqueDigestItemId::Consensus(&BEEFY_ENGINE_ID);
140
141 let filter = |log: ConsensusLog<AuthorityId>| match log {
142 ConsensusLog::MmrRoot(root) => Some(root),
143 _ => None,
144 };
145 header.digest().convert_first(|l| l.try_to(id).and_then(filter))
146}
147
148#[cfg(feature = "std")]
149pub use mmr_root_provider::MmrRootProvider;
150#[cfg(feature = "std")]
151mod mmr_root_provider {
152 use super::*;
153 use crate::{known_payloads, payload::PayloadProvider, Payload};
154 use alloc::sync::Arc;
155 use core::marker::PhantomData;
156 use sp_api::ProvideRuntimeApi;
157 use sp_mmr_primitives::MmrApi;
158 use sp_runtime::traits::NumberFor;
159
160 pub struct MmrRootProvider<B, R> {
164 runtime: Arc<R>,
165 _phantom: PhantomData<B>,
166 }
167
168 impl<B, R> Clone for MmrRootProvider<B, R> {
169 fn clone(&self) -> Self {
170 Self { runtime: self.runtime.clone(), _phantom: PhantomData }
171 }
172 }
173
174 impl<B, R> MmrRootProvider<B, R>
175 where
176 B: Block,
177 R: ProvideRuntimeApi<B>,
178 R::Api: MmrApi<B, MmrRootHash, NumberFor<B>>,
179 {
180 pub fn new(runtime: Arc<R>) -> Self {
182 Self { runtime, _phantom: PhantomData }
183 }
184
185 fn mmr_root_from_digest_or_runtime(&self, header: &B::Header) -> Option<MmrRootHash> {
187 find_mmr_root_digest::<B>(header).or_else(|| {
188 self.runtime.runtime_api().mmr_root(header.hash()).ok().and_then(|r| r.ok())
189 })
190 }
191 }
192
193 impl<B: Block, R> PayloadProvider<B> for MmrRootProvider<B, R>
194 where
195 B: Block,
196 R: ProvideRuntimeApi<B>,
197 R::Api: MmrApi<B, MmrRootHash, NumberFor<B>>,
198 {
199 fn payload(&self, header: &B::Header) -> Option<Payload> {
200 self.mmr_root_from_digest_or_runtime(header).map(|mmr_root| {
201 Payload::from_single_entry(known_payloads::MMR_ROOT_ID, mmr_root.encode())
202 })
203 }
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use crate::H256;
211 use sp_runtime::{traits::BlakeTwo256, Digest, DigestItem, OpaqueExtrinsic};
212
213 #[test]
214 fn should_construct_version_correctly() {
215 let tests = vec![(0, 0, 0b00000000), (7, 2, 0b11100010), (7, 31, 0b11111111)];
216
217 for (major, minor, version) in tests {
218 let v = MmrLeafVersion::new(major, minor);
219 assert_eq!(v.encode(), vec![version], "Encoding does not match.");
220 assert_eq!(v.split(), (major, minor));
221 }
222 }
223
224 #[test]
225 #[should_panic]
226 fn should_panic_if_major_too_large() {
227 MmrLeafVersion::new(8, 0);
228 }
229
230 #[test]
231 #[should_panic]
232 fn should_panic_if_minor_too_large() {
233 MmrLeafVersion::new(0, 32);
234 }
235
236 #[test]
237 fn extract_mmr_root_digest() {
238 type Header = sp_runtime::generic::Header<u64, BlakeTwo256>;
239 type Block = sp_runtime::generic::Block<Header, OpaqueExtrinsic>;
240 let mut header = Header::new(
241 1u64,
242 Default::default(),
243 Default::default(),
244 Default::default(),
245 Digest::default(),
246 );
247
248 assert!(find_mmr_root_digest::<Block>(&header).is_none());
250
251 let mmr_root_hash = H256::random();
252 header.digest_mut().push(DigestItem::Consensus(
253 BEEFY_ENGINE_ID,
254 ConsensusLog::<AuthorityId>::MmrRoot(mmr_root_hash).encode(),
255 ));
256
257 let extracted = find_mmr_root_digest::<Block>(&header);
259 assert_eq!(extracted, Some(mmr_root_hash));
260 }
261}