Skip to content

Commit 4d19e09

Browse files
committed
feat(data_structures): adapt stakes tracker to latest specs
fix #2398
1 parent f9790bc commit 4d19e09

File tree

11 files changed

+773
-487
lines changed

11 files changed

+773
-487
lines changed

data_structures/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,7 @@ rand_distr = "0.4.3"
5151
[[bench]]
5252
name = "sort_active_identities"
5353
harness = false
54+
55+
[[bench]]
56+
name = "staking"
57+
harness = false

data_structures/benches/staking.rs

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#[macro_use]
2+
extern crate bencher;
3+
use bencher::Bencher;
4+
use rand::Rng;
5+
use witnet_data_structures::staking::prelude::*;
6+
7+
fn populate(b: &mut Bencher) {
8+
let mut stakes = Stakes::<String, u64, u64, u64>::default();
9+
let mut i = 1;
10+
11+
b.iter(|| {
12+
let address = format!("{i}");
13+
let coins = i;
14+
let epoch = i;
15+
stakes.add_stake(address, coins, epoch).unwrap();
16+
17+
i += 1;
18+
});
19+
}
20+
21+
fn rank(b: &mut Bencher) {
22+
let mut stakes = Stakes::<String, u64, u64, u64>::default();
23+
let mut i = 1;
24+
25+
let stakers = 100_000;
26+
let rf = 10;
27+
28+
let mut rng = rand::thread_rng();
29+
30+
loop {
31+
let coins = i;
32+
let epoch = i;
33+
let address = format!("{}", rng.gen::<u64>());
34+
35+
stakes.add_stake(address, coins, epoch).unwrap();
36+
37+
i += 1;
38+
39+
if i == stakers {
40+
break;
41+
}
42+
}
43+
44+
b.iter(|| {
45+
let rank = stakes.rank(Capability::Mining, i);
46+
let mut top = rank.take(usize::try_from(stakers / rf).unwrap());
47+
let _first = top.next();
48+
let _last = top.last();
49+
50+
i += 1;
51+
})
52+
}
53+
54+
fn query_power(b: &mut Bencher) {
55+
let mut stakes = Stakes::<String, u64, u64, u64>::default();
56+
let mut i = 1;
57+
58+
let stakers = 100_000;
59+
60+
loop {
61+
let coins = i;
62+
let epoch = i;
63+
let address = format!("{i}");
64+
65+
stakes.add_stake(address, coins, epoch).unwrap();
66+
67+
i += 1;
68+
69+
if i == stakers {
70+
break;
71+
}
72+
}
73+
74+
i = 1;
75+
76+
b.iter(|| {
77+
let address = format!("{i}");
78+
let _power = stakes.query_power(&address, Capability::Mining, i);
79+
80+
i += 1;
81+
})
82+
}
83+
84+
benchmark_main!(benches);
85+
benchmark_group!(benches, populate, rank, query_power);

data_structures/src/capabilities.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#[repr(u8)]
2+
#[derive(Clone, Copy, Debug)]
3+
pub enum Capability {
4+
/// The base block mining and superblock voting capability
5+
Mining = 0,
6+
/// The universal HTTP GET / HTTP POST / WIP-0019 RNG capability
7+
Witnessing = 1,
8+
}
9+
10+
#[derive(Copy, Clone, Debug, Default, PartialEq)]
11+
pub struct CapabilityMap<T>
12+
where
13+
T: Default,
14+
{
15+
pub mining: T,
16+
pub witnessing: T,
17+
}
18+
19+
impl<T> CapabilityMap<T>
20+
where
21+
T: Copy + Default,
22+
{
23+
#[inline]
24+
pub fn get(&self, capability: Capability) -> T {
25+
match capability {
26+
Capability::Mining => self.mining,
27+
Capability::Witnessing => self.witnessing,
28+
}
29+
}
30+
31+
#[inline]
32+
pub fn update(&mut self, capability: Capability, value: T) {
33+
match capability {
34+
Capability::Mining => self.mining = value,
35+
Capability::Witnessing => self.witnessing = value,
36+
}
37+
}
38+
39+
#[inline]
40+
pub fn update_all(&mut self, value: T) {
41+
self.mining = value;
42+
self.witnessing = value;
43+
}
44+
}

data_structures/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ mod serialization_helpers;
7272
/// Provides convenient constants, structs and methods for handling values denominated in Wit.
7373
pub mod wit;
7474

75+
/// Provides support for segmented protocol capabilities.
76+
pub mod capabilities;
77+
7578
lazy_static! {
7679
/// Environment in which we are running: mainnet or testnet.
7780
/// This is used for Bech32 serialization.

data_structures/src/staking/aux.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use std::rc::Rc;
2+
use std::sync::RwLock;
3+
4+
use super::prelude::*;
5+
6+
/// Type alias for a reference-counted and read-write-locked instance of `Stake`.
7+
pub type SyncStake<Address, Coins, Epoch, Power> = Rc<RwLock<Stake<Address, Coins, Epoch, Power>>>;
8+
9+
/// The resulting type for all the fallible functions in this module.
10+
pub type Result<T, Address, Coins, Epoch> =
11+
std::result::Result<T, StakesError<Address, Coins, Epoch>>;
12+
13+
/// Couples an amount of coins and an address together. This is to be used in `Stakes` as the index
14+
/// of the `by_coins` index..
15+
#[derive(Eq, Ord, PartialEq, PartialOrd)]
16+
pub struct CoinsAndAddress<Coins, Address> {
17+
/// An amount of coins.
18+
pub coins: Coins,
19+
/// The address of a staker.
20+
pub address: Address,
21+
}
22+
23+
/// Allows telling the `census` method in `Stakes` to source addresses from its internal `by_coins`
24+
/// following different strategies.
25+
#[repr(u8)]
26+
#[derive(Clone, Copy, Debug)]
27+
pub enum CensusStrategy {
28+
/// Retrieve all addresses, ordered by decreasing power.
29+
All = 0,
30+
/// Retrieve every Nth address, ordered by decreasing power.
31+
StepBy(usize) = 1,
32+
/// Retrieve the most powerful N addresses, ordered by decreasing power.
33+
Take(usize) = 2,
34+
/// Retrieve a total of N addresses, evenly distributed from the index, ordered by decreasing
35+
/// power.
36+
Evenly(usize) = 3,
37+
}
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/// A minimum stakeable amount needs to exist to prevent spamming of the tracker.
2+
pub const MINIMUM_STAKEABLE_AMOUNT_WITS: u64 = 100;

data_structures/src/staking/errors.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use std::sync::PoisonError;
2+
3+
/// All errors related to the staking functionality.
4+
#[derive(Debug, PartialEq)]
5+
pub enum StakesError<Address, Coins, Epoch> {
6+
/// The amount of coins being staked or the amount that remains after unstaking is below the
7+
/// minimum stakeable amount.
8+
AmountIsBelowMinimum {
9+
/// The number of coins being staked or remaining after staking.
10+
amount: Coins,
11+
/// The minimum stakeable amount.
12+
minimum: Coins,
13+
},
14+
/// Tried to query `Stakes` for information that belongs to the past.
15+
EpochInThePast {
16+
/// The Epoch being referred.
17+
epoch: Epoch,
18+
/// The latest Epoch.
19+
latest: Epoch,
20+
},
21+
/// An operation thrown an Epoch value that overflows.
22+
EpochOverflow {
23+
/// The computed Epoch value.
24+
computed: u64,
25+
/// The maximum Epoch.
26+
maximum: Epoch,
27+
},
28+
/// Tried to query `Stakes` for the address of a staker that is not registered in `Stakes`.
29+
IdentityNotFound {
30+
/// The unknown address.
31+
identity: Address,
32+
},
33+
/// Tried to obtain a lock on a write-locked piece of data that is already locked.
34+
PoisonedLock,
35+
}
36+
37+
impl<T, Address, Coins, Epoch> From<PoisonError<T>> for StakesError<Address, Coins, Epoch> {
38+
fn from(_value: PoisonError<T>) -> Self {
39+
StakesError::PoisonedLock
40+
}
41+
}

0 commit comments

Comments
 (0)