mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-11 13:39:25 +00:00
test whole pre ec security emissions
This commit is contained in:
@@ -3,7 +3,7 @@ use std::{time::Duration, collections::HashMap};
|
||||
use serai_client::TemporalSerai;
|
||||
|
||||
use serai_abi::{
|
||||
emissions::primitives::INITIAL_REWARD_PER_BLOCK,
|
||||
emissions::primitives::{INITIAL_REWARD_PER_BLOCK, SECURE_BY},
|
||||
primitives::{Coin, COINS, NETWORKS},
|
||||
};
|
||||
|
||||
@@ -25,101 +25,71 @@ async fn test_emissions(serai: Serai) {
|
||||
// provide some genesis liquidity
|
||||
test_genesis_liquidity(serai.clone()).await;
|
||||
|
||||
let mut current_stake = HashMap::new();
|
||||
for n in NETWORKS {
|
||||
let stake = serai
|
||||
.as_of_latest_finalized_block()
|
||||
.await
|
||||
.unwrap()
|
||||
.validator_sets()
|
||||
.total_allocated_stake(n)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap_or(Amount(0))
|
||||
.0;
|
||||
current_stake.insert(n, stake);
|
||||
}
|
||||
|
||||
// wait until we have at least 1 session, epoch time is half an hour with the fast epoch
|
||||
// feature, so lets wait double that.
|
||||
tokio::time::timeout(tokio::time::Duration::from_secs(60 * 3), async {
|
||||
while serai
|
||||
.as_of_latest_finalized_block()
|
||||
.await
|
||||
.unwrap()
|
||||
.validator_sets()
|
||||
.session(NetworkId::Serai)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.0 <
|
||||
1
|
||||
{
|
||||
tokio::time::sleep(Duration::from_secs(6)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let last_block = serai.latest_finalized_block().await.unwrap();
|
||||
let serai_latest = serai.as_of(last_block.hash());
|
||||
|
||||
// we should be in the initial period, so calculate how much each network supposedly get..
|
||||
// we can check the supply to see how much coin hence liability we have.
|
||||
let mut distances: HashMap<NetworkId, u64> = HashMap::new();
|
||||
let mut total_distance = 0;
|
||||
for coin in COINS {
|
||||
if coin == Coin::Serai {
|
||||
continue;
|
||||
let mut last_epoch_start = 0;
|
||||
for i in 1 .. 3 {
|
||||
let mut current_stake = HashMap::new();
|
||||
for n in NETWORKS {
|
||||
let stake = serai
|
||||
.as_of_latest_finalized_block()
|
||||
.await
|
||||
.unwrap()
|
||||
.validator_sets()
|
||||
.total_allocated_stake(n)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap_or(Amount(0))
|
||||
.0;
|
||||
current_stake.insert(n, stake);
|
||||
}
|
||||
|
||||
let amount = serai_latest.coins().coin_supply(coin).await.unwrap();
|
||||
let required = required_stake(&serai_latest, Balance { coin, amount }).await;
|
||||
let mut current = *current_stake.get(&coin.network()).unwrap();
|
||||
if current > required {
|
||||
current = required;
|
||||
// wait until we have at least 1 session
|
||||
wait_for_session(&serai, i).await;
|
||||
|
||||
// get distances to ec security
|
||||
let last_block = serai.latest_finalized_block().await.unwrap();
|
||||
let serai_latest = serai.as_of(last_block.hash());
|
||||
let (distances, total_distance) = get_distances(&serai_latest, ¤t_stake).await;
|
||||
|
||||
// calculate how much reward in this session
|
||||
let block_count = last_block.number() - last_epoch_start;
|
||||
let reward_this_epoch = if i == 1 {
|
||||
// last block number should be the block count since we are in the first block of session 1.
|
||||
block_count * INITIAL_REWARD_PER_BLOCK
|
||||
} else {
|
||||
let blocks_until = SECURE_BY - last_block.number();
|
||||
let block_reward = total_distance / blocks_until;
|
||||
block_count * block_reward
|
||||
};
|
||||
|
||||
let reward_per_network = distances
|
||||
.into_iter()
|
||||
.map(|(n, distance)| {
|
||||
let reward = u64::try_from(
|
||||
u128::from(reward_this_epoch).saturating_mul(u128::from(distance)) /
|
||||
u128::from(total_distance),
|
||||
)
|
||||
.unwrap();
|
||||
(n, reward)
|
||||
})
|
||||
.collect::<HashMap<NetworkId, u64>>();
|
||||
|
||||
for (n, reward) in reward_per_network {
|
||||
let stake = serai_latest
|
||||
.validator_sets()
|
||||
.total_allocated_stake(n)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap_or(Amount(0))
|
||||
.0;
|
||||
|
||||
// all reward should automatically staked for the network since we are in initial period.
|
||||
assert_eq!(stake, *current_stake.get(&n).unwrap() + reward);
|
||||
}
|
||||
// TODO: check stake per address?
|
||||
// TODO: check post ec security era
|
||||
|
||||
let distance = required - current;
|
||||
total_distance += distance;
|
||||
|
||||
distances.insert(
|
||||
coin.network(),
|
||||
distances.get(&coin.network()).unwrap_or(&0).saturating_add(distance),
|
||||
);
|
||||
last_epoch_start = last_block.number();
|
||||
}
|
||||
|
||||
// add serai network portion(20%)
|
||||
let new_total_distance = total_distance.saturating_mul(10) / 8;
|
||||
distances.insert(NetworkId::Serai, new_total_distance - total_distance);
|
||||
total_distance = new_total_distance;
|
||||
|
||||
// since we should be in the first block after the first epoch, block number should also
|
||||
// give us the block count.
|
||||
let block_count = last_block.number();
|
||||
let reward_this_epoch = block_count * INITIAL_REWARD_PER_BLOCK;
|
||||
|
||||
let reward_per_network = distances
|
||||
.into_iter()
|
||||
.map(|(n, distance)| {
|
||||
let reward = u64::try_from(
|
||||
u128::from(reward_this_epoch).saturating_mul(u128::from(distance)) /
|
||||
u128::from(total_distance),
|
||||
)
|
||||
.unwrap();
|
||||
(n, reward)
|
||||
})
|
||||
.collect::<HashMap<NetworkId, u64>>();
|
||||
|
||||
for (n, reward) in reward_per_network {
|
||||
let stake =
|
||||
serai_latest.validator_sets().total_allocated_stake(n).await.unwrap().unwrap_or(Amount(0)).0;
|
||||
|
||||
// the reward should have been automatically staked for the network
|
||||
assert_eq!(stake, *current_stake.get(&n).unwrap() + reward);
|
||||
}
|
||||
|
||||
// TODO: check stake per address
|
||||
}
|
||||
|
||||
/// Returns the required stake in terms SRI for a given `Balance`.
|
||||
@@ -139,3 +109,62 @@ async fn required_stake(serai: &TemporalSerai<'_>, balance: Balance) -> u64 {
|
||||
let required_stake = total_coin_value.saturating_mul(3).saturating_div(2);
|
||||
required_stake.saturating_add(total_coin_value.saturating_div(5))
|
||||
}
|
||||
|
||||
async fn wait_for_session(serai: &Serai, session: u32) {
|
||||
// Epoch time is half an hour with the fast epoch feature, so lets wait double that.
|
||||
tokio::time::timeout(tokio::time::Duration::from_secs(60 * 6), async {
|
||||
while serai
|
||||
.as_of_latest_finalized_block()
|
||||
.await
|
||||
.unwrap()
|
||||
.validator_sets()
|
||||
.session(NetworkId::Serai)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.0 <
|
||||
session
|
||||
{
|
||||
tokio::time::sleep(Duration::from_secs(6)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn get_distances(
|
||||
serai: &TemporalSerai<'_>,
|
||||
current_stake: &HashMap<NetworkId, u64>,
|
||||
) -> (HashMap<NetworkId, u64>, u64) {
|
||||
// we should be in the initial period, so calculate how much each network supposedly get..
|
||||
// we can check the supply to see how much coin hence liability we have.
|
||||
let mut distances: HashMap<NetworkId, u64> = HashMap::new();
|
||||
let mut total_distance = 0;
|
||||
for coin in COINS {
|
||||
if coin == Coin::Serai {
|
||||
continue;
|
||||
}
|
||||
|
||||
let amount = serai.coins().coin_supply(coin).await.unwrap();
|
||||
let required = required_stake(&serai, Balance { coin, amount }).await;
|
||||
let mut current = *current_stake.get(&coin.network()).unwrap();
|
||||
if current > required {
|
||||
current = required;
|
||||
}
|
||||
|
||||
let distance = required - current;
|
||||
total_distance += distance;
|
||||
|
||||
distances.insert(
|
||||
coin.network(),
|
||||
distances.get(&coin.network()).unwrap_or(&0).saturating_add(distance),
|
||||
);
|
||||
}
|
||||
|
||||
// add serai network portion(20%)
|
||||
let new_total_distance = total_distance.saturating_mul(10) / 8;
|
||||
distances.insert(NetworkId::Serai, new_total_distance - total_distance);
|
||||
total_distance = new_total_distance;
|
||||
|
||||
(distances, total_distance)
|
||||
}
|
||||
|
||||
@@ -273,7 +273,21 @@ pub mod pallet {
|
||||
// TODO: `participants_for_latest_decided_set` returns keys with key shares but we
|
||||
// store keys with actual stake amounts. Pr https://github.com/serai-dex/serai/pull/518
|
||||
// supposed to change that and so this pr relies and that pr.
|
||||
Participants::<T>::set(n, ValidatorSets::<T>::participants_for_latest_decided_set(n));
|
||||
let participants = ValidatorSets::<T>::participants_for_latest_decided_set(n)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(key, shares)| {
|
||||
let amount = match n {
|
||||
NetworkId::Serai => shares * 50_000 * 10_u64.pow(8),
|
||||
NetworkId::Bitcoin => shares * 1_000_000 * 10_u64.pow(8),
|
||||
NetworkId::Ethereum => shares * 1_000_000 * 10_u64.pow(8),
|
||||
NetworkId::Monero => shares * 100_000 * 10_u64.pow(8),
|
||||
};
|
||||
(key, amount)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Participants::<T>::set(n, Some(participants.try_into().unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user