Feature/hourly set updates (#1012)

* Rename function/variables mixnodes->set

* Stub utility interface

* Rewarded set contract interface

* Move epoch to common, epoch to contract

* Move epoch to the chain

* Rewarded set validator-api

* [ci skip] Generate TS types

* Epoch queries

* Moved new code to a new module

* Restored cosmwasm dependencies to their beta.3 versions for better compatibility with the rest of the codebase

* Rewarded set write reorganisation

* Stub for validator api module  responsible for rewarded set updates

* Reorganised validator api cache

* Pending contract changes

* Relevant updates to the validator client

* Updating rewarded set based on contract state

* Advancing/Setting current epoch in the contract

* Using blocktime as 'now' at startup

* Adjusted validator-api side rewarding code

* Contract cleanup + query for epoch rewarded set heights

* [ci skip] Generate TS types

* Simplified rewarder processing loop and initial sync

* [ci skip] Generate TS types

* Fixed EXISTING query-related unit tests

* Fixed existing unit tests for rewarding-related transactions

* Cargo fmt

* Removed some dead code

* Using cosmwasm 1.0.0-beta3 for compatibility [with cw-storage-plus and rest of codebase]

* Missing TryInto import

* Additional storage and query related unit tests + a bug fix

* Transaction-related unit tests + bug fixes

* Required migration code

* Update common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs

Co-authored-by: Drazen Urch <drazen@urch.eu>

* Update common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs

Co-authored-by: Drazen Urch <drazen@urch.eu>

* Constant renaming

* Changed determining previous epoch return type to Option<Epoch> if they would precede the genesis

* Exposed the new endpoint to the wallet

* Epoch-related unit tests fixes

* Recommended #[must_use] on next_epoch method

* Renamed all epoch occurences to interval

As they refer to the 'rewarding interval'

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
This commit is contained in:
Drazen Urch
2022-01-21 15:04:23 +01:00
committed by GitHub
parent e52fe65985
commit fe6c685ab1
64 changed files with 2997 additions and 2413 deletions
+2
View File
@@ -28,6 +28,7 @@ pub(crate) fn stage_full() -> AdHoc {
routes::get_mixnode_status,
routes::get_mixnode_reward_estimation,
routes::get_mixnode_stake_saturation,
routes::get_mixnode_inclusion_probability,
],
)
})
@@ -42,6 +43,7 @@ pub(crate) fn stage_minimal() -> AdHoc {
routes![
routes::get_mixnode_status,
routes::get_mixnode_stake_saturation,
routes::get_mixnode_inclusion_probability,
],
)
})
+60 -20
View File
@@ -6,14 +6,13 @@ use crate::node_status_api::models::{
MixnodeUptimeHistory,
};
use crate::storage::ValidatorApiStorage;
use crate::{Epoch, ValidatorCache};
use crate::ValidatorCache;
use rocket::http::Status;
use rocket::serde::json::Json;
use rocket::State;
use time::OffsetDateTime;
use validator_api_requests::models::{
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
StakeSaturationResponse,
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse,
};
#[get("/mixnode/<identity>/report")]
@@ -112,26 +111,25 @@ pub(crate) async fn get_mixnode_status(
pub(crate) async fn get_mixnode_reward_estimation(
cache: &State<ValidatorCache>,
storage: &State<ValidatorApiStorage>,
first_epoch: &State<Epoch>,
identity: String,
) -> Result<Json<RewardEstimationResponse>, ErrorResponse> {
let (bond, status) = cache.mixnode_details(&identity).await;
if let Some(bond) = bond {
let epoch_reward_params = cache.epoch_reward_params().await;
let as_at = epoch_reward_params.timestamp();
let epoch_reward_params = epoch_reward_params.into_inner();
let interval_reward_params = cache.interval_reward_params().await;
let as_at = interval_reward_params.timestamp();
let interval_reward_params = interval_reward_params.into_inner();
let current_epoch = first_epoch.current(OffsetDateTime::now_utc());
let current_interval = cache.current_interval().await.into_inner();
let uptime = storage
.get_average_mixnode_uptime_in_interval(
&identity,
current_epoch.start_unix_timestamp(),
current_epoch.end_unix_timestamp(),
current_interval.start_unix_timestamp(),
current_interval.end_unix_timestamp(),
)
.await
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))?;
match epoch_reward_params.estimate_reward(&bond, uptime.u8(), status.is_active()) {
match interval_reward_params.estimate_reward(&bond, uptime.u8(), status.is_active()) {
Ok((
estimated_total_node_reward,
estimated_operator_reward,
@@ -141,9 +139,9 @@ pub(crate) async fn get_mixnode_reward_estimation(
estimated_total_node_reward,
estimated_operator_reward,
estimated_delegators_reward,
current_epoch_start: current_epoch.start_unix_timestamp(),
current_epoch_end: current_epoch.end_unix_timestamp(),
current_epoch_uptime: uptime.u8(),
current_interval_start: current_interval.start_unix_timestamp(),
current_interval_end: current_interval.end_unix_timestamp(),
current_interval_uptime: uptime.u8(),
as_at,
};
Ok(Json(reponse))
@@ -168,13 +166,13 @@ pub(crate) async fn get_mixnode_stake_saturation(
) -> Result<Json<StakeSaturationResponse>, ErrorResponse> {
let (bond, _) = cache.mixnode_details(&identity).await;
if let Some(bond) = bond {
let epoch_reward_params = cache.epoch_reward_params().await;
let as_at = epoch_reward_params.timestamp();
let epoch_reward_params = epoch_reward_params.into_inner();
let interval_reward_params = cache.interval_reward_params().await;
let as_at = interval_reward_params.timestamp();
let interval_reward_params = interval_reward_params.into_inner();
let saturation = bond.stake_saturation(
epoch_reward_params.circulating_supply,
epoch_reward_params.rewarded_set_size,
interval_reward_params.circulating_supply,
interval_reward_params.rewarded_set_size,
);
Ok(Json(StakeSaturationResponse {
@@ -188,3 +186,45 @@ pub(crate) async fn get_mixnode_stake_saturation(
))
}
}
#[get("/mixnode/<identity>/inclusion-probability")]
pub(crate) async fn get_mixnode_inclusion_probability(
cache: &State<ValidatorCache>,
identity: String,
) -> Json<Option<InclusionProbabilityResponse>> {
let mixnodes = cache.mixnodes().await.into_inner();
if let Some(target_mixnode) = mixnodes.iter().find(|x| x.identity() == &identity) {
let total_bonded_tokens = mixnodes
.iter()
.fold(0u128, |acc, x| acc + x.total_bond().unwrap_or_default())
as f64;
let rewarding_params = cache.interval_reward_params().await.into_inner();
let rewarded_set_size = rewarding_params.rewarded_set_size as f64;
let active_set_size = rewarding_params.active_set_size as f64;
let prob_one_draw =
target_mixnode.total_bond().unwrap_or_default() as f64 / total_bonded_tokens;
// Chance to be selected in any draw for active set
let prob_active_set = active_set_size * prob_one_draw;
// This is likely slightly too high, as we're not correcting form them not being selected in active, should be chance to be selected, minus the chance for being not selected in reserve
let prob_reserve_set = (rewarded_set_size - active_set_size) * prob_one_draw;
// (rewarded_set_size - active_set_size) * prob_one_draw * (1. - prob_active_set);
Json(Some(InclusionProbabilityResponse {
in_active: if prob_active_set > 1. {
1.
} else {
prob_active_set
} as f32,
in_reserve: if prob_reserve_set > 1. {
1.
} else {
prob_reserve_set
} as f32,
}))
} else {
Json(None)
}
}