Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion frame/beefy-mmr/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ hex = { version = "0.4", default-features = false, optional = true }
log = { version = "0.4", default-features = false, optional = true }
tiny-keccak = { version = "2.0.2", features = ["keccak"], optional = true }

beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/beefy" }
sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" }

[dev-dependencies]
env_logger = "0.9"
hex = "0.4"
Expand All @@ -22,4 +25,7 @@ hex-literal = "0.3"
debug = ["hex", "hex/std", "log"]
default = ["debug", "keccak", "std"]
keccak = ["tiny-keccak"]
std = []
std = [
"beefy-primitives/std",
"sp-api/std"
]
17 changes: 17 additions & 0 deletions frame/beefy-mmr/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

use beefy_primitives::mmr::{BeefyAuthoritySet, BeefyNextAuthoritySet};

/// Supported hashing output size.
///
/// The size is restricted to 32 bytes to allow for a more optimised implementation.
Expand Down Expand Up @@ -375,6 +377,21 @@ where
}
}

sp_api::decl_runtime_apis! {
/// API useful for BEEFY light clients.
pub trait BeefyMmrApi<H>
where
H: From<Hash> + Into<Hash>,
BeefyAuthoritySet<H>: sp_api::Decode,
{
/// Return the currently active BEEFY authority set.
fn authority_set() -> BeefyAuthoritySet<H>;

/// Return the next/queued BEEFY authority set.
fn next_authority_set() -> BeefyNextAuthoritySet<H>;
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
67 changes: 48 additions & 19 deletions frame/beefy-mmr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
use sp_runtime::traits::{Convert, Hash, Member};
use sp_std::prelude::*;

use beefy_primitives::mmr::{BeefyDataProvider, BeefyNextAuthoritySet, MmrLeaf, MmrLeafVersion};
use beefy_primitives::{
mmr::{BeefyAuthoritySet, BeefyDataProvider, BeefyNextAuthoritySet, MmrLeaf, MmrLeafVersion},
ValidatorSet as BeefyValidatorSet,
};
use pallet_mmr::{LeafDataProvider, ParentNumberAndHash};

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

/// Details of current BEEFY authority set.
#[pallet::storage]
#[pallet::getter(fn beefy_authorities)]
pub type BeefyAuthorities<T: Config> =
StorageValue<_, BeefyAuthoritySet<MerkleRootOf<T>>, ValueQuery>;

/// Details of next BEEFY authority set.
///
/// This storage entry is used as cache for calls to `update_beefy_next_authority_set`.
Expand All @@ -149,7 +158,7 @@ where
version: T::LeafVersion::get(),
parent_number_and_hash: ParentNumberAndHash::<T>::leaf_data(),
leaf_extra: T::BeefyDataProvider::extra_data(),
beefy_next_authority_set: Pallet::<T>::update_beefy_next_authority_set(),
beefy_next_authority_set: Pallet::<T>::beefy_next_authorities(),
}
}
}
Expand All @@ -163,35 +172,55 @@ where
}
}

impl<T> beefy_primitives::OnNewValidatorSet<<T as pallet_beefy::Config>::BeefyId> for Pallet<T>
where
T: pallet::Config,
MerkleRootOf<T>: From<beefy_merkle_tree::Hash> + Into<beefy_merkle_tree::Hash>,
{
/// Compute and cache BEEFY authority sets based on updated BEEFY validator sets.
fn on_new_validator_set(
current_set: &BeefyValidatorSet<<T as pallet_beefy::Config>::BeefyId>,
next_set: &BeefyValidatorSet<<T as pallet_beefy::Config>::BeefyId>,
) {
let current = Pallet::<T>::compute_authority_set(current_set);
let next = Pallet::<T>::compute_authority_set(next_set);
// cache the result
BeefyAuthorities::<T>::put(&current);
BeefyNextAuthorities::<T>::put(&next);
}
}

impl<T: Config> Pallet<T>
where
MerkleRootOf<T>: From<beefy_merkle_tree::Hash> + Into<beefy_merkle_tree::Hash>,
{
/// Returns details of the next BEEFY authority set.
/// Return the currently active BEEFY authority set.
pub fn authority_set() -> BeefyAuthoritySet<MerkleRootOf<T>> {
Pallet::<T>::beefy_authorities()
}

/// Return the next/queued BEEFY authority set.
pub fn next_authority_set() -> BeefyNextAuthoritySet<MerkleRootOf<T>> {
Pallet::<T>::beefy_next_authorities()
}

/// Returns details of a BEEFY authority set.
///
/// Details contain authority set id, authority set length and a merkle root,
/// constructed from uncompressed secp256k1 public keys converted to Ethereum addresses
/// of the next BEEFY authority set.
///
/// This function will use a storage-cached entry in case the set didn't change, or compute and
/// cache new one in case it did.
fn update_beefy_next_authority_set() -> BeefyNextAuthoritySet<MerkleRootOf<T>> {
let id = pallet_beefy::Pallet::<T>::validator_set_id() + 1;
let current_next = Self::beefy_next_authorities();
// avoid computing the merkle tree if validator set id didn't change.
if id == current_next.id {
return current_next
}

let beefy_addresses = pallet_beefy::Pallet::<T>::next_authorities()
fn compute_authority_set(
validator_set: &BeefyValidatorSet<<T as pallet_beefy::Config>::BeefyId>,
) -> BeefyAuthoritySet<MerkleRootOf<T>> {
let id = validator_set.id();
let beefy_addresses = validator_set
.validators()
.into_iter()
.cloned()
.map(T::BeefyAuthorityToMerkleLeaf::convert)
.collect::<Vec<_>>();
let len = beefy_addresses.len() as u32;
let root = beefy_merkle_tree::merkle_root::<Self, _, _>(beefy_addresses).into();
let next_set = BeefyNextAuthoritySet { id, len, root };
// cache the result
BeefyNextAuthorities::<T>::put(&next_set);
next_set
BeefyAuthoritySet { id, len, root }
}
}
1 change: 1 addition & 0 deletions frame/beefy-mmr/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ impl pallet_mmr::Config for Test {
impl pallet_beefy::Config for Test {
type BeefyId = BeefyId;
type MaxAuthorities = ConstU32<100>;
type OnNewValidatorSet = BeefyMmr;
}

parameter_types! {
Expand Down
50 changes: 50 additions & 0 deletions frame/beefy-mmr/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,53 @@ fn should_contain_valid_leaf_data() {
}
);
}

#[test]
fn should_update_authorities() {
new_test_ext(vec![1, 2, 3, 4]).execute_with(|| {
let auth_set = BeefyMmr::authority_set();
let next_auth_set = BeefyMmr::next_authority_set();

// check current authority set
assert_eq!(0, auth_set.id);
assert_eq!(2, auth_set.len);
let want: H256 =
hex!("176e73f1bf656478b728e28dd1a7733c98621b8acf830bff585949763dca7a96").into();
assert_eq!(want, auth_set.root);

// next authority set should have same validators but different id
assert_eq!(1, next_auth_set.id);
assert_eq!(auth_set.len, next_auth_set.len);
assert_eq!(auth_set.root, next_auth_set.root);

let announced_set = next_auth_set;
init_block(1);
let auth_set = BeefyMmr::authority_set();
let next_auth_set = BeefyMmr::next_authority_set();

// check new auth are expected ones
assert_eq!(announced_set, auth_set);
assert_eq!(1, auth_set.id);
// check next auth set
assert_eq!(2, next_auth_set.id);
let want: H256 =
hex!("9c6b2c1b0d0b25a008e6c882cc7b415f309965c72ad2b944ac0931048ca31cd5").into();
assert_eq!(2, next_auth_set.len);
assert_eq!(want, next_auth_set.root);

let announced_set = next_auth_set;
init_block(2);
let auth_set = BeefyMmr::authority_set();
let next_auth_set = BeefyMmr::next_authority_set();

// check new auth are expected ones
assert_eq!(announced_set, auth_set);
assert_eq!(2, auth_set.id);
// check next auth set
assert_eq!(3, next_auth_set.id);
let want: H256 =
hex!("9c6b2c1b0d0b25a008e6c882cc7b415f309965c72ad2b944ac0931048ca31cd5").into();
assert_eq!(2, next_auth_set.len);
assert_eq!(want, next_auth_set.root);
});
}
51 changes: 41 additions & 10 deletions frame/beefy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ use sp_runtime::{
};
use sp_std::prelude::*;

use beefy_primitives::{AuthorityIndex, ConsensusLog, ValidatorSet, BEEFY_ENGINE_ID};
use beefy_primitives::{
AuthorityIndex, ConsensusLog, OnNewValidatorSet, ValidatorSet, BEEFY_ENGINE_ID,
};

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

/// The maximum number of authorities that can be added.
type MaxAuthorities: Get<u32>;

/// A hook to act on the new BEEFY validator set.
///
/// For some applications it might be beneficial to make the BEEFY validator set available
/// externally apart from having it in the storage. For instance you might cache a light
/// weight MMR root over validators and make it available for Light Clients.
type OnNewValidatorSet: OnNewValidatorSet<<Self as Config>::BeefyId>;
}

#[pallet::pallet]
Expand Down Expand Up @@ -118,20 +127,29 @@ impl<T: Config> Pallet<T> {
) {
<Authorities<T>>::put(&new);

let next_id = Self::validator_set_id() + 1u64;
<ValidatorSetId<T>>::put(next_id);
if let Some(validator_set) = ValidatorSet::<T::BeefyId>::new(new, next_id) {
let new_id = Self::validator_set_id() + 1u64;
<ValidatorSetId<T>>::put(new_id);

<NextAuthorities<T>>::put(&queued);

if let Some(validator_set) = ValidatorSet::<T::BeefyId>::new(new, new_id) {
let log = DigestItem::Consensus(
BEEFY_ENGINE_ID,
ConsensusLog::AuthoritiesChange(validator_set).encode(),
ConsensusLog::AuthoritiesChange(validator_set.clone()).encode(),
);
<frame_system::Pallet<T>>::deposit_log(log);
}

<NextAuthorities<T>>::put(&queued);
let next_id = new_id + 1;
if let Some(next_validator_set) = ValidatorSet::<T::BeefyId>::new(queued, next_id) {
<T::OnNewValidatorSet as OnNewValidatorSet<_>>::on_new_validator_set(
&validator_set,
&next_validator_set,
);
}
}
}

fn initialize_authorities(authorities: &[T::BeefyId]) -> Result<(), ()> {
fn initialize_authorities(authorities: &Vec<T::BeefyId>) -> Result<(), ()> {
if authorities.is_empty() {
return Ok(())
}
Expand All @@ -141,12 +159,25 @@ impl<T: Config> Pallet<T> {
}

let bounded_authorities =
BoundedSlice::<T::BeefyId, T::MaxAuthorities>::try_from(authorities)?;
BoundedSlice::<T::BeefyId, T::MaxAuthorities>::try_from(authorities.as_slice())?;

let id = 0;
<Authorities<T>>::put(bounded_authorities);
<ValidatorSetId<T>>::put(0);
<ValidatorSetId<T>>::put(id);
// Like `pallet_session`, initialize the next validator set as well.
<NextAuthorities<T>>::put(bounded_authorities);

if let Some(validator_set) = ValidatorSet::<T::BeefyId>::new(authorities.clone(), id) {
let next_id = id + 1;
if let Some(next_validator_set) =
ValidatorSet::<T::BeefyId>::new(authorities.clone(), next_id)
{
<T::OnNewValidatorSet as OnNewValidatorSet<_>>::on_new_validator_set(
&validator_set,
&next_validator_set,
);
}
}
Ok(())
}
}
Expand Down
1 change: 1 addition & 0 deletions frame/beefy/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ impl frame_system::Config for Test {
impl pallet_beefy::Config for Test {
type BeefyId = BeefyId;
type MaxAuthorities = ConstU32<100>;
type OnNewValidatorSet = ();
}

parameter_types! {
Expand Down
14 changes: 14 additions & 0 deletions primitives/beefy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,20 @@ pub struct VoteMessage<Number, Id, Signature> {
pub signature: Signature,
}

/// New BEEFY validator set notification hook.
pub trait OnNewValidatorSet<AuthorityId> {
/// Function called by the pallet when BEEFY validator set changes.
fn on_new_validator_set(
validator_set: &ValidatorSet<AuthorityId>,
next_validator_set: &ValidatorSet<AuthorityId>,
);
}

/// No-op implementation of [OnNewValidatorSet].
impl<AuthorityId> OnNewValidatorSet<AuthorityId> for () {
fn on_new_validator_set(_: &ValidatorSet<AuthorityId>, _: &ValidatorSet<AuthorityId>) {}
}

sp_api::decl_runtime_apis! {
/// API necessary for BEEFY voters.
pub trait BeefyApi
Expand Down
13 changes: 8 additions & 5 deletions primitives/beefy/src/mmr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,29 +95,32 @@ impl MmrLeafVersion {
}
}

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

/// Details of the next BEEFY authority set.
pub type BeefyNextAuthoritySet<MerkleRoot> = BeefyAuthoritySet<MerkleRoot>;

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading