#![cfg_attr(not(feature = "std"), no_std)]
use codec::Encode;
use dkg_runtime_primitives::{
offchain::storage_keys::{
AGGREGATED_MISBEHAVIOUR_REPORTS, AGGREGATED_MISBEHAVIOUR_REPORTS_LOCK,
AGGREGATED_PUBLIC_KEYS, AGGREGATED_PUBLIC_KEYS_AT_GENESIS,
AGGREGATED_PUBLIC_KEYS_AT_GENESIS_LOCK, AGGREGATED_PUBLIC_KEYS_LOCK,
SUBMIT_GENESIS_KEYS_AT, SUBMIT_KEYS_AT,
},
proposal::Proposal,
traits::{GetDKGPublicKey, OnAuthoritySetChangeHandler},
utils::{ecdsa, to_slice_33, verify_signer_from_set_ecdsa},
AggregatedMisbehaviourReports, AggregatedPublicKeys, AuthorityIndex, AuthoritySet,
ConsensusLog, MisbehaviourType, ProposalHandlerTrait, ProposalNonce, RefreshProposal,
DKG_ENGINE_ID,
};
use frame_support::{
dispatch::DispatchResultWithPostInfo,
ensure,
pallet_prelude::{Get, Weight},
traits::{EstimateNextSessionRotation, OneSessionHandler},
BoundedVec,
};
use frame_system::{
offchain::{Signer, SubmitTransaction},
pallet_prelude::BlockNumberFor,
};
pub use pallet::*;
use sp_io::hashing::keccak_256;
use sp_runtime::{
generic::DigestItem,
offchain::{
storage::StorageValueRef,
storage_lock::{StorageLock, Time},
},
traits::{AtLeast32BitUnsigned, Convert, IsMember, One, Saturating, Zero},
DispatchError, Permill, RuntimeAppPublic,
};
use sp_std::{
collections::btree_map::BTreeMap,
convert::{TryFrom, TryInto},
fmt::Debug,
marker::PhantomData,
ops::{Rem, Sub},
prelude::*,
vec,
};
use types::RoundMetadata;
use weights::WeightInfo;
pub const INITIAL_REPUTATION: u32 = 1_000_000_000;
pub const BLOCKS_TO_WAIT_BEFORE_KEYGEN_RETRY_TRIGGER: u32 = 20;
pub const REPUTATION_INCREMENT: u32 = INITIAL_REPUTATION / 1000;
#[cfg(test)]
mod mock;
pub mod types;
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod weights;
#[frame_support::pallet]
pub mod pallet {
use dkg_runtime_primitives::{traits::OnDKGPublicKeyChangeHandler, ProposalHandlerTrait};
use frame_support::pallet_prelude::*;
use frame_system::{
ensure_signed,
offchain::{AppCrypto, CreateSignedTransaction},
pallet_prelude::*,
};
use log;
use sp_runtime::Percent;
use super::*;
pub struct AuthorityIdOf<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> Convert<T::AccountId, Option<T::DKGId>> for AuthorityIdOf<T> {
fn convert(controller: T::AccountId) -> Option<T::DKGId> {
AccountToAuthority::<T>::get(controller)
}
}
pub struct VoterSetData {
pub voter_set_merkle_root: [u8; 32],
pub average_session_length_in_millisecs: u64,
pub voter_count: u32,
}
#[pallet::config]
pub trait Config:
frame_system::Config + pallet_timestamp::Config + CreateSignedTransaction<Call<Self>>
{
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type DKGId: Member
+ Parameter
+ RuntimeAppPublic
+ MaybeSerializeDeserialize
+ AsRef<[u8]>
+ Into<ecdsa::Public>
+ From<ecdsa::Public>
+ MaxEncodedLen;
type DKGAuthorityToMerkleLeaf: Convert<Self::DKGId, Vec<u8>>;
type KeygenJailSentence: Get<BlockNumberFor<Self>>;
type SigningJailSentence: Get<BlockNumberFor<Self>>;
type AuthorityIdOf: Convert<Self::AccountId, Option<Self::DKGId>>;
type Reputation: Member
+ Parameter
+ Default
+ Encode
+ Decode
+ AtLeast32BitUnsigned
+ MaxEncodedLen
+ Copy;
type DecayPercentage: Get<Percent>;
type OffChainAuthId: AppCrypto<Self::Public, Self::Signature>;
type OnAuthoritySetChangeHandler: OnAuthoritySetChangeHandler<
Self::AccountId,
dkg_runtime_primitives::AuthoritySetId,
Self::DKGId,
>;
type OnDKGPublicKeyChangeHandler: OnDKGPublicKeyChangeHandler<
dkg_runtime_primitives::AuthoritySetId,
>;
type ProposalHandler: ProposalHandlerTrait<MaxProposalLength = Self::MaxProposalLength>;
type NextSessionRotation: EstimateNextSessionRotation<BlockNumberFor<Self>>;
#[pallet::constant]
type UnsignedInterval: Get<BlockNumberFor<Self>>;
#[pallet::constant]
type UnsignedPriority: Get<TransactionPriority>;
#[pallet::constant]
type SessionPeriod: Get<BlockNumberFor<Self>>;
#[pallet::constant]
type MaxKeyLength: Get<u32>
+ Default
+ TypeInfo
+ MaxEncodedLen
+ Debug
+ Clone
+ Eq
+ PartialEq
+ PartialOrd
+ Ord;
#[pallet::constant]
type MaxSignatureLength: Get<u32>
+ Default
+ TypeInfo
+ MaxEncodedLen
+ Debug
+ Clone
+ Eq
+ PartialEq
+ PartialOrd
+ Ord;
#[pallet::constant]
type MaxAuthorities: Get<u32>
+ Default
+ TypeInfo
+ MaxEncodedLen
+ Debug
+ Clone
+ Eq
+ PartialEq
+ PartialOrd
+ Ord;
#[pallet::constant]
type MaxReporters: Get<u32>
+ Default
+ TypeInfo
+ MaxEncodedLen
+ Debug
+ Clone
+ Eq
+ PartialEq
+ PartialOrd
+ Ord;
#[pallet::constant]
type VoteLength: Get<u32>
+ Default
+ TypeInfo
+ MaxEncodedLen
+ Debug
+ Clone
+ Eq
+ PartialEq
+ PartialOrd
+ Ord;
type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
#[pallet::constant]
type MaxProposalLength: Get<u32>
+ Debug
+ Clone
+ Eq
+ PartialEq
+ PartialOrd
+ Ord
+ TypeInfo;
type WeightInfo: WeightInfo;
}
#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn offchain_worker(block_number: BlockNumberFor<T>) {
let res = Self::submit_genesis_public_key_onchain(block_number);
log::debug!(
target: "runtime::dkg_metadata",
"submit_genesis_public_key_onchain : {:?}",
res,
);
let res = Self::submit_next_public_key_onchain(block_number);
log::debug!(
target: "runtime::dkg_metadata",
"submit_next_public_key_onchain : {:?}",
res,
);
let res = Self::submit_misbehaviour_reports_onchain(block_number);
log::debug!(
target: "runtime::dkg_metadata",
"submit_misbehaviour_reports_onchain : {:?}",
res,
);
let (authority_id, pk) = DKGPublicKey::<T>::get();
let maybe_next_key = NextDKGPublicKey::<T>::get();
log::debug!(
target: "runtime::dkg_metadata",
"Current Authority({}) DKG PublicKey:
**********************************************************
compressed: 0x{}
uncompressed: 0x{}
**********************************************************",
authority_id,
hex::encode(pk.clone()),
hex::encode(Self::decompress_public_key(pk.into()).unwrap_or_default()),
);
if let Some((next_authority_id, next_pk)) = maybe_next_key {
log::debug!(
target: "runtime::dkg_metadata",
"Next Authority({}) DKG PublicKey:
**********************************************************
compressed: 0x{}
uncompressed: 0x{}
**********************************************************",
next_authority_id,
hex::encode(next_pk.clone()),
hex::encode(Self::decompress_public_key(next_pk.into()).unwrap_or_default()),
);
}
}
fn on_initialize(n: BlockNumberFor<T>) -> frame_support::weights::Weight {
if let (true, _) = ShouldExecuteNewKeygen::<T>::get() {
ShouldExecuteNewKeygen::<T>::put((false, false))
}
let blocks_passed_since_last_session_rotation =
n - LastSessionRotationBlock::<T>::get();
if blocks_passed_since_last_session_rotation >= T::SessionPeriod::get() &&
blocks_passed_since_last_session_rotation %
BLOCKS_TO_WAIT_BEFORE_KEYGEN_RETRY_TRIGGER.into() ==
0u32.into()
{
ShouldExecuteNewKeygen::<T>::put((true, false));
}
if Self::should_refresh(n) && !Self::refresh_in_progress() {
if let Some(pub_key) = Self::next_dkg_public_key() {
Self::do_refresh(pub_key.1.into());
return Weight::from_parts(1_u64, 1024)
}
}
Weight::from_parts(0, 0)
}
}
#[pallet::storage]
#[pallet::getter(fn used_signatures)]
pub type UsedSignatures<T: Config> = StorageValue<
_,
BoundedVec<BoundedVec<u8, T::MaxSignatureLength>, T::MaxSignatureLength>,
ValueQuery,
>;
#[pallet::storage]
#[pallet::getter(fn refresh_nonce)]
pub type RefreshNonce<T: Config> = StorageValue<_, u32, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn next_unsigned_at)]
pub(super) type NextUnsignedAt<T: Config> = StorageValue<_, BlockNumberFor<T>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn refresh_in_progress)]
pub type RefreshInProgress<T: Config> = StorageValue<_, bool, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn should_execute_new_keygen)]
pub type ShouldExecuteNewKeygen<T: Config> = StorageValue<_, (bool, bool), ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn should_submit_proposer_vote)]
pub type ShouldSubmitProposerVote<T: Config> = StorageValue<_, bool, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn next_dkg_public_key)]
pub type NextDKGPublicKey<T: Config> = StorageValue<
_,
(dkg_runtime_primitives::AuthoritySetId, BoundedVec<u8, T::MaxKeyLength>),
OptionQuery,
>;
#[pallet::storage]
#[pallet::getter(fn next_public_key_signature)]
pub type NextPublicKeySignature<T: Config> =
StorageValue<_, BoundedVec<u8, T::MaxSignatureLength>, OptionQuery>;
#[pallet::storage]
#[pallet::getter(fn dkg_public_key)]
pub type DKGPublicKey<T: Config> = StorageValue<
_,
(dkg_runtime_primitives::AuthoritySetId, BoundedVec<u8, T::MaxKeyLength>),
ValueQuery,
>;
#[pallet::storage]
#[pallet::getter(fn public_key_signature)]
pub type DKGPublicKeySignature<T: Config> =
StorageValue<_, BoundedVec<u8, T::MaxSignatureLength>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn previous_public_key)]
pub type PreviousPublicKey<T: Config> = StorageValue<
_,
(dkg_runtime_primitives::AuthoritySetId, BoundedVec<u8, T::MaxKeyLength>),
ValueQuery,
>;
#[pallet::storage]
#[pallet::getter(fn historical_rounds)]
pub type HistoricalRounds<T: Config> = StorageMap<
_,
Blake2_256,
dkg_runtime_primitives::AuthoritySetId,
RoundMetadata<T::MaxKeyLength, T::MaxSignatureLength>,
ValueQuery,
>;
#[pallet::storage]
#[pallet::getter(fn signature_threshold)]
pub(super) type SignatureThreshold<T: Config> = StorageValue<_, u16, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn keygen_threshold)]
pub(super) type KeygenThreshold<T: Config> = StorageValue<_, u16, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn next_signature_threshold)]
pub(super) type NextSignatureThreshold<T: Config> = StorageValue<_, u16, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn next_keygen_threshold)]
pub(super) type NextKeygenThreshold<T: Config> = StorageValue<_, u16, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn pending_signature_threshold)]
pub(super) type PendingSignatureThreshold<T: Config> = StorageValue<_, u16, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn pending_keygen_threshold)]
pub(super) type PendingKeygenThreshold<T: Config> = StorageValue<_, u16, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn authorities)]
pub(super) type Authorities<T: Config> =
StorageValue<_, BoundedVec<T::DKGId, T::MaxAuthorities>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn authority_set_id)]
pub(super) type AuthoritySetId<T: Config> =
StorageValue<_, dkg_runtime_primitives::AuthoritySetId, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn next_authority_set_id)]
pub(super) type NextAuthoritySetId<T: Config> =
StorageValue<_, dkg_runtime_primitives::AuthoritySetId, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn next_authorities)]
pub(super) type NextAuthorities<T: Config> =
StorageValue<_, BoundedVec<T::DKGId, T::MaxAuthorities>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn current_authorities_accounts)]
pub type CurrentAuthoritiesAccounts<T: Config> =
StorageValue<_, BoundedVec<T::AccountId, T::MaxAuthorities>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn next_authorities_accounts)]
pub(super) type NextAuthoritiesAccounts<T: Config> =
StorageValue<_, BoundedVec<T::AccountId, T::MaxAuthorities>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn account_to_authority)]
pub(super) type AccountToAuthority<T: Config> =
StorageMap<_, Blake2_256, T::AccountId, T::DKGId, OptionQuery>;
#[pallet::storage]
#[pallet::getter(fn misbehaviour_reports)]
pub type MisbehaviourReports<T: Config> = StorageMap<
_,
Blake2_256,
(
dkg_runtime_primitives::MisbehaviourType,
dkg_runtime_primitives::AuthoritySetId,
T::DKGId,
),
AggregatedMisbehaviourReports<T::DKGId, T::MaxSignatureLength, T::MaxReporters>,
OptionQuery,
>;
#[pallet::storage]
#[pallet::getter(fn authority_reputations)]
pub type AuthorityReputations<T: Config> =
StorageMap<_, Blake2_128Concat, T::DKGId, T::Reputation, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn jailed_keygen_authorities)]
pub type JailedKeygenAuthorities<T: Config> =
StorageMap<_, Blake2_256, T::DKGId, BlockNumberFor<T>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn jailed_signing_authorities)]
pub type JailedSigningAuthorities<T: Config> =
StorageMap<_, Blake2_256, T::DKGId, BlockNumberFor<T>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn best_authorities)]
pub(super) type BestAuthorities<T: Config> =
StorageValue<_, BoundedVec<(u16, T::DKGId), T::MaxAuthorities>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn next_best_authorities)]
pub(super) type NextBestAuthorities<T: Config> =
StorageValue<_, BoundedVec<(u16, T::DKGId), T::MaxAuthorities>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn last_session_rotation_block)]
pub(super) type LastSessionRotationBlock<T: Config> =
StorageValue<_, BlockNumberFor<T>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn pending_refresh_proposal)]
pub(super) type PendingRefreshProposal<T: Config> =
StorageValue<_, RefreshProposal, OptionQuery>;
#[pallet::storage]
#[pallet::getter(fn current_refresh_proposal)]
pub(super) type CurrentRefreshProposal<T: Config> =
StorageValue<_, RefreshProposal, OptionQuery>;
#[pallet::error]
pub enum Error<T> {
NoMappedAccount,
InvalidThreshold,
MustBeAQueuedAuthority,
MustBeAnActiveAuthority,
InvalidPublicKeys,
AlreadySubmittedPublicKey,
AlreadySubmittedSignature,
UsedSignature,
InvalidSignature,
InvalidNonce,
InvalidMisbehaviourReports,
RefreshInProgress,
NoRefreshProposal,
InvalidRefreshProposal,
NoNextPublicKey,
InvalidControllerAccount,
OutOfBounds,
CannotRetreiveSigner,
ProposalNotSigned,
OffenderNotAuthority,
AlreadyJailed,
NotEnoughAuthoritiesToJail,
}
#[pallet::event]
#[pallet::generate_deposit(pub fn deposit_event)]
pub enum Event<T: Config> {
PublicKeySubmitted { compressed_pub_key: Vec<u8> },
NextPublicKeySubmitted { compressed_pub_key: Vec<u8> },
NextPublicKeySignatureSubmitted {
voter_merkle_root: [u8; 32],
session_length: u64,
voter_count: u32,
nonce: ProposalNonce,
pub_key: Vec<u8>,
signature: Vec<u8>,
compressed_pub_key: Vec<u8>,
},
PublicKeyChanged { compressed_pub_key: Vec<u8> },
PublicKeySignatureChanged {
voter_merkle_root: [u8; 32],
session_length: u64,
voter_count: u32,
nonce: ProposalNonce,
pub_key: Vec<u8>,
signature: Vec<u8>,
compressed_pub_key: Vec<u8>,
},
MisbehaviourReportsSubmitted {
misbehaviour_type: MisbehaviourType,
reporters: Vec<T::DKGId>,
offender: T::DKGId,
},
ProposerSetVotesSubmitted { voters: Vec<T::DKGId>, signatures: Vec<Vec<u8>>, vote: Vec<u8> },
RefreshKeysFinished { next_authority_set_id: dkg_runtime_primitives::AuthoritySetId },
NextKeygenThresholdUpdated { next_keygen_threshold: u16 },
NextSignatureThresholdUpdated { next_signature_threshold: u16 },
PendingKeygenThresholdUpdated { pending_keygen_threshold: u16 },
PendingSignatureThresholdUpdated { pending_signature_threshold: u16 },
EmergencyKeygenTriggered,
AuthorityJailed { misbehaviour_type: MisbehaviourType, authority: T::DKGId },
AuthorityUnJailed { authority: T::DKGId },
}
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub authorities: Vec<T::DKGId>,
pub keygen_threshold: u16,
pub signature_threshold: u16,
pub authority_ids: Vec<T::AccountId>,
}
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
Self {
authorities: Vec::new(),
signature_threshold: 1,
keygen_threshold: 3,
authority_ids: Vec::new(),
}
}
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
assert!(
self.signature_threshold < self.keygen_threshold,
"Signature threshold must be less than keygen threshold"
);
assert!(self.keygen_threshold > 1, "Keygen threshold must be greater than 1");
assert!(
self.authority_ids.len() >= self.keygen_threshold as usize,
"Not enough authority ids specified"
);
SignatureThreshold::<T>::put(self.signature_threshold);
KeygenThreshold::<T>::put(self.keygen_threshold);
NextSignatureThreshold::<T>::put(self.signature_threshold);
NextKeygenThreshold::<T>::put(self.keygen_threshold);
PendingSignatureThreshold::<T>::put(self.signature_threshold);
PendingKeygenThreshold::<T>::put(self.keygen_threshold);
RefreshNonce::<T>::put(0);
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(<T as Config>::WeightInfo::set_signature_threshold())]
#[pallet::call_index(0)]
pub fn set_signature_threshold(
origin: OriginFor<T>,
new_threshold: u16,
) -> DispatchResultWithPostInfo {
T::ForceOrigin::ensure_origin(origin)?;
ensure!(new_threshold > 0, Error::<T>::InvalidThreshold);
ensure!(
usize::from(new_threshold) < NextAuthorities::<T>::get().len(),
Error::<T>::InvalidThreshold
);
PendingSignatureThreshold::<T>::try_mutate(|threshold| {
*threshold = new_threshold;
Self::deposit_event(Event::PendingSignatureThresholdUpdated {
pending_signature_threshold: new_threshold,
});
Ok(().into())
})
}
#[pallet::weight(<T as Config>::WeightInfo::set_keygen_threshold())]
#[pallet::call_index(1)]
pub fn set_keygen_threshold(
origin: OriginFor<T>,
new_threshold: u16,
) -> DispatchResultWithPostInfo {
T::ForceOrigin::ensure_origin(origin)?;
ensure!(new_threshold > 1, Error::<T>::InvalidThreshold);
ensure!(
usize::from(new_threshold) <= NextAuthorities::<T>::get().len(),
Error::<T>::InvalidThreshold
);
if new_threshold <= PendingSignatureThreshold::<T>::get() {
let pending_signature_threshold = new_threshold.saturating_sub(1);
Self::update_signature_threshold(pending_signature_threshold)?;
Self::deposit_event(Event::PendingSignatureThresholdUpdated {
pending_signature_threshold,
});
}
Self::update_keygen_threshold(new_threshold)?;
Self::deposit_event(Event::PendingKeygenThresholdUpdated {
pending_keygen_threshold: new_threshold,
});
Ok(().into())
}
#[pallet::weight(<T as Config>::WeightInfo::submit_public_key(keys_and_signatures.keys_and_signatures.len() as u32))]
#[pallet::call_index(2)]
pub fn submit_public_key(
origin: OriginFor<T>,
keys_and_signatures: AggregatedPublicKeys,
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;
ensure!(!DKGPublicKey::<T>::exists(), Error::<T>::AlreadySubmittedPublicKey);
let authorities: Vec<T::DKGId> =
Self::best_authorities().iter().map(|id| id.1.clone()).collect();
let dict = Self::process_public_key_submissions(keys_and_signatures, authorities)?;
let threshold = Self::signature_threshold();
let mut accepted = false;
for (key, reporters) in dict.iter() {
if reporters.len() >= threshold.into() {
let bounded_key: BoundedVec<_, _> =
key.clone().try_into().map_err(|_| Error::<T>::OutOfBounds)?;
DKGPublicKey::<T>::put((Self::authority_set_id(), bounded_key));
Self::deposit_event(Event::PublicKeySubmitted {
compressed_pub_key: key.clone(),
});
accepted = true;
for authority in reporters {
let decay: Percent = T::DecayPercentage::get();
let reputation = AuthorityReputations::<T>::get(authority.clone());
AuthorityReputations::<T>::insert(
authority,
decay.mul_floor(reputation).saturating_add(INITIAL_REPUTATION.into()),
);
}
break
}
}
if accepted {
let current_block = <frame_system::Pallet<T>>::block_number();
<NextUnsignedAt<T>>::put(current_block + T::UnsignedInterval::get());
return Ok(().into())
}
Err(Error::<T>::InvalidPublicKeys.into())
}
#[pallet::weight(<T as Config>::WeightInfo::submit_next_public_key(keys_and_signatures.keys_and_signatures.len() as u32))]
#[pallet::call_index(3)]
pub fn submit_next_public_key(
origin: OriginFor<T>,
keys_and_signatures: AggregatedPublicKeys,
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;
ensure!(!NextDKGPublicKey::<T>::exists(), Error::<T>::AlreadySubmittedPublicKey);
let next_authorities: Vec<T::DKGId> =
Self::next_best_authorities().iter().map(|id| id.1.clone()).collect();
let dict = Self::process_public_key_submissions(keys_and_signatures, next_authorities)?;
let threshold = Self::next_signature_threshold();
let mut keys = dict.iter();
let accepted_key = loop {
if let Some((key, accounts)) = keys.next() {
if accounts.len() >= threshold.into() {
let bounded_key: BoundedVec<_, _> =
key.clone().try_into().map_err(|_| Error::<T>::OutOfBounds)?;
NextDKGPublicKey::<T>::put((Self::next_authority_set_id(), bounded_key));
for authority in accounts {
let reputation = AuthorityReputations::<T>::get(authority.clone());
AuthorityReputations::<T>::insert(
authority,
reputation.saturating_add(REPUTATION_INCREMENT.into()),
);
}
Self::deposit_event(Event::NextPublicKeySubmitted {
compressed_pub_key: key.clone(),
});
break Some((Self::next_authority_set_id(), key.clone()))
}
} else {
break None
}
};
if let Some((set_id, key)) = accepted_key {
let current_block = <frame_system::Pallet<T>>::block_number();
<NextUnsignedAt<T>>::put(current_block + T::UnsignedInterval::get());
RefreshInProgress::<T>::put(false);
log::debug!(
target: "runtime::dkg_metadata",
"Next DKG Public Key: {}, Authority Set ID: {}",
hex::encode(key),
set_id,
);
Ok(().into())
} else {
Err(Error::<T>::InvalidPublicKeys.into())
}
}
#[pallet::weight(<T as Config>::WeightInfo::submit_misbehaviour_reports(reports.reporters.len() as u32))]
#[pallet::call_index(5)]
pub fn submit_misbehaviour_reports(
origin: OriginFor<T>,
reports: AggregatedMisbehaviourReports<
T::DKGId,
T::MaxSignatureLength,
T::MaxReporters,
>,
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;
let offender = reports.offender.clone();
ensure!(
!JailedKeygenAuthorities::<T>::contains_key(&offender),
Error::<T>::AlreadyJailed
);
ensure!(
!JailedSigningAuthorities::<T>::contains_key(&offender),
Error::<T>::AlreadyJailed
);
let misbehaviour_type = reports.misbehaviour_type;
let authorities = match misbehaviour_type {
MisbehaviourType::Keygen => Self::next_authorities(),
MisbehaviourType::Sign => Self::authorities(),
};
ensure!(authorities.contains(&offender), Error::<T>::OffenderNotAuthority);
let valid_reporters = Self::process_misbehaviour_reports(reports, authorities.into());
let signature_threshold = match misbehaviour_type {
MisbehaviourType::Keygen => Self::next_signature_threshold(),
MisbehaviourType::Sign => Self::signature_threshold(),
};
if valid_reporters.len() > signature_threshold.into() {
let reputation = AuthorityReputations::<T>::get(&offender);
let decay = T::DecayPercentage::get();
AuthorityReputations::<T>::insert(&offender, decay.mul_floor(reputation));
let now = frame_system::Pallet::<T>::block_number();
match misbehaviour_type {
MisbehaviourType::Keygen => {
let unjailed_authorities = Self::next_best_authorities()
.into_iter()
.filter(|(_, id)| !JailedKeygenAuthorities::<T>::contains_key(id))
.map(|(_, id)| id)
.collect::<Vec<T::DKGId>>();
if unjailed_authorities.contains(&offender) {
JailedKeygenAuthorities::<T>::insert(&offender, now);
Self::deposit_event(Event::AuthorityJailed {
misbehaviour_type,
authority: offender.clone(),
});
let non_jailed_non_next_best_authorities = Self::next_authorities()
.into_iter()
.filter(|id| !unjailed_authorities.contains(id))
.filter(|id| !JailedKeygenAuthorities::<T>::contains_key(id))
.collect::<Vec<T::DKGId>>();
if !non_jailed_non_next_best_authorities.is_empty() {
let mut authorities_ordered_by_reputation =
AuthorityReputations::<T>::iter()
.filter(|id| {
non_jailed_non_next_best_authorities.contains(&id.0)
})
.collect::<Vec<(T::DKGId, T::Reputation)>>();
authorities_ordered_by_reputation.sort_by(|a, b| a.1.cmp(&b.1));
let highest_reputation_authority =
if let Some(most_reputed_authority) =
authorities_ordered_by_reputation.pop()
{
most_reputed_authority.0
} else {
non_jailed_non_next_best_authorities[0].clone()
};
let next_best_authorities: BoundedVec<_, _> =
Self::get_best_authorities(
Self::next_keygen_threshold() as usize,
&unjailed_authorities
.into_iter()
.filter(|id| *id != offender)
.chain(vec![highest_reputation_authority])
.collect::<Vec<_>>(),
)
.try_into()
.map_err(|_| Error::<T>::OutOfBounds)?;
ensure!(
next_best_authorities.len() >= 2,
Error::<T>::NotEnoughAuthoritiesToJail
);
NextBestAuthorities::<T>::put(next_best_authorities);
Self::deposit_event(Event::MisbehaviourReportsSubmitted {
misbehaviour_type,
reporters: valid_reporters,
offender,
});
return Ok(().into())
}
if unjailed_authorities.len() <= Self::next_keygen_threshold().into() {
if unjailed_authorities.len() > 2 {
let new_val = u16::try_from(unjailed_authorities.len() - 1)
.unwrap_or_default();
Self::update_next_keygen_threshold(new_val);
Self::update_pending_keygen_threshold(new_val);
if NextSignatureThreshold::<T>::get() >=
NextKeygenThreshold::<T>::get()
{
let next_signature_threshold =
NextSignatureThreshold::<T>::get();
if next_signature_threshold != 1 {
NextSignatureThreshold::<T>::put(
next_signature_threshold - 1,
);
PendingSignatureThreshold::<T>::put(
next_signature_threshold - 1,
);
}
}
}
}
}
let next_best_authorities: BoundedVec<_, _> = Self::get_best_authorities(
Self::next_keygen_threshold() as usize,
&unjailed_authorities
.into_iter()
.filter(|id| *id != offender)
.collect::<Vec<_>>(),
)
.try_into()
.map_err(|_| Error::<T>::OutOfBounds)?;
ensure!(
next_best_authorities.len() >= 2,
Error::<T>::NotEnoughAuthoritiesToJail
);
NextBestAuthorities::<T>::put(next_best_authorities);
},
MisbehaviourType::Sign => {
let unjailed_authorities = Self::best_authorities()
.into_iter()
.filter(|(_, id)| {
!JailedSigningAuthorities::<T>::contains_key(id) || *id != offender
})
.map(|(_, id)| id)
.collect::<Vec<T::DKGId>>();
if unjailed_authorities.len() < signature_threshold.into() {
if !unjailed_authorities.is_empty() {
JailedSigningAuthorities::<T>::insert(offender.clone(), now);
let new_val = u16::try_from(unjailed_authorities.len() - 1)
.unwrap_or_default();
Self::update_next_signature_threshold(new_val);
PendingSignatureThreshold::<T>::put(new_val);
}
} else {
JailedSigningAuthorities::<T>::insert(offender.clone(), now);
Self::deposit_event(Event::AuthorityJailed {
misbehaviour_type,
authority: offender.clone(),
});
}
},
};
Self::deposit_event(Event::MisbehaviourReportsSubmitted {
misbehaviour_type,
reporters: valid_reporters,
offender,
});
let current_block = <frame_system::Pallet<T>>::block_number();
<NextUnsignedAt<T>>::put(current_block + T::UnsignedInterval::get());
return Ok(().into())
}
Err(Error::<T>::InvalidMisbehaviourReports.into())
}
#[pallet::weight(<T as Config>::WeightInfo::unjail())]
#[pallet::call_index(6)]
pub fn unjail(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
let origin = ensure_signed(origin)?;
let authority =
T::AuthorityIdOf::convert(origin).ok_or(Error::<T>::InvalidControllerAccount)?;
if frame_system::Pallet::<T>::block_number() >
JailedKeygenAuthorities::<T>::get(authority.clone())
.saturating_add(T::KeygenJailSentence::get())
{
JailedKeygenAuthorities::<T>::remove(authority.clone());
}
if frame_system::Pallet::<T>::block_number() >
JailedSigningAuthorities::<T>::get(authority.clone())
.saturating_add(T::SigningJailSentence::get())
{
JailedSigningAuthorities::<T>::remove(authority);
}
Ok(().into())
}
#[pallet::weight(<T as Config>::WeightInfo::force_unjail_keygen())]
#[pallet::call_index(7)]
pub fn force_unjail_keygen(
origin: OriginFor<T>,
authority: T::DKGId,
) -> DispatchResultWithPostInfo {
T::ForceOrigin::ensure_origin(origin)?;
JailedKeygenAuthorities::<T>::remove(authority.clone());
Self::deposit_event(Event::AuthorityUnJailed { authority });
Ok(().into())
}
#[pallet::weight(<T as Config>::WeightInfo::force_unjail_signing())]
#[pallet::call_index(8)]
pub fn force_unjail_signing(
origin: OriginFor<T>,
authority: T::DKGId,
) -> DispatchResultWithPostInfo {
T::ForceOrigin::ensure_origin(origin)?;
JailedSigningAuthorities::<T>::remove(authority.clone());
Self::deposit_event(Event::AuthorityUnJailed { authority });
Ok(().into())
}
#[pallet::weight({0})]
#[pallet::call_index(9)]
pub fn force_change_authorities(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
T::ForceOrigin::ensure_origin(origin)?;
let next_authorities = NextAuthorities::<T>::get();
let next_authority_accounts = NextAuthoritiesAccounts::<T>::get();
let next_pub_key = Self::next_dkg_public_key();
PendingRefreshProposal::<T>::kill();
CurrentRefreshProposal::<T>::kill();
Self::change_authorities(
next_authorities.clone().into(),
next_authorities.into(),
next_authority_accounts.clone().into(),
next_authority_accounts.into(),
true,
);
if let Some(pub_key) = next_pub_key {
ShouldSubmitProposerVote::<T>::put(true);
let next_nonce = Self::refresh_nonce();
let data = Self::create_refresh_proposal(pub_key.1.into(), next_nonce)?;
match T::ProposalHandler::handle_unsigned_proposal(data) {
Ok(()) => {
RefreshInProgress::<T>::put(true);
RefreshNonce::<T>::put(next_nonce);
log::debug!("Handled refresh proposal");
},
Err(e) => {
log::warn!("Failed to handle refresh proposal: {:?}", e);
},
}
}
Ok(().into())
}
#[pallet::weight({0})]
#[pallet::call_index(10)]
pub fn trigger_emergency_keygen(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
T::ForceOrigin::ensure_origin(origin)?;
NextDKGPublicKey::<T>::kill();
NextPublicKeySignature::<T>::kill();
Self::deposit_event(Event::EmergencyKeygenTriggered);
<ShouldExecuteNewKeygen<T>>::put((true, true));
Ok(().into())
}
}
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
match source {
TransactionSource::Local | TransactionSource::InBlock => {},
_ => return InvalidTransaction::Call.into(),
}
let current_block = <frame_system::Pallet<T>>::block_number();
let next_unsigned_at = <NextUnsignedAt<T>>::get();
if next_unsigned_at > current_block {
log::debug!(
target: "runtime::dkg_metadata",
"validate unsigned: early block: current: {:?}, next_unsigned_at: {:?}",
current_block,
next_unsigned_at,
);
return InvalidTransaction::Stale.into()
}
let is_valid_call = matches! {
call,
Call::submit_public_key { .. } |
Call::submit_next_public_key { .. } |
Call::submit_misbehaviour_reports { .. }
};
if !is_valid_call {
log::warn!(
target: "runtime::dkg_metadata",
"validate unsigned: invalid call: {:?}",
call,
);
InvalidTransaction::Call.into()
} else {
log::debug!(
target: "runtime::dkg_metadata",
"validate unsigned: valid call: {:?}",
call,
);
ValidTransaction::with_tag_prefix("DKG")
.priority(T::UnsignedPriority::get())
.longevity(5)
.and_provides(current_block)
.propagate(true)
.build()
}
}
}
}
impl<T: Config> Pallet<T> {
pub fn authority_set() -> AuthoritySet<T::DKGId, T::MaxAuthorities> {
AuthoritySet::<T::DKGId, T::MaxAuthorities> {
authorities: Self::authorities(),
id: Self::authority_set_id(),
}
}
pub fn next_authority_set() -> AuthoritySet<T::DKGId, T::MaxAuthorities> {
AuthoritySet::<T::DKGId, T::MaxAuthorities> {
authorities: Self::next_authorities(),
id: Self::next_authority_set_id(),
}
}
pub fn update_signature_threshold(new_threshold: u16) -> DispatchResultWithPostInfo {
PendingSignatureThreshold::<T>::try_mutate(|threshold| {
*threshold = new_threshold;
Ok(().into())
})
}
pub fn update_keygen_threshold(new_threshold: u16) -> DispatchResultWithPostInfo {
PendingKeygenThreshold::<T>::try_mutate(|threshold| {
*threshold = new_threshold;
Ok(().into())
})
}
pub fn decompress_public_key(compressed: Vec<u8>) -> Result<Vec<u8>, DispatchError> {
let result = libsecp256k1::PublicKey::parse_slice(
&compressed,
Some(libsecp256k1::PublicKeyFormat::Compressed),
)
.map(|pk| pk.serialize())
.map_err(|_| Error::<T>::InvalidPublicKeys)?;
if result.len() == 65 {
Ok(result[1..].to_vec())
} else {
Ok(result.to_vec())
}
}
pub fn do_refresh(pub_key: Vec<u8>) {
let next_nonce = Self::refresh_nonce() + 1u32;
let data = match Self::create_refresh_proposal(pub_key, next_nonce) {
Ok(data) => data,
Err(e) => {
log::warn!("Failed to create refresh proposal: {:?}", e);
return
},
};
match T::ProposalHandler::handle_unsigned_proposal(data) {
Ok(()) => {
RefreshInProgress::<T>::put(true);
log::debug!("Handled refresh proposal");
},
Err(e) => {
log::warn!("Failed to handle refresh proposal: {:?}", e);
},
}
}
pub fn create_refresh_proposal(
pub_key: Vec<u8>,
nonce: u32,
) -> Result<Proposal<T::MaxProposalLength>, DispatchError> {
let uncompressed_pub_key = Self::decompress_public_key(pub_key)?;
let VoterSetData {
voter_set_merkle_root,
average_session_length_in_millisecs,
voter_count,
} = Self::create_voter_set_data();
let proposal = RefreshProposal {
voter_merkle_root: voter_set_merkle_root,
session_length: average_session_length_in_millisecs,
voter_count,
nonce: nonce.into(),
pub_key: uncompressed_pub_key,
};
PendingRefreshProposal::<T>::put(proposal.clone());
let bounded_proposal_data: BoundedVec<u8, T::MaxProposalLength> =
proposal.encode().try_into().unwrap_or_default();
Ok(Proposal::Unsigned { kind: ProposalKind::Refresh, data: bounded_proposal_data })
}
fn create_voter_set_data() -> VoterSetData {
let voters = Self::next_authorities();
let voter_set_merkle_root = Self::get_voter_set_tree_root(&voters);
let average_session_length_in_blocks: u64 =
T::NextSessionRotation::average_session_length().try_into().unwrap_or_default();
let average_millisecs_per_block: u64 =
<T as pallet_timestamp::Config>::MinimumPeriod::get()
.saturating_mul(2u32.into())
.try_into()
.unwrap_or_default();
let average_session_length_in_millisecs =
average_session_length_in_blocks.saturating_mul(average_millisecs_per_block);
VoterSetData {
voter_set_merkle_root,
average_session_length_in_millisecs,
voter_count: voters.len() as u32,
}
}
pub fn pre_process_for_merkleize(voters: &[T::DKGId]) -> Vec<[u8; 32]> {
let height = Self::get_voter_set_tree_height(voters.len());
let mut base_layer: Vec<[u8; 32]> = voters
.iter()
.map(|account| T::DKGAuthorityToMerkleLeaf::convert(account.clone()))
.map(|account| keccak_256(&account))
.collect();
let two = 2;
while base_layer.len() != two.saturating_pow(height.try_into().unwrap_or_default()) {
base_layer.push(keccak_256(&[0u8]));
}
base_layer
}
pub fn next_layer(curr_layer: Vec<[u8; 32]>) -> Vec<[u8; 32]> {
let mut layer_above: Vec<[u8; 32]> = Vec::new();
let mut index = 0;
while index < curr_layer.len() {
let mut input_to_hash_as_vec: Vec<u8> = curr_layer[index].to_vec();
input_to_hash_as_vec.extend_from_slice(&curr_layer[index + 1][..]);
let input_to_hash_as_slice = &input_to_hash_as_vec[..];
layer_above.push(keccak_256(input_to_hash_as_slice));
index += 2;
}
layer_above
}
pub fn get_voter_set_tree_height(voter_count: usize) -> u32 {
if voter_count == 1 {
1
} else {
let two: u32 = 2;
let mut h = 0;
while two.saturating_pow(h) < voter_count as u32 {
h += 1;
}
h
}
}
pub fn get_voter_set_tree_root(voters: &[T::DKGId]) -> [u8; 32] {
let mut curr_layer = Self::pre_process_for_merkleize(voters);
let mut height = Self::get_voter_set_tree_height(voters.len());
while height > 0 {
curr_layer = Self::next_layer(curr_layer);
height -= 1;
}
let mut root = [0u8; 32];
root.copy_from_slice(&curr_layer[0][..]);
root
}
pub fn process_public_key_submissions(
aggregated_keys: AggregatedPublicKeys,
authorities: Vec<T::DKGId>,
) -> Result<BTreeMap<Vec<u8>, Vec<T::DKGId>>, DispatchError> {
let mut dict: BTreeMap<Vec<u8>, Vec<T::DKGId>> = BTreeMap::new();
for (pub_key, signature) in aggregated_keys.keys_and_signatures {
let maybe_signers = authorities
.iter()
.map(|x| {
ecdsa::Public(to_slice_33(&x.encode()).unwrap_or_else(|| {
panic!("Failed to convert account id to ecdsa public key")
}))
})
.collect::<Vec<ecdsa::Public>>();
let (maybe_authority, success) =
verify_signer_from_set_ecdsa(maybe_signers, &pub_key, &signature);
let authority = maybe_authority.ok_or(Error::<T>::CannotRetreiveSigner)?;
if success {
let authority = T::DKGId::from(authority);
if !dict.contains_key(&pub_key) {
dict.insert(pub_key.clone(), Vec::new());
}
let temp = dict
.get_mut(&pub_key)
.expect("this should never panic, key inserted previously!");
if !temp.contains(&authority) {
temp.push(authority);
}
}
}
Ok(dict)
}
pub fn process_misbehaviour_reports(
reports: AggregatedMisbehaviourReports<T::DKGId, T::MaxSignatureLength, T::MaxReporters>,
verifying_set: Vec<T::DKGId>,
) -> Vec<T::DKGId> {
let mut valid_reporters = Vec::new();
for (inx, signature) in reports.signatures.iter().enumerate() {
let mut signed_payload = Vec::new();
signed_payload.extend_from_slice(&match reports.misbehaviour_type {
MisbehaviourType::Keygen => [0x01],
MisbehaviourType::Sign => [0x02],
});
signed_payload.extend_from_slice(reports.session_id.to_be_bytes().as_ref());
signed_payload.extend_from_slice(reports.offender.as_ref());
let verifying_set: Vec<ecdsa::Public> = verifying_set
.iter()
.map(|x| match to_slice_33(x.encode().as_ref()) {
Some(x) => ecdsa::Public(x),
None => ecdsa::Public([0u8; 33]),
})
.filter(|x| x.0 != [0u8; 33])
.collect();
let (_, success) =
verify_signer_from_set_ecdsa(verifying_set, &signed_payload, signature);
if success && !valid_reporters.contains(&reports.reporters[inx]) {
valid_reporters.push(reports.reporters[inx].clone());
}
}
valid_reporters
}
pub fn store_consensus_log(
authority_ids: BoundedVec<T::DKGId, T::MaxAuthorities>,
next_authority_ids: BoundedVec<T::DKGId, T::MaxAuthorities>,
active_set_id: dkg_runtime_primitives::AuthoritySetId,
) {
let log: DigestItem = DigestItem::Consensus(
DKG_ENGINE_ID,
ConsensusLog::AuthoritiesChange {
active: AuthoritySet::<T::DKGId, T::MaxAuthorities> {
authorities: authority_ids,
id: active_set_id,
},
queued: AuthoritySet::<T::DKGId, T::MaxAuthorities> {
authorities: next_authority_ids,
id: active_set_id.saturating_add(1),
},
}
.encode(),
);
<frame_system::Pallet<T>>::deposit_log(log);
}
fn change_authorities(
new_authority_ids: Vec<T::DKGId>,
next_authority_ids: Vec<T::DKGId>,
new_authorities_accounts: Vec<T::AccountId>,
next_authorities_accounts: Vec<T::AccountId>,
forced: bool,
) {
<T::OnAuthoritySetChangeHandler as OnAuthoritySetChangeHandler<
T::AccountId,
dkg_runtime_primitives::AuthoritySetId,
T::DKGId,
>>::on_authority_set_changed(&new_authorities_accounts, &new_authority_ids);
RefreshInProgress::<T>::put(false);
let new_current_signature_threshold = NextSignatureThreshold::<T>::get();
let new_current_keygen_threshold = NextKeygenThreshold::<T>::get();
Self::update_next_signature_threshold(PendingSignatureThreshold::<T>::get());
Self::update_next_keygen_threshold(PendingKeygenThreshold::<T>::get());
let next_id = Self::next_authority_set_id();
let bounded_next_authority_ids: BoundedVec<_, _> = next_authority_ids
.clone()
.try_into()
.expect("This should never overflow, since read from bounded storage");
NextAuthorities::<T>::put(&bounded_next_authority_ids);
let bounded_next_authorities_accounts: BoundedVec<_, _> = next_authorities_accounts
.try_into()
.expect("This should never overflow, since read from bounded storage");
NextAuthoritiesAccounts::<T>::put(&bounded_next_authorities_accounts);
NextAuthoritySetId::<T>::put(next_id.saturating_add(1));
let new_best_authorities = Self::next_best_authorities();
let next_pub_key = Self::next_dkg_public_key();
let next_pub_key_signature = Self::next_public_key_signature();
let dkg_pub_key = Self::dkg_public_key();
let pub_key_signature = Self::public_key_signature();
if next_authority_ids.len() < Self::next_keygen_threshold().into() {
Self::update_next_keygen_threshold(next_authority_ids.len() as u16);
PendingKeygenThreshold::<T>::put(next_authority_ids.len() as u16);
}
if next_authority_ids.len() <= Self::next_signature_threshold().into() {
Self::update_next_signature_threshold(next_authority_ids.len() as u16 - 1);
PendingSignatureThreshold::<T>::put(next_authority_ids.len() as u16 - 1);
}
let bounded_authorities: BoundedVec<_, _> =
Self::get_best_authorities(Self::next_keygen_threshold() as usize, &next_authority_ids)
.try_into()
.expect("This should never overflow, since read from bounded storage");
NextBestAuthorities::<T>::put(bounded_authorities);
let v = if forced {
next_pub_key.zip(Some(Default::default()))
} else {
next_pub_key.zip(next_pub_key_signature)
};
if let Some((next_pub_key, next_pub_key_signature)) = v {
SignatureThreshold::<T>::put(new_current_signature_threshold);
KeygenThreshold::<T>::put(new_current_keygen_threshold);
let bounded_authority_ids: BoundedVec<_, _> = new_authority_ids
.try_into()
.expect("This should never overflow, since read from bounded storage");
Authorities::<T>::put(&bounded_authority_ids);
let bounded_authority_accounts: BoundedVec<_, _> = new_authorities_accounts
.try_into()
.expect("This should never overflow, since read from bounded storage");
CurrentAuthoritiesAccounts::<T>::put(&bounded_authority_accounts);
BestAuthorities::<T>::put(new_best_authorities);
AuthoritySetId::<T>::put(next_id);
Self::store_consensus_log(bounded_authority_ids, bounded_next_authority_ids, next_id);
NextDKGPublicKey::<T>::kill();
NextPublicKeySignature::<T>::kill();
Self::insert_historical_refresh(
&(dkg_pub_key.0, dkg_pub_key.clone().1),
&(next_pub_key.0, next_pub_key.clone().1),
next_pub_key_signature.clone(),
);
DKGPublicKey::<T>::put(next_pub_key.clone());
DKGPublicKeySignature::<T>::put(next_pub_key_signature.clone());
PreviousPublicKey::<T>::put(dkg_pub_key);
let _ = UsedSignatures::<T>::try_mutate(|val| {
let added = val.try_push(pub_key_signature.clone());
if added.is_err() {
log::warn!(
target: "runtime::dkg_metadata",
"UsedSignature limit reached!",
);
};
added
});
let current_nonce = Self::refresh_nonce();
let next_nonce = current_nonce.saturating_add(1);
RefreshNonce::<T>::put(next_nonce);
let compressed_pub_key = next_pub_key.1.clone();
Self::deposit_event(Event::PublicKeyChanged {
compressed_pub_key: compressed_pub_key.clone().into(),
});
if let Some(curr) = CurrentRefreshProposal::<T>::get() {
Self::deposit_event(Event::PublicKeySignatureChanged {
voter_merkle_root: curr.voter_merkle_root,
session_length: curr.session_length,
voter_count: curr.voter_count,
nonce: curr.nonce,
pub_key: curr.pub_key,
signature: next_pub_key_signature.into(),
compressed_pub_key: compressed_pub_key.to_vec(),
});
CurrentRefreshProposal::<T>::kill();
}
}
}
fn initialize_authorities(authorities: &[T::DKGId], authority_account_ids: &[T::AccountId]) {
if authorities.is_empty() {
log::warn!(
target: "runtime::dkg_metadata",
"trying to intialize the autorities with empty list!",
);
return
}
log::debug!(
target: "runtime::dkg_metadata",
"intializing the authorities with: {:?} and account ids: {:?}",
authorities,
authority_account_ids,
);
assert!(Authorities::<T>::get().is_empty(), "Authorities are already initialized!");
let bounded_authorities: BoundedVec<_, _> =
authorities.to_vec().try_into().expect("Genesis Authorities out of bounds!");
Authorities::<T>::put(bounded_authorities.clone());
AuthoritySetId::<T>::put(0);
let bounded_authority_account_ids: BoundedVec<_, _> = authority_account_ids
.to_vec()
.try_into()
.expect("Genesis Authorities accounts out of bounds!");
CurrentAuthoritiesAccounts::<T>::put(bounded_authority_account_ids.clone());
NextAuthorities::<T>::put(bounded_authorities);
NextAuthoritySetId::<T>::put(1);
NextAuthoritiesAccounts::<T>::put(bounded_authority_account_ids);
let best_authorities =
Self::get_best_authorities(Self::keygen_threshold() as usize, authorities);
log::debug!(
target: "runtime::dkg_metadata",
"best_authorities: {:?}",
best_authorities,
);
let bounded_best_authorities: BoundedVec<_, _> = best_authorities
.to_vec()
.try_into()
.expect("Genesis Best Authorities out of bounds!");
BestAuthorities::<T>::put(bounded_best_authorities);
let next_best_authorities =
Self::get_best_authorities(Self::keygen_threshold() as usize, authorities);
let bounded_next_best_authorities: BoundedVec<_, _> = next_best_authorities
.to_vec()
.try_into()
.expect("Genesis Next Best Authorities out of bounds!");
NextBestAuthorities::<T>::put(bounded_next_best_authorities);
<T::OnAuthoritySetChangeHandler as OnAuthoritySetChangeHandler<
T::AccountId,
dkg_runtime_primitives::AuthoritySetId,
T::DKGId,
>>::on_authority_set_changed(authority_account_ids, authorities);
}
fn submit_genesis_public_key_onchain(
block_number: BlockNumberFor<T>,
) -> Result<(), &'static str> {
let next_unsigned_at = <NextUnsignedAt<T>>::get();
if next_unsigned_at > block_number {
return Err("Too early to send unsigned transaction")
}
let mut lock = StorageLock::<Time>::new(AGGREGATED_PUBLIC_KEYS_AT_GENESIS_LOCK);
{
let _guard = lock.lock();
let mut agg_key_ref = StorageValueRef::persistent(AGGREGATED_PUBLIC_KEYS_AT_GENESIS);
let mut submit_at_ref = StorageValueRef::persistent(SUBMIT_GENESIS_KEYS_AT);
const RECENTLY_SENT: &str = "Already submitted a key in this session";
let submit_at = submit_at_ref.get::<BlockNumberFor<T>>();
let agg_keys = agg_key_ref.get::<AggregatedPublicKeys>();
if let Ok(None) = agg_keys {
return Ok(())
}
if let Ok(Some(submit_at)) = submit_at {
if block_number < submit_at {
log::debug!(target: "runtime::dkg_metadata", "Offchain worker skipping public key submmission");
return Ok(())
} else {
submit_at_ref.clear();
}
} else {
return Err(RECENTLY_SENT)
}
if !Self::dkg_public_key().1.is_empty() {
agg_key_ref.clear();
return Ok(())
}
if let Ok(Some(agg_keys)) = agg_keys {
let res = SubmitTransaction::<T, Call<T>>::submit_unsigned_transaction(
Call::submit_public_key { keys_and_signatures: agg_keys }.into(),
)
.map_err(|_| "Failed to submit transaction");
match res {
Ok(_) => {
agg_key_ref.clear();
},
Err(e) => {
log::error!(target: "runtime::dkg_metadata", "Error: {:?}", e);
return Err("Failed to submit the public key, will retry later")
},
};
}
Ok(())
}
}
fn submit_next_public_key_onchain(block_number: BlockNumberFor<T>) -> Result<(), &'static str> {
let next_unsigned_at = <NextUnsignedAt<T>>::get();
if next_unsigned_at > block_number {
return Err("Too early to send unsigned transaction")
}
let mut lock = StorageLock::<Time>::new(AGGREGATED_PUBLIC_KEYS_LOCK);
{
let _guard = lock.lock();
let mut agg_key_ref = StorageValueRef::persistent(AGGREGATED_PUBLIC_KEYS);
let mut submit_at_ref = StorageValueRef::persistent(SUBMIT_KEYS_AT);
const RECENTLY_SENT: &str = "Already submitted a key in this session";
let submit_at = submit_at_ref.get::<BlockNumberFor<T>>();
let agg_keys = agg_key_ref.get::<AggregatedPublicKeys>();
if let Ok(None) = agg_keys {
return Ok(())
}
if let Ok(Some(submit_at)) = submit_at {
if block_number < submit_at {
log::debug!(target: "runtime::dkg_metadata", "Offchain worker skipping next public key submmission");
return Ok(())
} else {
submit_at_ref.clear();
}
} else {
return Err(RECENTLY_SENT)
}
if Self::next_dkg_public_key().is_some() {
agg_key_ref.clear();
return Ok(())
}
if let Ok(Some(agg_keys)) = agg_keys {
let res = SubmitTransaction::<T, Call<T>>::submit_unsigned_transaction(
Call::submit_next_public_key { keys_and_signatures: agg_keys }.into(),
)
.map_err(|_| "Failed to submit transaction");
match res {
Ok(_) => {
agg_key_ref.clear();
},
Err(e) => {
log::error!(target: "runtime::dkg_metadata", "Error: {:?}", e);
return Err("Failed to submit the next public key, will retry later")
},
};
}
Ok(())
}
}
fn submit_misbehaviour_reports_onchain(
block_number: BlockNumberFor<T>,
) -> Result<(), &'static str> {
let next_unsigned_at = <NextUnsignedAt<T>>::get();
if next_unsigned_at > block_number {
return Err("Too early to send unsigned transaction")
}
let mut lock = StorageLock::<Time>::new(AGGREGATED_MISBEHAVIOUR_REPORTS_LOCK);
{
let _guard = lock.lock();
let signer = Signer::<T, T::OffChainAuthId>::any_account();
if !signer.can_sign() {
return Err(
"No local accounts available. Consider adding one via `author_insertKey` RPC.",
)
}
let mut agg_reports_ref = StorageValueRef::persistent(AGGREGATED_MISBEHAVIOUR_REPORTS);
let agg_misbehaviour_reports = agg_reports_ref.get::<AggregatedMisbehaviourReports<
T::DKGId,
T::MaxSignatureLength,
T::MaxReporters,
>>();
if let Ok(Some(reports)) = agg_misbehaviour_reports {
if Self::misbehaviour_reports((
reports.misbehaviour_type,
reports.session_id,
reports.offender.clone(),
))
.is_some()
{
agg_reports_ref.clear();
return Ok(())
}
let res = SubmitTransaction::<T, Call<T>>::submit_unsigned_transaction(
Call::submit_misbehaviour_reports { reports }.into(),
)
.map_err(|_| "Failed to submit transaction");
match res {
Ok(_) => {
agg_reports_ref.clear();
},
Err(e) => {
log::error!(target: "runtime::dkg_metadata", "Error: {:?}", e);
return Err("Failed to submit the misbehaviour reports, will retry later")
},
};
}
Ok(())
}
}
pub fn update_next_keygen_threshold(next_threshold: u16) {
let current_next_keygen_threshold = Self::next_keygen_threshold();
if current_next_keygen_threshold != next_threshold {
NextKeygenThreshold::<T>::put(next_threshold);
Self::deposit_event(Event::NextKeygenThresholdUpdated {
next_keygen_threshold: next_threshold,
});
}
}
pub fn update_pending_keygen_threshold(next_threshold: u16) {
let current_pending_keygen_threshold = Self::pending_keygen_threshold();
if current_pending_keygen_threshold != next_threshold {
PendingKeygenThreshold::<T>::put(next_threshold);
Self::deposit_event(Event::PendingKeygenThresholdUpdated {
pending_keygen_threshold: next_threshold,
});
}
}
pub fn update_next_signature_threshold(next_threshold: u16) {
let current_next_signature_threshold = Self::next_signature_threshold();
if current_next_signature_threshold != next_threshold {
NextSignatureThreshold::<T>::put(next_threshold);
Self::deposit_event(Event::NextSignatureThresholdUpdated {
next_signature_threshold: next_threshold,
});
}
}
pub fn should_refresh(_now: BlockNumberFor<T>) -> bool {
let next_dkg_public_key = Self::next_dkg_public_key();
let next_dkg_public_key_signature = Self::next_public_key_signature();
next_dkg_public_key.is_some() && next_dkg_public_key_signature.is_none()
}
pub fn insert_historical_refresh(
dkg_pub_key: &(dkg_runtime_primitives::AuthoritySetId, BoundedVec<u8, T::MaxKeyLength>),
next_pub_key: &(dkg_runtime_primitives::AuthoritySetId, BoundedVec<u8, T::MaxKeyLength>),
next_pub_key_signature: BoundedVec<u8, T::MaxSignatureLength>,
) {
HistoricalRounds::<T>::insert(
next_pub_key.0,
RoundMetadata {
curr_round_pub_key: dkg_pub_key.1.clone(),
next_round_pub_key: next_pub_key.clone().1,
refresh_signature: next_pub_key_signature,
},
);
}
pub fn get_best_authorities_by_reputation(
count: usize,
authorities: &[T::DKGId],
) -> Vec<(u16, T::DKGId)> {
let mut reputations_of_authorities = authorities
.iter()
.map(|id| (AuthorityReputations::<T>::get(id), id))
.collect::<Vec<(_, _)>>();
reputations_of_authorities.sort_by(|a, b| b.0.cmp(&a.0));
return reputations_of_authorities
.iter()
.take(count)
.cloned()
.enumerate()
.map(|(i, (_, id))| ((i + 1) as u16, id.clone()))
.collect()
}
pub fn get_best_authorities(count: usize, authorities: &[T::DKGId]) -> Vec<(u16, T::DKGId)> {
let jailed_authorities = authorities
.iter()
.cloned()
.filter(|id| JailedKeygenAuthorities::<T>::contains_key(id))
.collect::<Vec<T::DKGId>>();
let mut best_authorities = authorities
.iter()
.cloned()
.filter(|id| !JailedKeygenAuthorities::<T>::contains_key(id))
.collect::<Vec<T::DKGId>>();
if best_authorities.len() < count {
let best_jailed = Self::get_best_authorities_by_reputation(
count - best_authorities.len(),
&jailed_authorities,
);
best_authorities.extend(best_jailed.iter().map(|x| x.1.clone()));
}
Self::get_best_authorities_by_reputation(count, &best_authorities)
}
#[cfg(feature = "runtime-benchmarks")]
pub fn set_dkg_public_key(key: BoundedVec<u8, T::MaxKeyLength>) {
DKGPublicKey::<T>::put((0, key))
}
}
impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
type Public = T::DKGId;
}
impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
type Key = T::DKGId;
fn on_genesis_session<'a, I: 'a>(validators: I)
where
I: Iterator<Item = (&'a T::AccountId, T::DKGId)>,
{
log::debug!(target: "runtime::dkg_metadata", "on_genesis_session");
let mut authority_account_ids = Vec::new();
let authorities = validators
.map(|(l, k)| {
authority_account_ids.push(l.clone());
k
})
.collect::<Vec<_>>();
Self::initialize_authorities(&authorities, &authority_account_ids);
}
fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, queued_validators: I)
where
I: Iterator<Item = (&'a T::AccountId, T::DKGId)>,
{
let mut authority_account_ids = Vec::new();
let mut queued_authority_account_ids = Vec::new();
let next_authorities = validators
.map(|(acc, k)| {
authority_account_ids.push(acc.clone());
AccountToAuthority::<T>::insert(acc, k.clone());
k
})
.collect::<Vec<_>>();
let next_queued_authorities = queued_validators
.map(|(acc, k)| {
queued_authority_account_ids.push(acc.clone());
AccountToAuthority::<T>::insert(acc, k.clone());
k
})
.collect::<Vec<_>>();
LastSessionRotationBlock::<T>::put(frame_system::Pallet::<T>::block_number());
Self::change_authorities(
next_authorities,
next_queued_authorities,
authority_account_ids,
queued_authority_account_ids,
false,
);
}
fn on_disabled(i: u32) {
let log: DigestItem = DigestItem::Consensus(
DKG_ENGINE_ID,
ConsensusLog::<T::DKGId, T::MaxAuthorities>::OnDisabled(i as AuthorityIndex).encode(),
);
<frame_system::Pallet<T>>::deposit_log(log);
}
}
impl<T: Config> IsMember<T::DKGId> for Pallet<T> {
fn is_member(authority_id: &T::DKGId) -> bool {
Self::authorities().iter().any(|id| id == authority_id)
}
}
impl<T: Config> GetDKGPublicKey for Pallet<T> {
fn dkg_key() -> Vec<u8> {
Self::dkg_public_key().1.into()
}
fn previous_dkg_key() -> Vec<u8> {
Self::previous_public_key().1.into()
}
}
pub struct DKGPeriodicSessions<Period, Offset, T>(PhantomData<(Period, Offset, T)>);
impl<
BlockNumber: Rem<Output = BlockNumber> + Sub<Output = BlockNumber> + Zero + PartialOrd,
Period: Get<BlockNumber>,
Offset: Get<BlockNumber>,
T: Config,
> pallet_session::ShouldEndSession<BlockNumber> for DKGPeriodicSessions<Period, Offset, T>
{
fn should_end_session(now: BlockNumber) -> bool {
let offset = Offset::get();
let next_public_key_exists = NextDKGPublicKey::<T>::get().is_some();
let next_public_key_signature_exists = NextPublicKeySignature::<T>::get().is_some();
next_public_key_exists &&
next_public_key_signature_exists &&
now >= offset &&
((now - offset) % Period::get()) >= Zero::zero()
}
}
impl<
BlockNumber: AtLeast32BitUnsigned + Clone + core::fmt::Debug + sp_std::convert::From<BlockNumberFor<T>>,
Period: Get<BlockNumber>,
Offset: Get<BlockNumber>,
T: Config + pallet_session::Config,
> EstimateNextSessionRotation<BlockNumber> for DKGPeriodicSessions<Period, Offset, T>
{
fn average_session_length() -> BlockNumber {
Period::get()
}
fn estimate_current_session_progress(now: BlockNumber) -> (Option<Permill>, Weight) {
let offset = Offset::get();
let period = Period::get();
let progress = if now >= offset {
let last_rotated_block = LastSessionRotationBlock::<T>::get();
let current_elapsed_time = now.clone().saturating_sub(last_rotated_block.into());
if current_elapsed_time > period {
Some(Permill::from_percent(100))
} else {
let current = (now - offset) % period.clone() + One::one();
Some(Permill::from_rational(current, period))
}
} else {
Some(Permill::from_rational(now + One::one(), offset))
};
(progress, Zero::zero())
}
fn estimate_next_session_rotation(now: BlockNumber) -> (Option<BlockNumber>, Weight) {
let offset = Offset::get();
let period = Period::get();
let next_session = if now > offset {
let block_after_last_session = (now.clone() - offset) % period.clone();
if block_after_last_session > Zero::zero() {
now.saturating_add(period.saturating_sub(block_after_last_session))
} else {
now + period
}
} else {
offset
};
(Some(next_session), Zero::zero())
}
}
use dkg_runtime_primitives::traits::OnSignedProposal;
use webb_proposals::ProposalKind;
impl<T: Config> OnSignedProposal<T::MaxProposalLength> for Pallet<T> {
fn on_signed_proposal(proposal: Proposal<T::MaxProposalLength>) -> Result<(), DispatchError> {
ensure!(proposal.is_signed(), Error::<T>::ProposalNotSigned);
if proposal.kind() == ProposalKind::Refresh {
let (_, next_pub_key) =
Self::next_dkg_public_key().ok_or(Error::<T>::NoNextPublicKey)?;
ensure!(
Self::next_public_key_signature().is_none(),
Error::<T>::AlreadySubmittedSignature
);
let maybe_prop = Self::pending_refresh_proposal();
ensure!(maybe_prop.is_some(), Error::<T>::NoRefreshProposal);
let curr = maybe_prop.unwrap_or_default();
ensure!(curr.encode() == proposal.data().clone(), Error::<T>::InvalidRefreshProposal);
let bounded_signature: BoundedVec<_, _> = proposal
.signature()
.unwrap_or_default()
.try_into()
.map_err(|_| Error::<T>::InvalidSignature)?;
ensure!(
!Self::used_signatures().contains(&bounded_signature),
Error::<T>::UsedSignature
);
NextPublicKeySignature::<T>::put(bounded_signature);
CurrentRefreshProposal::<T>::put(curr.clone());
PendingRefreshProposal::<T>::kill();
Self::deposit_event(Event::NextPublicKeySignatureSubmitted {
signature: proposal.signature().unwrap_or_default(),
voter_merkle_root: curr.voter_merkle_root,
session_length: curr.session_length,
voter_count: curr.voter_count,
nonce: curr.nonce,
pub_key: curr.pub_key,
compressed_pub_key: next_pub_key.into(),
});
};
Ok(())
}
}