referrerpolicy=no-referrer-when-downgrade

sp_consensus_beefy/
payload.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
18use alloc::{vec, vec::Vec};
19use codec::{Decode, DecodeWithMemTracking, Encode};
20use scale_info::TypeInfo;
21use sp_runtime::traits::Block;
22
23/// Id of different payloads in the [`crate::Commitment`] data.
24pub type BeefyPayloadId = [u8; 2];
25
26/// Registry of all known [`BeefyPayloadId`].
27pub mod known_payloads {
28	use crate::BeefyPayloadId;
29
30	/// A [`Payload`](super::Payload) identifier for Merkle Mountain Range root hash.
31	///
32	/// Encoded value should contain a [`crate::MmrRootHash`] type (i.e. 32-bytes hash).
33	pub const MMR_ROOT_ID: BeefyPayloadId = *b"mh";
34}
35
36/// A BEEFY payload type allowing for future extensibility of adding additional kinds of payloads.
37///
38/// The idea is to store a vector of SCALE-encoded values with an extra identifier.
39/// Identifiers MUST be sorted by the [`BeefyPayloadId`] to allow efficient lookup of expected
40/// value. Duplicated identifiers are disallowed. It's okay for different implementations to only
41/// support a subset of possible values.
42#[derive(
43	Decode,
44	DecodeWithMemTracking,
45	Encode,
46	Debug,
47	PartialEq,
48	Eq,
49	Clone,
50	Ord,
51	PartialOrd,
52	Hash,
53	TypeInfo,
54)]
55pub struct Payload(Vec<(BeefyPayloadId, Vec<u8>)>);
56
57impl Payload {
58	/// Construct a new payload given an initial value
59	pub fn from_single_entry(id: BeefyPayloadId, value: Vec<u8>) -> Self {
60		Self(vec![(id, value)])
61	}
62
63	/// Returns a raw payload under given `id`.
64	///
65	/// If the [`BeefyPayloadId`] is not found in the payload `None` is returned.
66	pub fn get_raw(&self, id: &BeefyPayloadId) -> Option<&Vec<u8>> {
67		let index = self.0.binary_search_by(|probe| probe.0.cmp(id)).ok()?;
68		Some(&self.0[index].1)
69	}
70
71	/// Returns all the raw payloads under given `id`.
72	pub fn get_all_raw<'a>(
73		&'a self,
74		id: &'a BeefyPayloadId,
75	) -> impl Iterator<Item = &'a Vec<u8>> + 'a {
76		self.0
77			.iter()
78			.filter_map(move |probe| if &probe.0 != id { return None } else { Some(&probe.1) })
79	}
80
81	/// Returns a decoded payload value under given `id`.
82	///
83	/// In case the value is not there, or it cannot be decoded `None` is returned.
84	pub fn get_decoded<T: Decode>(&self, id: &BeefyPayloadId) -> Option<T> {
85		self.get_raw(id).and_then(|raw| T::decode(&mut &raw[..]).ok())
86	}
87
88	/// Returns all decoded payload values under given `id`.
89	pub fn get_all_decoded<'a, T: Decode>(
90		&'a self,
91		id: &'a BeefyPayloadId,
92	) -> impl Iterator<Item = Option<T>> + 'a {
93		self.get_all_raw(id).map(|raw| T::decode(&mut &raw[..]).ok())
94	}
95
96	/// Push a `Vec<u8>` with a given id into the payload vec.
97	/// This method will internally sort the payload vec after every push.
98	///
99	/// Returns self to allow for daisy chaining.
100	pub fn push_raw(mut self, id: BeefyPayloadId, value: Vec<u8>) -> Self {
101		self.0.push((id, value));
102		self.0.sort_by_key(|(id, _)| *id);
103		self
104	}
105}
106
107/// Trait for custom BEEFY payload providers.
108pub trait PayloadProvider<B: Block> {
109	/// Provide BEEFY payload if available for `header`.
110	fn payload(&self, header: &B::Header) -> Option<Payload>;
111}
112
113#[cfg(test)]
114mod tests {
115	use super::*;
116
117	#[test]
118	fn payload_methods_work_as_expected() {
119		let id1: BeefyPayloadId = *b"hw";
120		let msg1: String = "1. Hello World!".to_string();
121		let id2: BeefyPayloadId = *b"yb";
122		let msg2: String = "2. Yellow Board!".to_string();
123		let id3: BeefyPayloadId = *b"cs";
124		let msg3: String = "3. Cello Cord!".to_string();
125
126		let payload = Payload::from_single_entry(id1, msg1.encode())
127			.push_raw(id2, msg2.encode())
128			.push_raw(id3, msg3.encode());
129
130		assert_eq!(payload.get_decoded(&id1), Some(msg1));
131		assert_eq!(payload.get_decoded(&id2), Some(msg2));
132		assert_eq!(payload.get_raw(&id3), Some(&msg3.encode()));
133		assert_eq!(payload.get_raw(&known_payloads::MMR_ROOT_ID), None);
134	}
135}