Skip to content

Commit

Permalink
Optimise signed proposal submission (#595)
Browse files Browse the repository at this point in the history
* add round robin for submitting prop on chain

* fix clippy
  • Loading branch information
1xstj authored May 4, 2023
1 parent 4a6709d commit c7810c4
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 9 deletions.
2 changes: 1 addition & 1 deletion pallets/dkg-metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ pub mod pallet {
/// Accounts for the current authorities
#[pallet::storage]
#[pallet::getter(fn current_authorities_accounts)]
pub(super) type CurrentAuthoritiesAccounts<T: Config> =
pub type CurrentAuthoritiesAccounts<T: Config> =
StorageValue<_, BoundedVec<T::AccountId, T::MaxAuthorities>, ValueQuery>;

/// Authority account ids scheduled for the next session
Expand Down
61 changes: 53 additions & 8 deletions pallets/dkg-proposal-handler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,14 @@ use dkg_runtime_primitives::{
StoredUnsignedProposal, TypedChainId,
};
use frame_support::pallet_prelude::*;
use frame_system::offchain::{AppCrypto, SendSignedTransaction, Signer};
use frame_system::offchain::{AppCrypto, SendSignedTransaction, SignMessage, Signer};
pub use pallet::*;
use sp_runtime::{
offchain::{
storage::StorageValueRef,
storage_lock::{StorageLock, Time},
},
traits::Saturating,
traits::{Saturating, Zero},
};
use sp_std::{convert::TryInto, vec::Vec};
use webb_proposals::{OnSignedProposal, Proposal, ProposalKind};
Expand Down Expand Up @@ -756,6 +756,24 @@ impl<T: Config> Pallet<T> {
StoredUnsignedProposalOf::<T> { proposal, timestamp }
}

// ** Calculate the turn of authorities to submit transactions **
// we use a simple round robin algorithm to determine who submits the proposal on-chain, this
// avoids all the validators trying to submit at the same time.
fn get_expected_signer(block_number: T::BlockNumber) -> Option<T::AccountId> {
let current_authorities = pallet_dkg_metadata::CurrentAuthoritiesAccounts::<T>::get();
let block_as_u32: u32 = block_number.try_into().unwrap_or_default();

// sanity check
if current_authorities.is_empty() || block_as_u32.is_zero() {
// we can safely return None here,
// the calling function will submit the proposal anyway
return None
}

let submitter_index: u32 = block_as_u32 % current_authorities.len() as u32;
current_authorities.get(submitter_index as usize).cloned()
}

// *** Offchain worker methods ***

/// Offchain worker function that submits signed proposals from the offchain storage on-chain
Expand All @@ -769,23 +787,50 @@ impl<T: Config> Pallet<T> {
if next_unsigned_at > block_number {
return Err("Too early to send unsigned transaction")
}

// ensure we have the signer setup
let signer = Signer::<T, <T as Config>::OffChainAuthId>::all_accounts();
if !signer.can_sign() {
return Err(
"No local accounts available. Consider adding one via `author_insertKey` RPC.",
)
}

// check if its our turn to submit proposals
if let Some(expected_signer_account) = Self::get_expected_signer(block_number) {
// the signer does not have a method to read all available public keys, we instead sign
// a dummy message and read the current pub key from the signature.
let signature = signer.sign_message(b"test");
let account: &T::AccountId =
&signature.first().expect("Unable to retreive signed message").0.id; // the unwrap here is ok since we checked if can_sign() is true above

if account != &expected_signer_account {
log::debug!(
target: "runtime::dkg_proposal_handler",
"submit_signed_proposal_onchain: Not our turn to sign, selected signer is {:?}",
expected_signer_account
);
return Ok(())
}
}

let mut lock = StorageLock::<Time>::new(SUBMIT_SIGNED_PROPOSAL_ON_CHAIN_LOCK);
{
let _guard = lock.lock();

let signer = Signer::<T, <T as Config>::OffChainAuthId>::all_accounts();
if !signer.can_sign() {
return Err(
"No local accounts available. Consider adding one via `author_insertKey` RPC.",
)
}
match Self::get_next_offchain_signed_proposal(block_number) {
Ok(next_proposals) => {
log::debug!(
target: "runtime::dkg_proposal_handler",
"submit_signed_proposal_onchain: found {} proposals to submit before filtering\n {:?}",
next_proposals.len(), next_proposals
);

// early exit if nothing to submit
if next_proposals.is_empty() {
return Ok(())
}

// We filter out all proposals that are already on chain
let filtered_proposals = next_proposals
.iter()
Expand Down

0 comments on commit c7810c4

Please sign in to comment.