Feature/rewarding interval updates (#880)
* Introduced rewarding_interval_nonce to contract state * Queries for ibid. * Mixnode demanded set size * Routes for obtaining demanded/active mixnode sets * Testing only demanded nodes * Typo * Initial state * Feature-locking unused imports * Generating pseudorandom (with deterministic seed) demanded mixnodes set * cargo fmt * Fixed tauri state * Renamed network monitor address to the rewarding validator * [ci skip] Generate TS types * Notice for the future * Transactions to begin/finish mixnode rewarding + double rewarding protection * Validator API using new contract calls * Removed dead code from an old experiment * [ci skip] Generate TS types * Removed unused import * Renamed 'demanded' set to 'rewarded' set * Some renaming action * [ci skip] Generate TS types * Fixed post-merge dependency issue in tests * Post merge test fix Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
04636a569a
commit
7e1cf2f105
Vendored
+10
@@ -162,6 +162,16 @@ impl ValidatorCache {
|
||||
})
|
||||
}
|
||||
|
||||
// NOTE: this does not guarantee consistent results between multiple validator APIs, because
|
||||
// currently we do not guarantee the list of mixnodes (i.e. `mixnodes: &[MixNodeBond]`) will be the same -
|
||||
// somebody might bond/unbond a node or change delegation between different cache refreshes.
|
||||
//
|
||||
// I guess that's not a problem right now and we can resolve it later. My idea for that would be as follows:
|
||||
// since the demanded set changes only monthly, just write the identities of those nodes to the smart
|
||||
// contract upon finished rewarding (this works under assumption of rewards being distributed by a single validator)
|
||||
//
|
||||
// alternatively we could have some state locking mechanism for the duration of determining the demanded set
|
||||
// this could work with multiple validators via some multisig mechanism
|
||||
fn determine_rewarded_set(
|
||||
&self,
|
||||
mixnodes: &[MixNodeBond],
|
||||
|
||||
@@ -203,9 +203,42 @@ impl<C> Client<C> {
|
||||
.calculate_custom_fee(total_gas_limit)
|
||||
}
|
||||
|
||||
pub(crate) async fn begin_mixnode_rewarding(
|
||||
&self,
|
||||
rewarding_interval_nonce: u32,
|
||||
) -> Result<(), RewardingError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
self.0
|
||||
.write()
|
||||
.await
|
||||
.nymd
|
||||
.begin_mixnode_rewarding(rewarding_interval_nonce)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn finish_mixnode_rewarding(
|
||||
&self,
|
||||
rewarding_interval_nonce: u32,
|
||||
) -> Result<(), RewardingError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
self.0
|
||||
.write()
|
||||
.await
|
||||
.nymd
|
||||
.finish_mixnode_rewarding(rewarding_interval_nonce)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn reward_mixnodes(
|
||||
&self,
|
||||
nodes: &[MixnodeToReward],
|
||||
rewarding_interval_nonce: u32,
|
||||
) -> Result<(), RewardingError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
@@ -216,7 +249,7 @@ impl<C> Client<C> {
|
||||
.await;
|
||||
let msgs: Vec<(ExecuteMsg, _)> = nodes
|
||||
.iter()
|
||||
.map(Into::into)
|
||||
.map(|node| node.to_execute_msg(rewarding_interval_nonce))
|
||||
.zip(std::iter::repeat(Vec::new()))
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ pub(crate) struct MixnodeToReward {
|
||||
|
||||
impl MixnodeToReward {
|
||||
/// Somewhat clumsy way of feature gatting tokenomics payments. In a tokenomics scenario this will never be None at reward time. We levarage that to Into a different ExecuteMsg variant
|
||||
// TODO: to re-integrate in another PR that combines rewarded/active sets with tokenomics
|
||||
#[allow(dead_code)]
|
||||
fn params(&self) -> Option<NodeRewardParams> {
|
||||
if cfg!(feature = "tokenomics") {
|
||||
self.params
|
||||
@@ -64,6 +66,26 @@ impl MixnodeToReward {
|
||||
}
|
||||
}
|
||||
|
||||
impl MixnodeToReward {
|
||||
pub(crate) fn to_execute_msg(&self, rewarding_interval_nonce: u32) -> ExecuteMsg {
|
||||
ExecuteMsg::RewardMixnode {
|
||||
identity: self.identity.clone(),
|
||||
uptime: self.uptime.u8() as u32,
|
||||
rewarding_interval_nonce,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: to re-integrate in another PR that combines rewarded/active sets with tokenomics
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn to_execute_msg_v2(&self, rewarding_interval_nonce: u32) -> ExecuteMsg {
|
||||
ExecuteMsg::RewardMixnodeV2 {
|
||||
identity: self.identity.clone(),
|
||||
params: self.params().unwrap(),
|
||||
rewarding_interval_nonce,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct FailedMixnodeRewardChunkDetails {
|
||||
possibly_unrewarded: Vec<MixnodeToReward>,
|
||||
error_message: String,
|
||||
@@ -74,22 +96,6 @@ pub(crate) struct FailureData {
|
||||
mixnodes: Option<Vec<FailedMixnodeRewardChunkDetails>>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a MixnodeToReward> for ExecuteMsg {
|
||||
fn from(node: &MixnodeToReward) -> Self {
|
||||
if let Some(params) = node.params() {
|
||||
ExecuteMsg::RewardMixnodeV2 {
|
||||
identity: node.identity.clone(),
|
||||
params,
|
||||
}
|
||||
} else {
|
||||
ExecuteMsg::RewardMixnode {
|
||||
identity: node.identity.clone(),
|
||||
uptime: node.uptime.u8() as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Rewarder {
|
||||
nymd_client: Client<SigningNymdClient>,
|
||||
validator_cache: ValidatorCache,
|
||||
@@ -285,14 +291,20 @@ impl Rewarder {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `eligible_mixnodes`: list of the nodes that are eligible to receive non-zero rewards.
|
||||
/// * `rewarding_interval_nonce`: nonce associated with the current rewarding interval
|
||||
async fn distribute_rewards_to_mixnodes(
|
||||
&self,
|
||||
eligible_mixnodes: &[MixnodeToReward],
|
||||
rewarding_interval_nonce: u32,
|
||||
) -> Option<Vec<FailedMixnodeRewardChunkDetails>> {
|
||||
let mut failed_chunks = Vec::new();
|
||||
|
||||
for (i, mix_chunk) in eligible_mixnodes.chunks(MAX_TO_REWARD_AT_ONCE).enumerate() {
|
||||
if let Err(err) = self.nymd_client.reward_mixnodes(mix_chunk).await {
|
||||
if let Err(err) = self
|
||||
.nymd_client
|
||||
.reward_mixnodes(mix_chunk, rewarding_interval_nonce)
|
||||
.await
|
||||
{
|
||||
// this is a super weird edge case that we didn't catch change to sequence and
|
||||
// resent rewards unnecessarily, but the mempool saved us from executing it again
|
||||
// however, still we want to wait until we're sure we're into the next block
|
||||
@@ -327,13 +339,13 @@ impl Rewarder {
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `epoch_rewarding_id`: id of the current epoch rewarding.
|
||||
/// * `epoch_rewarding_id`: id of the current epoch rewarding as stored in the databse.
|
||||
///
|
||||
/// * `active_monitor_mixnodes`: list of the nodes that were tested at least once by the network monitor
|
||||
/// in the last epoch.
|
||||
async fn distribute_rewards(
|
||||
&self,
|
||||
epoch_rewarding_id: i64,
|
||||
epoch_rewarding_database_id: i64,
|
||||
active_monitor_mixnodes: &[MixnodeStatusReport],
|
||||
) -> Result<(RewardingReport, Option<FailureData>), RewardingError> {
|
||||
let mut failure_data = FailureData::default();
|
||||
@@ -345,12 +357,20 @@ impl Rewarder {
|
||||
return Err(RewardingError::NoMixnodesToReward);
|
||||
}
|
||||
|
||||
let current_rewarding_nonce = self
|
||||
.nymd_client
|
||||
.get_current_rewarding_interval()
|
||||
.await?
|
||||
.current_rewarding_interval_nonce;
|
||||
self.nymd_client
|
||||
.begin_mixnode_rewarding(current_rewarding_nonce + 1)
|
||||
.await?;
|
||||
failure_data.mixnodes = self
|
||||
.distribute_rewards_to_mixnodes(&eligible_mixnodes)
|
||||
.distribute_rewards_to_mixnodes(&eligible_mixnodes, current_rewarding_nonce + 1)
|
||||
.await;
|
||||
|
||||
let report = RewardingReport {
|
||||
epoch_rewarding_id,
|
||||
epoch_rewarding_id: epoch_rewarding_database_id,
|
||||
eligible_mixnodes: eligible_mixnodes.len() as i64,
|
||||
possibly_unrewarded_mixnodes: failure_data
|
||||
.mixnodes
|
||||
@@ -364,6 +384,10 @@ impl Rewarder {
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
|
||||
self.nymd_client
|
||||
.finish_mixnode_rewarding(current_rewarding_nonce + 1)
|
||||
.await?;
|
||||
|
||||
if failure_data.mixnodes.is_none() {
|
||||
Ok((report, None))
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user