mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-10 04:44:40 +00:00
Grab up to 150 key shares of validators, not 150 validators
This commit is contained in:
parent
b0fcdd3367
commit
6587590986
1 changed files with 37 additions and 16 deletions
|
@ -69,11 +69,12 @@ pub mod pallet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The minimum allocation required to join a validator set.
|
/// The allocation required per key share.
|
||||||
// Uses Identity for the lookup to avoid a hash of a severely limited fixed key-space.
|
// Uses Identity for the lookup to avoid a hash of a severely limited fixed key-space.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
#[pallet::getter(fn minimum_allocation)]
|
#[pallet::getter(fn allocation_per_key_share)]
|
||||||
pub type MinimumAllocation<T: Config> = StorageMap<_, Identity, NetworkId, Amount, OptionQuery>;
|
pub type AllocationPerKeyShare<T: Config> =
|
||||||
|
StorageMap<_, Identity, NetworkId, Amount, OptionQuery>;
|
||||||
/// The validators selected to be in-set.
|
/// The validators selected to be in-set.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
#[pallet::getter(fn participants)]
|
#[pallet::getter(fn participants)]
|
||||||
|
@ -87,7 +88,6 @@ pub mod pallet {
|
||||||
/// The validators selected to be in-set, yet with the ability to perform a check for presence.
|
/// The validators selected to be in-set, yet with the ability to perform a check for presence.
|
||||||
// Uses Identity so we can call clear_prefix over network, manually inserting a Blake2 hash
|
// Uses Identity so we can call clear_prefix over network, manually inserting a Blake2 hash
|
||||||
// before the spammable key.
|
// before the spammable key.
|
||||||
// TODO: Review child trees?
|
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub type InSet<T: Config> =
|
pub type InSet<T: Config> =
|
||||||
StorageMap<_, Identity, (NetworkId, [u8; 16], Public), (), OptionQuery>;
|
StorageMap<_, Identity, (NetworkId, [u8; 16], Public), (), OptionQuery>;
|
||||||
|
@ -138,6 +138,18 @@ pub mod pallet {
|
||||||
let hash = sp_io::hashing::blake2_128(&(network, amount, key).encode());
|
let hash = sp_io::hashing::blake2_128(&(network, amount, key).encode());
|
||||||
(network, amount, hash, key)
|
(network, amount, hash, key)
|
||||||
}
|
}
|
||||||
|
fn recover_amount_from_sorted_allocation_key(key: &[u8]) -> Amount {
|
||||||
|
let distance_from_end = 8 + 16 + 32;
|
||||||
|
let start_pos = key.len() - distance_from_end;
|
||||||
|
let mut raw: [u8; 8] = key[start_pos .. (start_pos + 8)].try_into().unwrap();
|
||||||
|
for byte in &mut raw {
|
||||||
|
*byte = !*byte;
|
||||||
|
}
|
||||||
|
Amount(u64::from_be_bytes(raw))
|
||||||
|
}
|
||||||
|
fn recover_key_from_sorted_allocation_key(key: &[u8]) -> Public {
|
||||||
|
Public(key[(key.len() - 32) ..].try_into().unwrap())
|
||||||
|
}
|
||||||
fn set_allocation(network: NetworkId, key: Public, amount: Amount) {
|
fn set_allocation(network: NetworkId, key: Public, amount: Amount) {
|
||||||
let prior = Allocations::<T>::take((network, key));
|
let prior = Allocations::<T>::take((network, key));
|
||||||
if let Some(amount) = prior {
|
if let Some(amount) = prior {
|
||||||
|
@ -203,6 +215,8 @@ pub mod pallet {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let allocation_per_key_share = Self::allocation_per_key_share(network).unwrap().0;
|
||||||
|
|
||||||
let mut prefix = SortedAllocations::<T>::final_prefix().to_vec();
|
let mut prefix = SortedAllocations::<T>::final_prefix().to_vec();
|
||||||
prefix.extend(&network.encode());
|
prefix.extend(&network.encode());
|
||||||
let prefix = prefix;
|
let prefix = prefix;
|
||||||
|
@ -210,16 +224,23 @@ pub mod pallet {
|
||||||
let mut last = prefix.clone();
|
let mut last = prefix.clone();
|
||||||
|
|
||||||
let mut participants = vec![];
|
let mut participants = vec![];
|
||||||
for _ in 0 .. MAX_VALIDATORS_PER_SET {
|
let mut key_shares = 0;
|
||||||
|
while key_shares < u64::from(MAX_VALIDATORS_PER_SET) {
|
||||||
let Some(next) = sp_io::storage::next_key(&last) else { break };
|
let Some(next) = sp_io::storage::next_key(&last) else { break };
|
||||||
if !next.starts_with(&prefix) {
|
if !next.starts_with(&prefix) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let key = Public(next[(next.len() - 32) .. next.len()].try_into().unwrap());
|
let key = Self::recover_key_from_sorted_allocation_key(&next);
|
||||||
|
|
||||||
InSet::<T>::set(Self::in_set_key(network, key), Some(()));
|
InSet::<T>::set(Self::in_set_key(network, key), Some(()));
|
||||||
participants.push(key);
|
participants.push(key);
|
||||||
|
|
||||||
|
// This can technically set key_shares to a value exceeding MAX_VALIDATORS_PER_SET
|
||||||
|
// Off-chain, the key shares per validator will be accordingly adjusted
|
||||||
|
// TODO: Recover the amount from `next` to avoud a new storage lookup
|
||||||
|
key_shares +=
|
||||||
|
Self::recover_amount_from_sorted_allocation_key(&next).0 / allocation_per_key_share;
|
||||||
|
|
||||||
last = next;
|
last = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,10 +257,10 @@ pub mod pallet {
|
||||||
pub enum Error<T> {
|
pub enum Error<T> {
|
||||||
/// Validator Set doesn't exist.
|
/// Validator Set doesn't exist.
|
||||||
NonExistentValidatorSet,
|
NonExistentValidatorSet,
|
||||||
/// Not enough stake to participate in a set.
|
/// Not enough allocation to obtain a key share in the set.
|
||||||
InsufficientStake,
|
|
||||||
/// Trying to deallocate more than allocated.
|
|
||||||
InsufficientAllocation,
|
InsufficientAllocation,
|
||||||
|
/// Trying to deallocate more than allocated.
|
||||||
|
NotEnoughAllocated,
|
||||||
/// Deallocation would remove the participant from the set, despite the validator not
|
/// Deallocation would remove the participant from the set, despite the validator not
|
||||||
/// specifying so.
|
/// specifying so.
|
||||||
DeallocationWouldRemoveParticipant,
|
DeallocationWouldRemoveParticipant,
|
||||||
|
@ -263,7 +284,7 @@ pub mod pallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
for id in self.networks.clone() {
|
for id in self.networks.clone() {
|
||||||
MinimumAllocation::<T>::set(id, Some(self.stake));
|
AllocationPerKeyShare::<T>::set(id, Some(self.stake));
|
||||||
for participant in self.participants.clone() {
|
for participant in self.participants.clone() {
|
||||||
Pallet::<T>::set_allocation(id, participant, self.stake);
|
Pallet::<T>::set_allocation(id, participant, self.stake);
|
||||||
}
|
}
|
||||||
|
@ -334,8 +355,8 @@ pub mod pallet {
|
||||||
match Self::verify_signature(set, key_pair, signature) {
|
match Self::verify_signature(set, key_pair, signature) {
|
||||||
Err(Error::AlreadyGeneratedKeys) => Err(InvalidTransaction::Stale)?,
|
Err(Error::AlreadyGeneratedKeys) => Err(InvalidTransaction::Stale)?,
|
||||||
Err(Error::NonExistentValidatorSet) |
|
Err(Error::NonExistentValidatorSet) |
|
||||||
Err(Error::InsufficientStake) |
|
|
||||||
Err(Error::InsufficientAllocation) |
|
Err(Error::InsufficientAllocation) |
|
||||||
|
Err(Error::NotEnoughAllocated) |
|
||||||
Err(Error::DeallocationWouldRemoveParticipant) |
|
Err(Error::DeallocationWouldRemoveParticipant) |
|
||||||
Err(Error::NonExistentValidator) |
|
Err(Error::NonExistentValidator) |
|
||||||
Err(Error::BadSignature) => Err(InvalidTransaction::BadProof)?,
|
Err(Error::BadSignature) => Err(InvalidTransaction::BadProof)?,
|
||||||
|
@ -359,8 +380,8 @@ pub mod pallet {
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
) -> Result<(), Error<T>> {
|
) -> Result<(), Error<T>> {
|
||||||
let new_allocation = Self::allocation((network, account)).unwrap_or(Amount(0)).0 + amount.0;
|
let new_allocation = Self::allocation((network, account)).unwrap_or(Amount(0)).0 + amount.0;
|
||||||
if new_allocation < Self::minimum_allocation(network).unwrap().0 {
|
if new_allocation < Self::allocation_per_key_share(network).unwrap().0 {
|
||||||
Err(Error::<T>::InsufficientStake)?;
|
Err(Error::<T>::InsufficientAllocation)?;
|
||||||
}
|
}
|
||||||
Self::set_allocation(network, account, Amount(new_allocation));
|
Self::set_allocation(network, account, Amount(new_allocation));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -388,11 +409,11 @@ pub mod pallet {
|
||||||
.ok_or(Error::<T>::NonExistentValidator)?
|
.ok_or(Error::<T>::NonExistentValidator)?
|
||||||
.0
|
.0
|
||||||
.checked_sub(amount.0)
|
.checked_sub(amount.0)
|
||||||
.ok_or(Error::<T>::InsufficientAllocation)?;
|
.ok_or(Error::<T>::NotEnoughAllocated)?;
|
||||||
// If we're not removing the entire allocation, yet the allocation is no longer at or above
|
// If we're not removing the entire allocation, yet the allocation is no longer at or above
|
||||||
// the minimum stake, error
|
// the threshold for a key share, error
|
||||||
if (new_allocation != 0) &&
|
if (new_allocation != 0) &&
|
||||||
(new_allocation < Self::minimum_allocation(network).unwrap_or(Amount(0)).0)
|
(new_allocation < Self::allocation_per_key_share(network).unwrap_or(Amount(0)).0)
|
||||||
{
|
{
|
||||||
Err(Error::<T>::DeallocationWouldRemoveParticipant)?;
|
Err(Error::<T>::DeallocationWouldRemoveParticipant)?;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue