Skip to content

Commit

Permalink
feat(data_structures): adapt stakes tracker to latest specs
Browse files Browse the repository at this point in the history
fix #2398
  • Loading branch information
aesedepece committed Oct 18, 2023
1 parent f9790bc commit 4d19e09
Show file tree
Hide file tree
Showing 11 changed files with 773 additions and 487 deletions.
4 changes: 4 additions & 0 deletions data_structures/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,7 @@ rand_distr = "0.4.3"
[[bench]]
name = "sort_active_identities"
harness = false

[[bench]]
name = "staking"
harness = false
85 changes: 85 additions & 0 deletions data_structures/benches/staking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#[macro_use]
extern crate bencher;
use bencher::Bencher;
use rand::Rng;
use witnet_data_structures::staking::prelude::*;

fn populate(b: &mut Bencher) {
let mut stakes = Stakes::<String, u64, u64, u64>::default();
let mut i = 1;

b.iter(|| {
let address = format!("{i}");
let coins = i;
let epoch = i;
stakes.add_stake(address, coins, epoch).unwrap();

i += 1;
});
}

fn rank(b: &mut Bencher) {
let mut stakes = Stakes::<String, u64, u64, u64>::default();
let mut i = 1;

let stakers = 100_000;
let rf = 10;

let mut rng = rand::thread_rng();

loop {
let coins = i;
let epoch = i;
let address = format!("{}", rng.gen::<u64>());

stakes.add_stake(address, coins, epoch).unwrap();

i += 1;

if i == stakers {
break;
}
}

b.iter(|| {
let rank = stakes.rank(Capability::Mining, i);
let mut top = rank.take(usize::try_from(stakers / rf).unwrap());
let _first = top.next();
let _last = top.last();

i += 1;
})
}

fn query_power(b: &mut Bencher) {
let mut stakes = Stakes::<String, u64, u64, u64>::default();
let mut i = 1;

let stakers = 100_000;

loop {
let coins = i;
let epoch = i;
let address = format!("{i}");

stakes.add_stake(address, coins, epoch).unwrap();

i += 1;

if i == stakers {
break;
}
}

i = 1;

b.iter(|| {
let address = format!("{i}");
let _power = stakes.query_power(&address, Capability::Mining, i);

i += 1;
})
}

benchmark_main!(benches);
benchmark_group!(benches, populate, rank, query_power);
44 changes: 44 additions & 0 deletions data_structures/src/capabilities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum Capability {
/// The base block mining and superblock voting capability
Mining = 0,
/// The universal HTTP GET / HTTP POST / WIP-0019 RNG capability
Witnessing = 1,
}

#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct CapabilityMap<T>
where
T: Default,
{
pub mining: T,
pub witnessing: T,
}

impl<T> CapabilityMap<T>
where
T: Copy + Default,
{
#[inline]
pub fn get(&self, capability: Capability) -> T {
match capability {
Capability::Mining => self.mining,
Capability::Witnessing => self.witnessing,
}
}

#[inline]
pub fn update(&mut self, capability: Capability, value: T) {
match capability {
Capability::Mining => self.mining = value,
Capability::Witnessing => self.witnessing = value,
}
}

#[inline]
pub fn update_all(&mut self, value: T) {
self.mining = value;
self.witnessing = value;
}
}
3 changes: 3 additions & 0 deletions data_structures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ mod serialization_helpers;
/// Provides convenient constants, structs and methods for handling values denominated in Wit.
pub mod wit;

/// Provides support for segmented protocol capabilities.
pub mod capabilities;

lazy_static! {
/// Environment in which we are running: mainnet or testnet.
/// This is used for Bech32 serialization.
Expand Down
37 changes: 37 additions & 0 deletions data_structures/src/staking/aux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::rc::Rc;
use std::sync::RwLock;

use super::prelude::*;

/// Type alias for a reference-counted and read-write-locked instance of `Stake`.
pub type SyncStake<Address, Coins, Epoch, Power> = Rc<RwLock<Stake<Address, Coins, Epoch, Power>>>;

/// The resulting type for all the fallible functions in this module.
pub type Result<T, Address, Coins, Epoch> =
std::result::Result<T, StakesError<Address, Coins, Epoch>>;

/// Couples an amount of coins and an address together. This is to be used in `Stakes` as the index
/// of the `by_coins` index..
#[derive(Eq, Ord, PartialEq, PartialOrd)]
pub struct CoinsAndAddress<Coins, Address> {
/// An amount of coins.
pub coins: Coins,
/// The address of a staker.
pub address: Address,
}

/// Allows telling the `census` method in `Stakes` to source addresses from its internal `by_coins`
/// following different strategies.
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum CensusStrategy {
/// Retrieve all addresses, ordered by decreasing power.
All = 0,
/// Retrieve every Nth address, ordered by decreasing power.
StepBy(usize) = 1,
/// Retrieve the most powerful N addresses, ordered by decreasing power.
Take(usize) = 2,
/// Retrieve a total of N addresses, evenly distributed from the index, ordered by decreasing
/// power.
Evenly(usize) = 3,
}
2 changes: 2 additions & 0 deletions data_structures/src/staking/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// A minimum stakeable amount needs to exist to prevent spamming of the tracker.
pub const MINIMUM_STAKEABLE_AMOUNT_WITS: u64 = 100;
41 changes: 41 additions & 0 deletions data_structures/src/staking/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use std::sync::PoisonError;

/// All errors related to the staking functionality.
#[derive(Debug, PartialEq)]
pub enum StakesError<Address, Coins, Epoch> {
/// The amount of coins being staked or the amount that remains after unstaking is below the
/// minimum stakeable amount.
AmountIsBelowMinimum {
/// The number of coins being staked or remaining after staking.
amount: Coins,
/// The minimum stakeable amount.
minimum: Coins,
},
/// Tried to query `Stakes` for information that belongs to the past.
EpochInThePast {
/// The Epoch being referred.
epoch: Epoch,
/// The latest Epoch.
latest: Epoch,
},
/// An operation thrown an Epoch value that overflows.
EpochOverflow {
/// The computed Epoch value.
computed: u64,
/// The maximum Epoch.
maximum: Epoch,
},
/// Tried to query `Stakes` for the address of a staker that is not registered in `Stakes`.
IdentityNotFound {
/// The unknown address.
identity: Address,
},
/// Tried to obtain a lock on a write-locked piece of data that is already locked.
PoisonedLock,
}

impl<T, Address, Coins, Epoch> From<PoisonError<T>> for StakesError<Address, Coins, Epoch> {
fn from(_value: PoisonError<T>) -> Self {
StakesError::PoisonedLock
}
}
Loading

0 comments on commit 4d19e09

Please sign in to comment.