Skip to content

Commit

Permalink
feat: optimise storage access rate chore mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
neutrinoks committed Mar 8, 2024
1 parent feb5ee4 commit 6b065c3
Showing 1 changed file with 57 additions and 61 deletions.
118 changes: 57 additions & 61 deletions pallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub mod pallet {
use frame_support::{
dispatch::DispatchResultWithPostInfo,
pallet_prelude::*,
parameter_types,
traits::{
tokens::currency::LockIdentifier, Currency, Get, LockableCurrency, ReservableCurrency,
},
Expand All @@ -57,6 +58,7 @@ pub mod pallet {
bytecode::verify_script_integrity_and_check_signers, types::ScriptTransaction,
};
use sp_core::crypto::AccountId32;
use sp_runtime::traits::AtLeast32BitUnsigned;
use sp_std::{vec, vec::Vec};

use super::*;
Expand All @@ -68,8 +70,9 @@ pub mod pallet {

type MvmResult<T> = Result<Mvm<StorageAdapter<VMStorage<T>>, BalanceAdapter<T>>, Vec<u8>>;

/// Maximum number of multisig storage entries to be checked per block.
const MAX_MULTISIG_CHECKING_PER_BLOCK: u64 = 20;
parameter_types! {
pub const MaxChoreEntriesPerVec: u32 = 128;
}

#[pallet::pallet]
#[pallet::without_storage_info] // Allows to define storage items without fixed size
Expand All @@ -85,9 +88,13 @@ pub mod pallet {
#[pallet::storage]
pub type MultisigStorage<T> = StorageMap<_, Blake2_128Concat, CallHash, MultisigOf<T>>;

/// Storage for chore mechanism for old Multisigs in `MultisigStorage`.
#[pallet::storage]
pub type ChoreOnIdleStorage<T> = StorageValue<_, u64>;
pub type ChoreOnIdleStorage<T> = StorageMap<
_,
Blake2_128Concat,
BlockNumberFor<T>,
BoundedVec<CallHash, MaxChoreEntriesPerVec>,
>;

/// MoveVM pallet configuration trait
#[pallet::config]
Expand All @@ -99,7 +106,7 @@ pub mod pallet {

/// Just the `Currency::Balance` type; we have this item to allow us to
/// constrain it to `From<u128>` and `Into<u128>`.
type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
type CurrencyBalance: AtLeast32BitUnsigned
+ FullCodec
+ Copy
+ MaybeSerializeDeserialize
Expand Down Expand Up @@ -184,59 +191,24 @@ pub mod pallet {

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_idle(block_height: BlockNumberFor<T>, mut remaining_weight: Weight) -> Weight {
let len = MultisigStorage::<T>::iter_keys().count() as u64;
let lifetime = T::MaxLifetimeRequests::get();
// Abort if storage is empty or the allowed lifetime is longer than the blockchain's
// existence (otherwise, an integer underflow can occur).
if len == 0 || block_height < lifetime {
fn on_idle(block_height: BlockNumberFor<T>, remaining_weight: Weight) -> Weight {
// Check storage, if we have an entry for this block.
let chore_info = if let Ok(entry) = ChoreOnIdleStorage::<T>::try_get(block_height) {
entry
} else {
return remaining_weight - T::DbWeight::get().reads(1);
}

// We will read three times for sure and write one time for sure for the storage,
// no matter if we execute the internal chore method or not.
remaining_weight -= T::DbWeight::get().reads_writes(3, 1);

let mut idx: u64 = ChoreOnIdleStorage::<T>::get().unwrap_or(0);
if idx >= len {
idx = 0;
}

let keys = MultisigStorage::<T>::iter_keys().skip(idx as usize);
let block_tbr = block_height - lifetime;
let mut call = Vec::<CallHash>::new();
let mut count: u64 = 0;

for key in keys {
if let Some(call_hash) = Self::chore_multisig_storage(key, block_tbr) {
call.push(call_hash);
}
count += 1;
if let Some(weight) =
remaining_weight.checked_sub(&T::WeightInfo::chore_multisig_storage())
{
remaining_weight = weight;
if count >= MAX_MULTISIG_CHECKING_PER_BLOCK {
break;
}
} else {
remaining_weight = Weight::zero();
break;
}
}
};

let n_removed = call.len() as u64;
idx += count - n_removed;
if idx >= len - n_removed {
idx = 0;
// Remove all that entries from MultisigStorage.
for hash in chore_info.iter() {
MultisigStorage::<T>::remove(hash);
}
ChoreOnIdleStorage::<T>::put(idx);

if !call.is_empty() {
Self::deposit_event(Event::<T>::MultiSignRequestRemoved { call });
}
// Emit event about removed old multi-signer execution requests.
let call: Vec<CallHash> = chore_info.clone().into();
Self::deposit_event(Event::<T>::MultiSignRequestRemoved { call });

remaining_weight
remaining_weight - T::DbWeight::get().reads_writes(1, (chore_info.len() as u64) + 1)
}
}

Expand Down Expand Up @@ -302,7 +274,9 @@ pub mod pallet {
// blockchain storage or the balance sheet.
// If we have only one signer, it will skip this; if not, we have to wait for more signers, so we store it as a multi-signer request.
if !signature_handler.all_signers_approved() {
MultisigStorage::<T>::insert(call_hash, signature_handler.into_inner());
let multisig = signature_handler.into_inner();
Self::new_multi_sign_request_chore(*multisig.block_number(), call_hash)?;
MultisigStorage::<T>::insert(call_hash, multisig);
Self::deposit_event(Event::SignedMultisigScript { who });
return result::execute_only_signing();
}
Expand Down Expand Up @@ -552,14 +526,34 @@ pub mod pallet {
Ok(accounts)
}

fn chore_multisig_storage(key: CallHash, block_tbr: BlockNumberFor<T>) -> Option<CallHash> {
let multisig = MultisigStorage::<T>::get(key)?;
if *multisig.block_number() > block_tbr {
None
} else {
MultisigStorage::<T>::remove(key);
Some(key)
fn new_multi_sign_request_chore(
block_height: BlockNumberFor<T>,
hash: CallHash,
) -> Result<(), Error<T>> {
// Increase the actual block-height by the maximum lifetime of a request.
let block_height = block_height + T::MaxLifetimeRequests::get();

// Check for an existing entry or create an empty one.
let mut entry: BoundedVec<CallHash, MaxChoreEntriesPerVec> =
ChoreOnIdleStorage::<T>::try_get(block_height).unwrap_or_default();

// Check if this hash is already included, then it is ok.
for h in entry.iter() {
if *h == hash {
return Ok(());
}
}

// Verify that it is not full, in case it already exists. Then add this new request.
if entry.is_full() {
return Err(Error::<T>::ChoreOnIdleVecOverflow);
}
entry.force_push(hash);

// Store entry to `ChoreOnIdleStorage`.
ChoreOnIdleStorage::<T>::insert(block_height, entry);

Ok(())
}

pub fn transaction_bc_call_hash(transaction_bc: &[u8]) -> CallHash {
Expand Down Expand Up @@ -606,6 +600,8 @@ pub mod pallet {
UserHasAlreadySigned,
/// Script contains more signers than allowed maximum number of signers.
MaxSignersExceeded,
/// No space left in the ChoreOnIdleStorage during this block.
ChoreOnIdleVecOverflow,

// Errors that can be received from MoveVM
/// Unknown validation status
Expand Down

0 comments on commit 6b065c3

Please sign in to comment.