Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit a43f5e7

Browse files
acatangiuDaviRain-Su
authored andcommitted
pallet-beefy-mmr: add API for BEEFY Authority Sets (paritytech#11406)
* pallet-beefy: add Config::OnNewValidatorSet type Add a hook to pallet-beefy for doing specific work when BEEFY validator set changes. For example, this can be used by pallet-beefy-mmr to cache a lightweight MMR root over validators and make it available to light clients. * pallet-beefy-mmr: implement OnNewValidatorSet Implement pallet-beefy::OnNewValidatorSet to be notified of BEEFY validator set changes. Use the notifications to compute and cache a light weight 'BEEFY authority set' which is an MMR root over BEEFY validator set plus some extra info. Previously, pallet-beefy-mmr was interogating pallet-beefy about validator set id on every block to find out when it needs to recompute the authority set. By using the event-driven approach in this commit, we also save one extra state interogation per block. * pallet-beefy-mmr: add new authority_set() API Expose current and next BEEFY authority sets through runtime API. These can be directly used by light clients to avoid having them compute them themselves based on BEEFY validator sets. Signed-off-by: acatangiu <[email protected]> * rename BeefyMmr exposed runtime api
1 parent bf4e8ce commit a43f5e7

File tree

12 files changed

+202
-35
lines changed

12 files changed

+202
-35
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frame/beefy-mmr/primitives/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ hex = { version = "0.4", default-features = false, optional = true }
1313
log = { version = "0.4", default-features = false, optional = true }
1414
tiny-keccak = { version = "2.0.2", features = ["keccak"], optional = true }
1515

16+
beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/beefy" }
17+
sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" }
18+
1619
[dev-dependencies]
1720
env_logger = "0.9"
1821
hex = "0.4"
@@ -22,4 +25,7 @@ hex-literal = "0.3"
2225
debug = ["hex", "hex/std", "log"]
2326
default = ["debug", "keccak", "std"]
2427
keccak = ["tiny-keccak"]
25-
std = []
28+
std = [
29+
"beefy-primitives/std",
30+
"sp-api/std"
31+
]

frame/beefy-mmr/primitives/src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ extern crate alloc;
3636
#[cfg(not(feature = "std"))]
3737
use alloc::vec::Vec;
3838

39+
use beefy_primitives::mmr::{BeefyAuthoritySet, BeefyNextAuthoritySet};
40+
3941
/// Supported hashing output size.
4042
///
4143
/// The size is restricted to 32 bytes to allow for a more optimised implementation.
@@ -375,6 +377,21 @@ where
375377
}
376378
}
377379

380+
sp_api::decl_runtime_apis! {
381+
/// API useful for BEEFY light clients.
382+
pub trait BeefyMmrApi<H>
383+
where
384+
H: From<Hash> + Into<Hash>,
385+
BeefyAuthoritySet<H>: sp_api::Decode,
386+
{
387+
/// Return the currently active BEEFY authority set proof.
388+
fn authority_set_proof() -> BeefyAuthoritySet<H>;
389+
390+
/// Return the next/queued BEEFY authority set proof.
391+
fn next_authority_set_proof() -> BeefyNextAuthoritySet<H>;
392+
}
393+
}
394+
378395
#[cfg(test)]
379396
mod tests {
380397
use super::*;

frame/beefy-mmr/src/lib.rs

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@
3636
use sp_runtime::traits::{Convert, Hash, Member};
3737
use sp_std::prelude::*;
3838

39-
use beefy_primitives::mmr::{BeefyDataProvider, BeefyNextAuthoritySet, MmrLeaf, MmrLeafVersion};
39+
use beefy_primitives::{
40+
mmr::{BeefyAuthoritySet, BeefyDataProvider, BeefyNextAuthoritySet, MmrLeaf, MmrLeafVersion},
41+
ValidatorSet as BeefyValidatorSet,
42+
};
4043
use pallet_mmr::{LeafDataProvider, ParentNumberAndHash};
4144

4245
use frame_support::{crypto::ecdsa::ECDSAExt, traits::Get};
@@ -124,6 +127,12 @@ pub mod pallet {
124127
type BeefyDataProvider: BeefyDataProvider<Self::LeafExtra>;
125128
}
126129

130+
/// Details of current BEEFY authority set.
131+
#[pallet::storage]
132+
#[pallet::getter(fn beefy_authorities)]
133+
pub type BeefyAuthorities<T: Config> =
134+
StorageValue<_, BeefyAuthoritySet<MerkleRootOf<T>>, ValueQuery>;
135+
127136
/// Details of next BEEFY authority set.
128137
///
129138
/// This storage entry is used as cache for calls to `update_beefy_next_authority_set`.
@@ -149,7 +158,7 @@ where
149158
version: T::LeafVersion::get(),
150159
parent_number_and_hash: ParentNumberAndHash::<T>::leaf_data(),
151160
leaf_extra: T::BeefyDataProvider::extra_data(),
152-
beefy_next_authority_set: Pallet::<T>::update_beefy_next_authority_set(),
161+
beefy_next_authority_set: Pallet::<T>::beefy_next_authorities(),
153162
}
154163
}
155164
}
@@ -163,35 +172,55 @@ where
163172
}
164173
}
165174

175+
impl<T> beefy_primitives::OnNewValidatorSet<<T as pallet_beefy::Config>::BeefyId> for Pallet<T>
176+
where
177+
T: pallet::Config,
178+
MerkleRootOf<T>: From<beefy_merkle_tree::Hash> + Into<beefy_merkle_tree::Hash>,
179+
{
180+
/// Compute and cache BEEFY authority sets based on updated BEEFY validator sets.
181+
fn on_new_validator_set(
182+
current_set: &BeefyValidatorSet<<T as pallet_beefy::Config>::BeefyId>,
183+
next_set: &BeefyValidatorSet<<T as pallet_beefy::Config>::BeefyId>,
184+
) {
185+
let current = Pallet::<T>::compute_authority_set(current_set);
186+
let next = Pallet::<T>::compute_authority_set(next_set);
187+
// cache the result
188+
BeefyAuthorities::<T>::put(&current);
189+
BeefyNextAuthorities::<T>::put(&next);
190+
}
191+
}
192+
166193
impl<T: Config> Pallet<T>
167194
where
168195
MerkleRootOf<T>: From<beefy_merkle_tree::Hash> + Into<beefy_merkle_tree::Hash>,
169196
{
170-
/// Returns details of the next BEEFY authority set.
197+
/// Return the currently active BEEFY authority set proof.
198+
pub fn authority_set_proof() -> BeefyAuthoritySet<MerkleRootOf<T>> {
199+
Pallet::<T>::beefy_authorities()
200+
}
201+
202+
/// Return the next/queued BEEFY authority set proof.
203+
pub fn next_authority_set_proof() -> BeefyNextAuthoritySet<MerkleRootOf<T>> {
204+
Pallet::<T>::beefy_next_authorities()
205+
}
206+
207+
/// Returns details of a BEEFY authority set.
171208
///
172209
/// Details contain authority set id, authority set length and a merkle root,
173210
/// constructed from uncompressed secp256k1 public keys converted to Ethereum addresses
174211
/// of the next BEEFY authority set.
175-
///
176-
/// This function will use a storage-cached entry in case the set didn't change, or compute and
177-
/// cache new one in case it did.
178-
fn update_beefy_next_authority_set() -> BeefyNextAuthoritySet<MerkleRootOf<T>> {
179-
let id = pallet_beefy::Pallet::<T>::validator_set_id() + 1;
180-
let current_next = Self::beefy_next_authorities();
181-
// avoid computing the merkle tree if validator set id didn't change.
182-
if id == current_next.id {
183-
return current_next
184-
}
185-
186-
let beefy_addresses = pallet_beefy::Pallet::<T>::next_authorities()
212+
fn compute_authority_set(
213+
validator_set: &BeefyValidatorSet<<T as pallet_beefy::Config>::BeefyId>,
214+
) -> BeefyAuthoritySet<MerkleRootOf<T>> {
215+
let id = validator_set.id();
216+
let beefy_addresses = validator_set
217+
.validators()
187218
.into_iter()
219+
.cloned()
188220
.map(T::BeefyAuthorityToMerkleLeaf::convert)
189221
.collect::<Vec<_>>();
190222
let len = beefy_addresses.len() as u32;
191223
let root = beefy_merkle_tree::merkle_root::<Self, _, _>(beefy_addresses).into();
192-
let next_set = BeefyNextAuthoritySet { id, len, root };
193-
// cache the result
194-
BeefyNextAuthorities::<T>::put(&next_set);
195-
next_set
224+
BeefyAuthoritySet { id, len, root }
196225
}
197226
}

frame/beefy-mmr/src/mock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ impl pallet_mmr::Config for Test {
125125
impl pallet_beefy::Config for Test {
126126
type BeefyId = BeefyId;
127127
type MaxAuthorities = ConstU32<100>;
128+
type OnNewValidatorSet = BeefyMmr;
128129
}
129130

130131
parameter_types! {

frame/beefy-mmr/src/tests.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,53 @@ fn should_contain_valid_leaf_data() {
149149
}
150150
);
151151
}
152+
153+
#[test]
154+
fn should_update_authorities() {
155+
new_test_ext(vec![1, 2, 3, 4]).execute_with(|| {
156+
let auth_set = BeefyMmr::authority_set_proof();
157+
let next_auth_set = BeefyMmr::next_authority_set_proof();
158+
159+
// check current authority set
160+
assert_eq!(0, auth_set.id);
161+
assert_eq!(2, auth_set.len);
162+
let want: H256 =
163+
hex!("176e73f1bf656478b728e28dd1a7733c98621b8acf830bff585949763dca7a96").into();
164+
assert_eq!(want, auth_set.root);
165+
166+
// next authority set should have same validators but different id
167+
assert_eq!(1, next_auth_set.id);
168+
assert_eq!(auth_set.len, next_auth_set.len);
169+
assert_eq!(auth_set.root, next_auth_set.root);
170+
171+
let announced_set = next_auth_set;
172+
init_block(1);
173+
let auth_set = BeefyMmr::authority_set_proof();
174+
let next_auth_set = BeefyMmr::next_authority_set_proof();
175+
176+
// check new auth are expected ones
177+
assert_eq!(announced_set, auth_set);
178+
assert_eq!(1, auth_set.id);
179+
// check next auth set
180+
assert_eq!(2, next_auth_set.id);
181+
let want: H256 =
182+
hex!("9c6b2c1b0d0b25a008e6c882cc7b415f309965c72ad2b944ac0931048ca31cd5").into();
183+
assert_eq!(2, next_auth_set.len);
184+
assert_eq!(want, next_auth_set.root);
185+
186+
let announced_set = next_auth_set;
187+
init_block(2);
188+
let auth_set = BeefyMmr::authority_set_proof();
189+
let next_auth_set = BeefyMmr::next_authority_set_proof();
190+
191+
// check new auth are expected ones
192+
assert_eq!(announced_set, auth_set);
193+
assert_eq!(2, auth_set.id);
194+
// check next auth set
195+
assert_eq!(3, next_auth_set.id);
196+
let want: H256 =
197+
hex!("9c6b2c1b0d0b25a008e6c882cc7b415f309965c72ad2b944ac0931048ca31cd5").into();
198+
assert_eq!(2, next_auth_set.len);
199+
assert_eq!(want, next_auth_set.root);
200+
});
201+
}

frame/beefy/src/lib.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ use sp_runtime::{
3232
};
3333
use sp_std::prelude::*;
3434

35-
use beefy_primitives::{AuthorityIndex, ConsensusLog, ValidatorSet, BEEFY_ENGINE_ID};
35+
use beefy_primitives::{
36+
AuthorityIndex, ConsensusLog, OnNewValidatorSet, ValidatorSet, BEEFY_ENGINE_ID,
37+
};
3638

3739
#[cfg(test)]
3840
mod mock;
@@ -58,6 +60,13 @@ pub mod pallet {
5860

5961
/// The maximum number of authorities that can be added.
6062
type MaxAuthorities: Get<u32>;
63+
64+
/// A hook to act on the new BEEFY validator set.
65+
///
66+
/// For some applications it might be beneficial to make the BEEFY validator set available
67+
/// externally apart from having it in the storage. For instance you might cache a light
68+
/// weight MMR root over validators and make it available for Light Clients.
69+
type OnNewValidatorSet: OnNewValidatorSet<<Self as Config>::BeefyId>;
6170
}
6271

6372
#[pallet::pallet]
@@ -118,20 +127,29 @@ impl<T: Config> Pallet<T> {
118127
) {
119128
<Authorities<T>>::put(&new);
120129

121-
let next_id = Self::validator_set_id() + 1u64;
122-
<ValidatorSetId<T>>::put(next_id);
123-
if let Some(validator_set) = ValidatorSet::<T::BeefyId>::new(new, next_id) {
130+
let new_id = Self::validator_set_id() + 1u64;
131+
<ValidatorSetId<T>>::put(new_id);
132+
133+
<NextAuthorities<T>>::put(&queued);
134+
135+
if let Some(validator_set) = ValidatorSet::<T::BeefyId>::new(new, new_id) {
124136
let log = DigestItem::Consensus(
125137
BEEFY_ENGINE_ID,
126-
ConsensusLog::AuthoritiesChange(validator_set).encode(),
138+
ConsensusLog::AuthoritiesChange(validator_set.clone()).encode(),
127139
);
128140
<frame_system::Pallet<T>>::deposit_log(log);
129-
}
130141

131-
<NextAuthorities<T>>::put(&queued);
142+
let next_id = new_id + 1;
143+
if let Some(next_validator_set) = ValidatorSet::<T::BeefyId>::new(queued, next_id) {
144+
<T::OnNewValidatorSet as OnNewValidatorSet<_>>::on_new_validator_set(
145+
&validator_set,
146+
&next_validator_set,
147+
);
148+
}
149+
}
132150
}
133151

134-
fn initialize_authorities(authorities: &[T::BeefyId]) -> Result<(), ()> {
152+
fn initialize_authorities(authorities: &Vec<T::BeefyId>) -> Result<(), ()> {
135153
if authorities.is_empty() {
136154
return Ok(())
137155
}
@@ -141,12 +159,25 @@ impl<T: Config> Pallet<T> {
141159
}
142160

143161
let bounded_authorities =
144-
BoundedSlice::<T::BeefyId, T::MaxAuthorities>::try_from(authorities)?;
162+
BoundedSlice::<T::BeefyId, T::MaxAuthorities>::try_from(authorities.as_slice())?;
145163

164+
let id = 0;
146165
<Authorities<T>>::put(bounded_authorities);
147-
<ValidatorSetId<T>>::put(0);
166+
<ValidatorSetId<T>>::put(id);
148167
// Like `pallet_session`, initialize the next validator set as well.
149168
<NextAuthorities<T>>::put(bounded_authorities);
169+
170+
if let Some(validator_set) = ValidatorSet::<T::BeefyId>::new(authorities.clone(), id) {
171+
let next_id = id + 1;
172+
if let Some(next_validator_set) =
173+
ValidatorSet::<T::BeefyId>::new(authorities.clone(), next_id)
174+
{
175+
<T::OnNewValidatorSet as OnNewValidatorSet<_>>::on_new_validator_set(
176+
&validator_set,
177+
&next_validator_set,
178+
);
179+
}
180+
}
150181
Ok(())
151182
}
152183
}

frame/beefy/src/mock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ impl frame_system::Config for Test {
8787
impl pallet_beefy::Config for Test {
8888
type BeefyId = BeefyId;
8989
type MaxAuthorities = ConstU32<100>;
90+
type OnNewValidatorSet = ();
9091
}
9192

9293
parameter_types! {

primitives/beefy/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,20 @@ pub struct VoteMessage<Number, Id, Signature> {
154154
pub signature: Signature,
155155
}
156156

157+
/// New BEEFY validator set notification hook.
158+
pub trait OnNewValidatorSet<AuthorityId> {
159+
/// Function called by the pallet when BEEFY validator set changes.
160+
fn on_new_validator_set(
161+
validator_set: &ValidatorSet<AuthorityId>,
162+
next_validator_set: &ValidatorSet<AuthorityId>,
163+
);
164+
}
165+
166+
/// No-op implementation of [OnNewValidatorSet].
167+
impl<AuthorityId> OnNewValidatorSet<AuthorityId> for () {
168+
fn on_new_validator_set(_: &ValidatorSet<AuthorityId>, _: &ValidatorSet<AuthorityId>) {}
169+
}
170+
157171
sp_api::decl_runtime_apis! {
158172
/// API necessary for BEEFY voters.
159173
pub trait BeefyApi

primitives/beefy/src/mmr.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,29 +95,32 @@ impl MmrLeafVersion {
9595
}
9696
}
9797

98-
/// Details of the next BEEFY authority set.
98+
/// Details of a BEEFY authority set.
9999
#[derive(Debug, Default, PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen)]
100-
pub struct BeefyNextAuthoritySet<MerkleRoot> {
101-
/// Id of the next set.
100+
pub struct BeefyAuthoritySet<MerkleRoot> {
101+
/// Id of the set.
102102
///
103103
/// Id is required to correlate BEEFY signed commitments with the validator set.
104104
/// Light Client can easily verify that the commitment witness it is getting is
105105
/// produced by the latest validator set.
106106
pub id: crate::ValidatorSetId,
107107
/// Number of validators in the set.
108108
///
109-
/// Some BEEFY Light Clients may use an interactive protocol to verify only subset
109+
/// Some BEEFY Light Clients may use an interactive protocol to verify only a subset
110110
/// of signatures. We put set length here, so that these clients can verify the minimal
111111
/// number of required signatures.
112112
pub len: u32,
113-
/// Merkle Root Hash build from BEEFY AuthorityIds.
113+
/// Merkle Root Hash built from BEEFY AuthorityIds.
114114
///
115115
/// This is used by Light Clients to confirm that the commitments are signed by the correct
116116
/// validator set. Light Clients using interactive protocol, might verify only subset of
117117
/// signatures, hence don't require the full list here (will receive inclusion proofs).
118118
pub root: MerkleRoot,
119119
}
120120

121+
/// Details of the next BEEFY authority set.
122+
pub type BeefyNextAuthoritySet<MerkleRoot> = BeefyAuthoritySet<MerkleRoot>;
123+
121124
#[cfg(test)]
122125
mod tests {
123126
use super::*;

0 commit comments

Comments
 (0)