Removed gateway rewarding and delegation (#856)
* Removed gateway rewarding and delegation * Removed redundant error variants * [ci skip] Generate TS types * Test fixes Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
ef88ce9252
commit
b19528d47e
@@ -257,7 +257,7 @@ impl TopologyRefresher {
|
||||
Ok(mixes) => mixes,
|
||||
};
|
||||
|
||||
let gateways = match self.validator_client.get_cached_active_gateways().await {
|
||||
let gateways = match self.validator_client.get_cached_gateways().await {
|
||||
Err(err) => {
|
||||
error!("failed to get network gateways - {}", err);
|
||||
return None;
|
||||
|
||||
@@ -279,7 +279,7 @@ impl NymClient {
|
||||
Ok(mixes) => mixes,
|
||||
};
|
||||
|
||||
let gateways = match validator_client.get_cached_active_gateways().await {
|
||||
let gateways = match validator_client.get_cached_gateways().await {
|
||||
Err(err) => panic!("{}", err),
|
||||
Ok(gateways) => gateways,
|
||||
};
|
||||
|
||||
@@ -24,7 +24,6 @@ pub struct Config {
|
||||
mixnode_page_limit: Option<u32>,
|
||||
gateway_page_limit: Option<u32>,
|
||||
mixnode_delegations_page_limit: Option<u32>,
|
||||
gateway_delegations_page_limit: Option<u32>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
@@ -41,7 +40,6 @@ impl Config {
|
||||
mixnode_page_limit: None,
|
||||
gateway_page_limit: None,
|
||||
mixnode_delegations_page_limit: None,
|
||||
gateway_delegations_page_limit: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,11 +57,6 @@ impl Config {
|
||||
self.mixnode_delegations_page_limit = limit;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_gateway_delegations_page_limit(mut self, limit: Option<u32>) -> Config {
|
||||
self.gateway_delegations_page_limit = limit;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
@@ -74,7 +67,6 @@ pub struct Client<C> {
|
||||
mixnode_page_limit: Option<u32>,
|
||||
gateway_page_limit: Option<u32>,
|
||||
mixnode_delegations_page_limit: Option<u32>,
|
||||
gateway_delegations_page_limit: Option<u32>,
|
||||
|
||||
// ideally they would have been read-only, but unfortunately rust doesn't have such features
|
||||
pub validator_api: validator_api::Client,
|
||||
@@ -100,7 +92,6 @@ impl Client<SigningNymdClient> {
|
||||
mixnode_page_limit: config.mixnode_page_limit,
|
||||
gateway_page_limit: config.gateway_page_limit,
|
||||
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
|
||||
gateway_delegations_page_limit: config.gateway_delegations_page_limit,
|
||||
validator_api: validator_api_client,
|
||||
nymd: nymd_client,
|
||||
})
|
||||
@@ -136,7 +127,6 @@ impl Client<QueryNymdClient> {
|
||||
mixnode_page_limit: config.mixnode_page_limit,
|
||||
gateway_page_limit: config.gateway_page_limit,
|
||||
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
|
||||
gateway_delegations_page_limit: config.gateway_delegations_page_limit,
|
||||
validator_api: validator_api_client,
|
||||
nymd: nymd_client,
|
||||
})
|
||||
@@ -339,116 +329,6 @@ impl<C> Client<C> {
|
||||
Ok(delegations)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_single_gateway_delegations(
|
||||
&self,
|
||||
identity: mixnet_contract::IdentityKey,
|
||||
) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let mut delegations = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_gateway_delegations(
|
||||
identity.clone(),
|
||||
start_after.take(),
|
||||
self.gateway_delegations_page_limit,
|
||||
)
|
||||
.await?;
|
||||
delegations.append(&mut paged_response.delegations);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(delegations)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_gateway_delegations(
|
||||
&self,
|
||||
) -> Result<Vec<mixnet_contract::UnpackedDelegation<RawDelegationData>>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let mut delegations = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_all_gateway_delegations(
|
||||
start_after.take(),
|
||||
self.gateway_delegations_page_limit,
|
||||
)
|
||||
.await?;
|
||||
delegations.append(&mut paged_response.delegations);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(delegations)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_reverse_gateway_delegations(
|
||||
&self,
|
||||
delegation_owner: &cosmrs::AccountId,
|
||||
) -> Result<Vec<mixnet_contract::IdentityKey>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let mut delegations = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_reverse_gateway_delegations_paged(
|
||||
mixnet_contract::Addr::unchecked(delegation_owner.as_ref()),
|
||||
start_after.take(),
|
||||
self.mixnode_delegations_page_limit,
|
||||
)
|
||||
.await?;
|
||||
delegations.append(&mut paged_response.delegated_nodes);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(delegations)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_gateway_delegations_of_owner(
|
||||
&self,
|
||||
delegation_owner: &cosmrs::AccountId,
|
||||
) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let mut delegations = Vec::new();
|
||||
for node_identity in self
|
||||
.get_all_nymd_reverse_gateway_delegations(delegation_owner)
|
||||
.await?
|
||||
{
|
||||
let delegation = self
|
||||
.nymd
|
||||
.get_gateway_delegation(node_identity, delegation_owner)
|
||||
.await?;
|
||||
delegations.push(delegation);
|
||||
}
|
||||
|
||||
Ok(delegations)
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
&self,
|
||||
request_body: &BlindSignRequestBody,
|
||||
@@ -488,12 +368,6 @@ impl ApiClient {
|
||||
Ok(self.validator_api.get_active_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_active_gateways(
|
||||
&self,
|
||||
) -> Result<Vec<GatewayBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_active_gateways().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnodes().await?)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ use cosmwasm_std::Coin;
|
||||
use mixnet_contract::{
|
||||
Addr, Delegation, ExecuteMsg, Gateway, GatewayOwnershipResponse, IdentityKey,
|
||||
LayerDistribution, MixNode, MixOwnershipResponse, PagedAllDelegationsResponse,
|
||||
PagedGatewayDelegationsResponse, PagedGatewayResponse, PagedMixDelegationsResponse,
|
||||
PagedMixnodeResponse, PagedReverseGatewayDelegationsResponse,
|
||||
PagedGatewayResponse, PagedMixDelegationsResponse, PagedMixnodeResponse,
|
||||
PagedReverseMixDelegationsResponse, QueryMsg, RawDelegationData, StateParams,
|
||||
};
|
||||
use serde::Serialize;
|
||||
@@ -354,82 +353,6 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets list of all delegations towards particular gateway on particular page.
|
||||
pub async fn get_gateway_delegations(
|
||||
&self,
|
||||
gateway_identity: IdentityKey,
|
||||
start_after: Option<Addr>,
|
||||
page_limit: Option<u32>,
|
||||
) -> Result<PagedGatewayDelegationsResponse, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetGatewayDelegations {
|
||||
gateway_identity,
|
||||
start_after,
|
||||
limit: page_limit,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets list of all gateway delegations on particular page.
|
||||
pub async fn get_all_gateway_delegations(
|
||||
&self,
|
||||
start_after: Option<Vec<u8>>,
|
||||
page_limit: Option<u32>,
|
||||
) -> Result<PagedAllDelegationsResponse<RawDelegationData>, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetAllGatewayDelegations {
|
||||
start_after,
|
||||
limit: page_limit,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets list of all the gateways on which a particular address delegated.
|
||||
pub async fn get_reverse_gateway_delegations_paged(
|
||||
&self,
|
||||
delegation_owner: Addr,
|
||||
start_after: Option<IdentityKey>,
|
||||
page_limit: Option<u32>,
|
||||
) -> Result<PagedReverseGatewayDelegationsResponse, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetReverseGatewayDelegations {
|
||||
delegation_owner,
|
||||
start_after,
|
||||
limit: page_limit,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Checks value of delegation of given client towards particular gateway.
|
||||
pub async fn get_gateway_delegation(
|
||||
&self,
|
||||
gateway_identity: IdentityKey,
|
||||
delegator: &AccountId,
|
||||
) -> Result<Delegation, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetGatewayDelegation {
|
||||
gateway_identity,
|
||||
address: Addr::unchecked(delegator.as_ref()),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send funds from one address to another
|
||||
pub async fn send(
|
||||
&self,
|
||||
@@ -698,57 +621,6 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Delegates specified amount of stake to particular gateway.
|
||||
pub async fn delegate_to_gateway(
|
||||
&self,
|
||||
gateway_identity: &str,
|
||||
amount: &Coin,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
let fee = self.get_fee(Operation::DelegateToGateway);
|
||||
|
||||
let req = ExecuteMsg::DelegateToGateway {
|
||||
gateway_identity: gateway_identity.to_string(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"Delegating to gateway from rust!",
|
||||
vec![cosmwasm_coin_ptr_to_cosmos_coin(amount)],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Removes stake delegation from a particular gateway.
|
||||
pub async fn remove_gateway_delegation(
|
||||
&self,
|
||||
gateway_identity: &str,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
let fee = self.get_fee(Operation::UndelegateFromGateway);
|
||||
|
||||
let req = ExecuteMsg::UndelegateFromGateway {
|
||||
gateway_identity: gateway_identity.to_string(),
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"Removing gateway delegation from rust!",
|
||||
Vec::new(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_state_params(
|
||||
&self,
|
||||
new_params: StateParams,
|
||||
|
||||
@@ -73,11 +73,6 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_active_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS, routes::ACTIVE])
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
&self,
|
||||
request_body: &BlindSignRequestBody,
|
||||
|
||||
@@ -120,48 +120,6 @@ impl PagedReverseMixDelegationsResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedGatewayDelegationsResponse {
|
||||
pub node_identity: IdentityKey,
|
||||
pub delegations: Vec<Delegation>,
|
||||
pub start_next_after: Option<Addr>,
|
||||
}
|
||||
|
||||
impl PagedGatewayDelegationsResponse {
|
||||
pub fn new(
|
||||
node_identity: IdentityKey,
|
||||
delegations: Vec<Delegation>,
|
||||
start_next_after: Option<Addr>,
|
||||
) -> Self {
|
||||
PagedGatewayDelegationsResponse {
|
||||
node_identity,
|
||||
delegations,
|
||||
start_next_after,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedReverseGatewayDelegationsResponse {
|
||||
pub delegation_owner: Addr,
|
||||
pub delegated_nodes: Vec<IdentityKey>,
|
||||
pub start_next_after: Option<IdentityKey>,
|
||||
}
|
||||
|
||||
impl PagedReverseGatewayDelegationsResponse {
|
||||
pub fn new(
|
||||
delegation_owner: Addr,
|
||||
delegated_nodes: Vec<IdentityKey>,
|
||||
start_next_after: Option<IdentityKey>,
|
||||
) -> Self {
|
||||
PagedReverseGatewayDelegationsResponse {
|
||||
delegation_owner,
|
||||
delegated_nodes,
|
||||
start_next_after,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedAllDelegationsResponse<T> {
|
||||
pub delegations: Vec<UnpackedDelegation<T>>,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#![allow(clippy::field_reassign_with_default)]
|
||||
|
||||
use crate::{IdentityKey, SphinxKey};
|
||||
use cosmwasm_std::{coin, Addr, Coin};
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
@@ -24,7 +24,6 @@ pub struct Gateway {
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct GatewayBond {
|
||||
pub bond_amount: Coin,
|
||||
pub total_delegation: Coin,
|
||||
pub owner: Addr,
|
||||
pub block_height: u64,
|
||||
pub gateway: Gateway,
|
||||
@@ -33,7 +32,6 @@ pub struct GatewayBond {
|
||||
impl GatewayBond {
|
||||
pub fn new(bond_amount: Coin, owner: Addr, block_height: u64, gateway: Gateway) -> Self {
|
||||
GatewayBond {
|
||||
total_delegation: coin(0, &bond_amount.denom),
|
||||
bond_amount,
|
||||
owner,
|
||||
block_height,
|
||||
@@ -61,27 +59,11 @@ impl GatewayBond {
|
||||
impl PartialOrd for GatewayBond {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
// first remove invalid cases
|
||||
if self.bond_amount.denom != self.total_delegation.denom {
|
||||
return None;
|
||||
}
|
||||
|
||||
if other.bond_amount.denom != other.total_delegation.denom {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.bond_amount.denom != other.bond_amount.denom {
|
||||
return None;
|
||||
}
|
||||
|
||||
// try to order by total bond + delegation
|
||||
let total_cmp = (self.bond_amount.amount + self.total_delegation.amount)
|
||||
.partial_cmp(&(self.bond_amount.amount + self.total_delegation.amount))?;
|
||||
|
||||
if total_cmp != Ordering::Equal {
|
||||
return Some(total_cmp);
|
||||
}
|
||||
|
||||
// then if those are equal, prefer higher bond over delegation
|
||||
// try to order by total bond
|
||||
let bond_cmp = self
|
||||
.bond_amount
|
||||
.amount
|
||||
@@ -90,15 +72,6 @@ impl PartialOrd for GatewayBond {
|
||||
return Some(bond_cmp);
|
||||
}
|
||||
|
||||
// then look at delegation (I'm not sure we can get here, but better safe than sorry)
|
||||
let delegation_cmp = self
|
||||
.total_delegation
|
||||
.amount
|
||||
.partial_cmp(&other.total_delegation.amount)?;
|
||||
if delegation_cmp != Ordering::Equal {
|
||||
return Some(delegation_cmp);
|
||||
}
|
||||
|
||||
// then check block height
|
||||
let height_cmp = self.block_height.partial_cmp(&other.block_height)?;
|
||||
if height_cmp != Ordering::Equal {
|
||||
@@ -172,20 +145,19 @@ mod tests {
|
||||
#[test]
|
||||
fn gateway_bond_partial_ord() {
|
||||
let _150foos = Coin::new(150, "foo");
|
||||
let _140foos = Coin::new(140, "foo");
|
||||
let _50foos = Coin::new(50, "foo");
|
||||
let _0foos = Coin::new(0, "foo");
|
||||
|
||||
let gate1 = GatewayBond {
|
||||
bond_amount: _150foos.clone(),
|
||||
total_delegation: _50foos.clone(),
|
||||
owner: Addr::unchecked("foo1"),
|
||||
block_height: 100,
|
||||
gateway: gateway_fixture(),
|
||||
};
|
||||
|
||||
let gate2 = GatewayBond {
|
||||
bond_amount: _150foos.clone(),
|
||||
total_delegation: _50foos.clone(),
|
||||
bond_amount: _150foos,
|
||||
owner: Addr::unchecked("foo2"),
|
||||
block_height: 120,
|
||||
gateway: gateway_fixture(),
|
||||
@@ -193,15 +165,13 @@ mod tests {
|
||||
|
||||
let gate3 = GatewayBond {
|
||||
bond_amount: _50foos,
|
||||
total_delegation: _150foos.clone(),
|
||||
owner: Addr::unchecked("foo3"),
|
||||
block_height: 120,
|
||||
gateway: gateway_fixture(),
|
||||
};
|
||||
|
||||
let gate4 = GatewayBond {
|
||||
bond_amount: _150foos.clone(),
|
||||
total_delegation: _0foos.clone(),
|
||||
bond_amount: _140foos,
|
||||
owner: Addr::unchecked("foo4"),
|
||||
block_height: 120,
|
||||
gateway: gateway_fixture(),
|
||||
@@ -209,21 +179,19 @@ mod tests {
|
||||
|
||||
let gate5 = GatewayBond {
|
||||
bond_amount: _0foos,
|
||||
total_delegation: _150foos,
|
||||
owner: Addr::unchecked("foo5"),
|
||||
block_height: 120,
|
||||
gateway: gateway_fixture(),
|
||||
};
|
||||
|
||||
// summary:
|
||||
// gate1: 150bond + 50delegation, foo1, 100
|
||||
// gate2: 150bond + 50delegation, foo2, 120
|
||||
// gate3: 50bond + 150delegation, foo3, 120
|
||||
// gate4: 150bond + 0delegation, foo4, 120
|
||||
// gate5: 0bond + 150delegation, foo5, 120
|
||||
// gate1: 150bond, foo1, 100
|
||||
// gate2: 150bond, foo2, 120
|
||||
// gate3: 50bond, foo3, 120
|
||||
// gate4: 140bond, foo4, 120
|
||||
// gate5: 0bond, foo5, 120
|
||||
|
||||
// highest total bond+delegation is used
|
||||
// then bond followed by delegation
|
||||
// highest total bond is used
|
||||
// finally just the rest of the fields
|
||||
|
||||
// gate1 has higher total than gate4 or gate5
|
||||
|
||||
@@ -9,8 +9,7 @@ mod types;
|
||||
|
||||
pub use cosmwasm_std::{Addr, Coin};
|
||||
pub use delegation::{
|
||||
Delegation, PagedAllDelegationsResponse, PagedGatewayDelegationsResponse,
|
||||
PagedMixDelegationsResponse, PagedReverseGatewayDelegationsResponse,
|
||||
Delegation, PagedAllDelegationsResponse, PagedMixDelegationsResponse,
|
||||
PagedReverseMixDelegationsResponse, RawDelegationData, UnpackedDelegation,
|
||||
};
|
||||
pub use gateway::{Gateway, GatewayBond, GatewayOwnershipResponse, PagedGatewayResponse};
|
||||
|
||||
@@ -31,25 +31,11 @@ pub enum ExecuteMsg {
|
||||
mix_identity: IdentityKey,
|
||||
},
|
||||
|
||||
DelegateToGateway {
|
||||
gateway_identity: IdentityKey,
|
||||
},
|
||||
|
||||
UndelegateFromGateway {
|
||||
gateway_identity: IdentityKey,
|
||||
},
|
||||
|
||||
RewardMixnode {
|
||||
identity: IdentityKey,
|
||||
// percentage value in range 0-100
|
||||
uptime: u32,
|
||||
},
|
||||
|
||||
RewardGateway {
|
||||
identity: IdentityKey,
|
||||
// percentage value in range 0-100
|
||||
uptime: u32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
@@ -88,24 +74,6 @@ pub enum QueryMsg {
|
||||
mix_identity: IdentityKey,
|
||||
address: Addr,
|
||||
},
|
||||
GetGatewayDelegations {
|
||||
gateway_identity: IdentityKey,
|
||||
start_after: Option<Addr>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
GetAllGatewayDelegations {
|
||||
start_after: Option<Vec<u8>>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
GetReverseGatewayDelegations {
|
||||
delegation_owner: Addr,
|
||||
start_after: Option<IdentityKey>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
GetGatewayDelegation {
|
||||
gateway_identity: IdentityKey,
|
||||
address: Addr,
|
||||
},
|
||||
LayerDistribution {},
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,10 @@ pub struct StateParams {
|
||||
|
||||
pub minimum_mixnode_bond: Uint128, // minimum amount a mixnode must bond to get into the system
|
||||
pub minimum_gateway_bond: Uint128, // minimum amount a gateway must bond to get into the system
|
||||
|
||||
pub mixnode_bond_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25
|
||||
pub gateway_bond_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25
|
||||
pub mixnode_delegation_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25
|
||||
pub gateway_delegation_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25
|
||||
pub mixnode_active_set_size: u32,
|
||||
pub gateway_active_set_size: u32,
|
||||
}
|
||||
|
||||
impl Display for StateParams {
|
||||
@@ -51,30 +49,15 @@ impl Display for StateParams {
|
||||
"mixnode bond reward rate: {}; ",
|
||||
self.mixnode_bond_reward_rate
|
||||
)?;
|
||||
write!(
|
||||
f,
|
||||
"gateway bond reward rate: {}; ",
|
||||
self.gateway_bond_reward_rate
|
||||
)?;
|
||||
write!(
|
||||
f,
|
||||
"mixnode delegation reward rate: {}; ",
|
||||
self.mixnode_delegation_reward_rate
|
||||
)?;
|
||||
write!(
|
||||
f,
|
||||
"gateway delegation reward rate: {}; ",
|
||||
self.gateway_delegation_reward_rate
|
||||
)?;
|
||||
write!(
|
||||
f,
|
||||
"mixnode active set size: {}",
|
||||
self.mixnode_active_set_size
|
||||
)?;
|
||||
write!(
|
||||
f,
|
||||
"gateway active set size: {} ]",
|
||||
self.gateway_active_set_size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +268,6 @@ mod message_receiver {
|
||||
vec![gateway::Node {
|
||||
owner: "foomp4".to_string(),
|
||||
stake: 123,
|
||||
delegation: 456,
|
||||
location: "unknown".to_string(),
|
||||
host: "1.2.3.4".parse().unwrap(),
|
||||
mix_host: "1.2.3.4:1789".parse().unwrap(),
|
||||
|
||||
@@ -72,7 +72,6 @@ pub struct Node {
|
||||
// somebody correct me if I'm wrong, but we should only ever have a single denom of currency
|
||||
// on the network at a type, right?
|
||||
pub stake: u128,
|
||||
pub delegation: u128,
|
||||
pub location: String,
|
||||
pub host: NetworkAddress,
|
||||
// we're keeping this as separate resolved field since we do not want to be resolving the potential
|
||||
@@ -127,7 +126,6 @@ impl<'a> TryFrom<&'a GatewayBond> for Node {
|
||||
Ok(Node {
|
||||
owner: bond.owner.as_str().to_owned(),
|
||||
stake: bond.bond_amount.amount.into(),
|
||||
delegation: bond.total_delegation.amount.into(),
|
||||
location: bond.gateway.location.clone(),
|
||||
host,
|
||||
mix_host,
|
||||
|
||||
@@ -22,18 +22,13 @@ pub const INITIAL_MIXNODE_BOND: Uint128 = Uint128(100_000000);
|
||||
|
||||
// percentage annual increase. Given starting value of x, we expect to have 1.1x at the end of the year
|
||||
pub const INITIAL_MIXNODE_BOND_REWARD_RATE: u64 = 110;
|
||||
pub const INITIAL_GATEWAY_BOND_REWARD_RATE: u64 = 110;
|
||||
pub const INITIAL_MIXNODE_DELEGATION_REWARD_RATE: u64 = 110;
|
||||
pub const INITIAL_GATEWAY_DELEGATION_REWARD_RATE: u64 = 110;
|
||||
|
||||
pub const INITIAL_MIXNODE_ACTIVE_SET_SIZE: u32 = 100;
|
||||
pub const INITIAL_GATEWAY_ACTIVE_SET_SIZE: u32 = 20;
|
||||
|
||||
fn default_initial_state(owner: Addr) -> State {
|
||||
let mixnode_bond_reward_rate = Decimal::percent(INITIAL_MIXNODE_BOND_REWARD_RATE);
|
||||
let gateway_bond_reward_rate = Decimal::percent(INITIAL_GATEWAY_BOND_REWARD_RATE);
|
||||
let mixnode_delegation_reward_rate = Decimal::percent(INITIAL_MIXNODE_DELEGATION_REWARD_RATE);
|
||||
let gateway_delegation_reward_rate = Decimal::percent(INITIAL_GATEWAY_DELEGATION_REWARD_RATE);
|
||||
|
||||
State {
|
||||
owner,
|
||||
@@ -43,28 +38,17 @@ fn default_initial_state(owner: Addr) -> State {
|
||||
minimum_mixnode_bond: INITIAL_MIXNODE_BOND,
|
||||
minimum_gateway_bond: INITIAL_GATEWAY_BOND,
|
||||
mixnode_bond_reward_rate,
|
||||
gateway_bond_reward_rate,
|
||||
mixnode_delegation_reward_rate,
|
||||
gateway_delegation_reward_rate,
|
||||
mixnode_active_set_size: INITIAL_MIXNODE_ACTIVE_SET_SIZE,
|
||||
gateway_active_set_size: INITIAL_GATEWAY_ACTIVE_SET_SIZE,
|
||||
},
|
||||
mixnode_epoch_bond_reward: calculate_epoch_reward_rate(
|
||||
INITIAL_DEFAULT_EPOCH_LENGTH,
|
||||
mixnode_bond_reward_rate,
|
||||
),
|
||||
gateway_epoch_bond_reward: calculate_epoch_reward_rate(
|
||||
INITIAL_DEFAULT_EPOCH_LENGTH,
|
||||
gateway_bond_reward_rate,
|
||||
),
|
||||
mixnode_epoch_delegation_reward: calculate_epoch_reward_rate(
|
||||
INITIAL_DEFAULT_EPOCH_LENGTH,
|
||||
mixnode_delegation_reward_rate,
|
||||
),
|
||||
gateway_epoch_delegation_reward: calculate_epoch_reward_rate(
|
||||
INITIAL_DEFAULT_EPOCH_LENGTH,
|
||||
gateway_delegation_reward_rate,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,21 +94,12 @@ pub fn execute(
|
||||
ExecuteMsg::RewardMixnode { identity, uptime } => {
|
||||
transactions::try_reward_mixnode(deps, env, info, identity, uptime)
|
||||
}
|
||||
ExecuteMsg::RewardGateway { identity, uptime } => {
|
||||
transactions::try_reward_gateway(deps, env, info, identity, uptime)
|
||||
}
|
||||
ExecuteMsg::DelegateToMixnode { mix_identity } => {
|
||||
transactions::try_delegate_to_mixnode(deps, env, info, mix_identity)
|
||||
}
|
||||
ExecuteMsg::UndelegateFromMixnode { mix_identity } => {
|
||||
transactions::try_remove_delegation_from_mixnode(deps, info, mix_identity)
|
||||
}
|
||||
ExecuteMsg::DelegateToGateway { gateway_identity } => {
|
||||
transactions::try_delegate_to_gateway(deps, env, info, gateway_identity)
|
||||
}
|
||||
ExecuteMsg::UndelegateFromGateway { gateway_identity } => {
|
||||
transactions::try_remove_delegation_from_gateway(deps, info, gateway_identity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,37 +151,6 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
|
||||
mix_identity,
|
||||
address,
|
||||
)?),
|
||||
QueryMsg::GetGatewayDelegations {
|
||||
gateway_identity,
|
||||
start_after,
|
||||
limit,
|
||||
} => to_binary(&queries::query_gateway_delegations_paged(
|
||||
deps,
|
||||
gateway_identity,
|
||||
start_after,
|
||||
limit,
|
||||
)?),
|
||||
QueryMsg::GetAllGatewayDelegations { start_after, limit } => to_binary(
|
||||
&queries::query_all_gateway_delegations_paged(deps, start_after, limit)?,
|
||||
),
|
||||
QueryMsg::GetReverseGatewayDelegations {
|
||||
delegation_owner,
|
||||
start_after,
|
||||
limit,
|
||||
} => to_binary(&queries::query_reverse_gateway_delegations_paged(
|
||||
deps,
|
||||
delegation_owner,
|
||||
start_after,
|
||||
limit,
|
||||
)?),
|
||||
QueryMsg::GetGatewayDelegation {
|
||||
gateway_identity,
|
||||
address,
|
||||
} => to_binary(&queries::query_gateway_delegation(
|
||||
deps,
|
||||
gateway_identity,
|
||||
address,
|
||||
)?),
|
||||
};
|
||||
|
||||
Ok(query_res?)
|
||||
|
||||
@@ -48,15 +48,9 @@ pub enum ContractError {
|
||||
#[error("The bond reward rate for mixnode was set to be lower than 1")]
|
||||
DecreasingMixnodeBondReward,
|
||||
|
||||
#[error("The bond reward rate for gateway was set to be lower than 1")]
|
||||
DecreasingGatewayBondReward,
|
||||
|
||||
#[error("The delegation reward rate for mixnode was set to be lower than 1")]
|
||||
DecreasingMixnodeDelegationReward,
|
||||
|
||||
#[error("The delegation reward rate for gateway was set to be lower than 1")]
|
||||
DecreasingGatewayDelegationReward,
|
||||
|
||||
#[error("The node had uptime larger than 100%")]
|
||||
UnexpectedUptime,
|
||||
|
||||
@@ -83,10 +77,4 @@ pub enum ContractError {
|
||||
identity: IdentityKey,
|
||||
address: Addr,
|
||||
},
|
||||
|
||||
#[error("Could not find any delegation information associated with gateway {identity} for {address}")]
|
||||
NoGatewayDelegationFound {
|
||||
identity: IdentityKey,
|
||||
address: Addr,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -209,10 +209,7 @@ impl<'a, T: Clone + Serialize + DeserializeOwned> Iterator for Delegations<'a, T
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::queries::tests::store_n_mix_delegations;
|
||||
use crate::storage::{
|
||||
all_gateway_delegations_read, all_mix_delegations_read, gateway_delegations,
|
||||
mix_delegations,
|
||||
};
|
||||
use crate::storage::{all_mix_delegations_read, mix_delegations};
|
||||
use crate::support::tests::helpers;
|
||||
use cosmwasm_std::testing::mock_dependencies;
|
||||
use mixnet_contract::RawDelegationData;
|
||||
@@ -389,66 +386,4 @@ mod tests {
|
||||
UnpackedDelegation::new(delegation_owner2, node_identity2, raw_delegation.clone()),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_gateway_delegations() {
|
||||
let mut deps = mock_dependencies(&[]);
|
||||
let node_identity1: IdentityKey = "foo1".into();
|
||||
let delegation_owner1 = Addr::unchecked("bar1");
|
||||
let node_identity2: IdentityKey = "foo2".into();
|
||||
let delegation_owner2 = Addr::unchecked("bar2");
|
||||
let raw_delegation = RawDelegationData::new(1000u128.into(), 42);
|
||||
let mut start_after = None;
|
||||
|
||||
gateway_delegations(&mut deps.storage, &node_identity1)
|
||||
.save(delegation_owner1.as_bytes(), &raw_delegation)
|
||||
.unwrap();
|
||||
|
||||
let bucket = all_gateway_delegations_read::<RawDelegationData>(&deps.storage);
|
||||
let response =
|
||||
get_all_delegations_paged::<RawDelegationData>(&bucket, &start_after, 10).unwrap();
|
||||
start_after = response.start_next_after;
|
||||
let delegations = response.delegations;
|
||||
assert_eq!(delegations.len(), 1);
|
||||
assert_eq!(
|
||||
delegations[0],
|
||||
UnpackedDelegation::new(
|
||||
delegation_owner1.clone(),
|
||||
node_identity1.clone(),
|
||||
raw_delegation.clone()
|
||||
)
|
||||
);
|
||||
|
||||
gateway_delegations(&mut deps.storage, &node_identity2)
|
||||
.save(delegation_owner2.as_bytes(), &raw_delegation)
|
||||
.unwrap();
|
||||
|
||||
let bucket = all_gateway_delegations_read::<RawDelegationData>(&deps.storage);
|
||||
let response =
|
||||
get_all_delegations_paged::<RawDelegationData>(&bucket, &start_after, 10).unwrap();
|
||||
start_after = response.start_next_after;
|
||||
let delegations = response.delegations;
|
||||
assert_eq!(delegations.len(), 2);
|
||||
assert_eq!(
|
||||
delegations[1],
|
||||
UnpackedDelegation::new(
|
||||
delegation_owner2.clone(),
|
||||
node_identity2.clone(),
|
||||
raw_delegation.clone()
|
||||
)
|
||||
);
|
||||
|
||||
gateway_delegations(&mut deps.storage, &node_identity1)
|
||||
.remove(delegation_owner1.as_bytes());
|
||||
|
||||
let bucket = all_gateway_delegations_read::<RawDelegationData>(&deps.storage);
|
||||
let response =
|
||||
get_all_delegations_paged::<RawDelegationData>(&bucket, &start_after, 10).unwrap();
|
||||
let delegations = response.delegations;
|
||||
assert_eq!(delegations.len(), 1);
|
||||
assert_eq!(
|
||||
delegations[0],
|
||||
UnpackedDelegation::new(delegation_owner2, node_identity2, raw_delegation.clone()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,17 @@
|
||||
use crate::error::ContractError;
|
||||
use crate::helpers::get_all_delegations_paged;
|
||||
use crate::storage::{
|
||||
all_gateway_delegations_read, all_mix_delegations_read, gateway_delegations_read,
|
||||
gateways_owners_read, gateways_read, mix_delegations_read, mixnodes_owners_read, mixnodes_read,
|
||||
read_layer_distribution, read_state_params, reverse_gateway_delegations_read,
|
||||
all_mix_delegations_read, gateways_owners_read, gateways_read, mix_delegations_read,
|
||||
mixnodes_owners_read, mixnodes_read, read_layer_distribution, read_state_params,
|
||||
reverse_mix_delegations_read,
|
||||
};
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{coin, Addr, Deps, Order, StdResult};
|
||||
use mixnet_contract::{
|
||||
Delegation, GatewayBond, GatewayOwnershipResponse, IdentityKey, LayerDistribution, MixNodeBond,
|
||||
MixOwnershipResponse, PagedAllDelegationsResponse, PagedGatewayDelegationsResponse,
|
||||
PagedGatewayResponse, PagedMixDelegationsResponse, PagedMixnodeResponse,
|
||||
PagedReverseGatewayDelegationsResponse, PagedReverseMixDelegationsResponse, RawDelegationData,
|
||||
StateParams,
|
||||
MixOwnershipResponse, PagedAllDelegationsResponse, PagedGatewayResponse,
|
||||
PagedMixDelegationsResponse, PagedMixnodeResponse, PagedReverseMixDelegationsResponse,
|
||||
RawDelegationData, StateParams,
|
||||
};
|
||||
|
||||
const BOND_PAGE_MAX_LIMIT: u32 = 100;
|
||||
@@ -211,114 +209,11 @@ pub(crate) fn query_mixnode_delegation(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn query_gateway_delegations_paged(
|
||||
deps: Deps,
|
||||
gateway_identity: IdentityKey,
|
||||
start_after: Option<Addr>,
|
||||
limit: Option<u32>,
|
||||
) -> StdResult<PagedGatewayDelegationsResponse> {
|
||||
let limit = limit
|
||||
.unwrap_or(DELEGATION_PAGE_DEFAULT_LIMIT)
|
||||
.min(DELEGATION_PAGE_MAX_LIMIT) as usize;
|
||||
let start = calculate_start_value(start_after);
|
||||
|
||||
let delegations = gateway_delegations_read(deps.storage, &gateway_identity)
|
||||
.range(start.as_deref(), None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| {
|
||||
res.map(|entry| {
|
||||
Delegation::new(
|
||||
Addr::unchecked(String::from_utf8(entry.0).expect(
|
||||
"Non-UTF8 address used as key in bucket. The storage is corrupted!",
|
||||
)),
|
||||
coin(entry.1.amount.u128(), DENOM),
|
||||
entry.1.block_height,
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<StdResult<Vec<Delegation>>>()?;
|
||||
|
||||
let start_next_after = delegations.last().map(|delegation| delegation.owner());
|
||||
|
||||
Ok(PagedGatewayDelegationsResponse::new(
|
||||
gateway_identity,
|
||||
delegations,
|
||||
start_next_after,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn query_all_gateway_delegations_paged(
|
||||
deps: Deps,
|
||||
start_after: Option<Vec<u8>>,
|
||||
limit: Option<u32>,
|
||||
) -> StdResult<PagedAllDelegationsResponse<RawDelegationData>> {
|
||||
let limit = limit
|
||||
.unwrap_or(DELEGATION_PAGE_DEFAULT_LIMIT)
|
||||
.min(DELEGATION_PAGE_MAX_LIMIT) as usize;
|
||||
|
||||
let bucket = all_gateway_delegations_read::<RawDelegationData>(deps.storage);
|
||||
let start = start_after.map(|mut v| {
|
||||
v.push(0);
|
||||
v
|
||||
});
|
||||
get_all_delegations_paged::<RawDelegationData>(&bucket, &start, limit)
|
||||
}
|
||||
|
||||
pub(crate) fn query_reverse_gateway_delegations_paged(
|
||||
deps: Deps,
|
||||
delegation_owner: Addr,
|
||||
start_after: Option<IdentityKey>,
|
||||
limit: Option<u32>,
|
||||
) -> StdResult<PagedReverseGatewayDelegationsResponse> {
|
||||
let limit = limit
|
||||
.unwrap_or(DELEGATION_PAGE_DEFAULT_LIMIT)
|
||||
.min(DELEGATION_PAGE_MAX_LIMIT) as usize;
|
||||
let start = calculate_start_value(start_after);
|
||||
|
||||
let delegations = reverse_gateway_delegations_read(deps.storage, &delegation_owner)
|
||||
.range(start.as_deref(), None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| {
|
||||
res.map(|entry| {
|
||||
String::from_utf8(entry.0)
|
||||
.expect("Non-UTF8 address used as key in bucket. The storage is corrupted!")
|
||||
})
|
||||
})
|
||||
.collect::<StdResult<Vec<IdentityKey>>>()?;
|
||||
|
||||
let start_next_after = delegations.last().cloned();
|
||||
|
||||
Ok(PagedReverseGatewayDelegationsResponse::new(
|
||||
delegation_owner,
|
||||
delegations,
|
||||
start_next_after,
|
||||
))
|
||||
}
|
||||
|
||||
// queries for delegation value of given address for particular node
|
||||
pub(crate) fn query_gateway_delegation(
|
||||
deps: Deps,
|
||||
gateway_identity: IdentityKey,
|
||||
address: Addr,
|
||||
) -> Result<Delegation, ContractError> {
|
||||
match gateway_delegations_read(deps.storage, &gateway_identity).may_load(address.as_bytes())? {
|
||||
Some(delegation_value) => Ok(Delegation::new(
|
||||
address,
|
||||
coin(delegation_value.amount.u128(), DENOM),
|
||||
delegation_value.block_height,
|
||||
)),
|
||||
None => Err(ContractError::NoGatewayDelegationFound {
|
||||
identity: gateway_identity,
|
||||
address,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::state::State;
|
||||
use crate::storage::{config, gateway_delegations, gateways, mix_delegations, mixnodes};
|
||||
use crate::storage::{config, gateways, mix_delegations, mixnodes};
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::{
|
||||
good_gateway_bond, good_mixnode_bond, raw_delegation_fixture,
|
||||
@@ -673,16 +568,11 @@ pub(crate) mod tests {
|
||||
minimum_mixnode_bond: 123u128.into(),
|
||||
minimum_gateway_bond: 456u128.into(),
|
||||
mixnode_bond_reward_rate: "1.23".parse().unwrap(),
|
||||
gateway_bond_reward_rate: "4.56".parse().unwrap(),
|
||||
mixnode_delegation_reward_rate: "7.89".parse().unwrap(),
|
||||
gateway_delegation_reward_rate: "0.12".parse().unwrap(),
|
||||
mixnode_active_set_size: 1000,
|
||||
gateway_active_set_size: 20,
|
||||
},
|
||||
mixnode_epoch_bond_reward: "1.23".parse().unwrap(),
|
||||
gateway_epoch_bond_reward: "4.56".parse().unwrap(),
|
||||
mixnode_epoch_delegation_reward: "7.89".parse().unwrap(),
|
||||
gateway_epoch_delegation_reward: "0.12".parse().unwrap(),
|
||||
};
|
||||
|
||||
config(deps.as_mut().storage).save(&dummy_state).unwrap();
|
||||
@@ -1203,524 +1093,4 @@ pub(crate) mod tests {
|
||||
assert_eq!(2, page2.delegated_nodes.len());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_n_gateway_delegations(
|
||||
n: u32,
|
||||
storage: &mut dyn Storage,
|
||||
node_identity: &IdentityKey,
|
||||
) {
|
||||
for i in 0..n {
|
||||
let address = format!("address{}", i);
|
||||
gateway_delegations(storage, node_identity)
|
||||
.save(address.as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod querying_for_gateway_delegations_paged {
|
||||
use super::*;
|
||||
use crate::storage::gateway_delegations;
|
||||
|
||||
#[test]
|
||||
fn retrieval_obeys_limits() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let limit = 2;
|
||||
let node_identity: IdentityKey = "foo".into();
|
||||
store_n_gateway_delegations(100, &mut deps.storage, &node_identity);
|
||||
|
||||
let page1 = query_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
node_identity,
|
||||
None,
|
||||
Option::from(limit),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(limit, page1.delegations.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retrieval_has_default_limit() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let node_identity: IdentityKey = "foo".into();
|
||||
store_n_gateway_delegations(
|
||||
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
|
||||
&mut deps.storage,
|
||||
&node_identity,
|
||||
);
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 =
|
||||
query_gateway_delegations_paged(deps.as_ref(), node_identity, None, None).unwrap();
|
||||
assert_eq!(
|
||||
DELEGATION_PAGE_DEFAULT_LIMIT,
|
||||
page1.delegations.len() as u32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retrieval_has_max_limit() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let node_identity: IdentityKey = "foo".into();
|
||||
store_n_gateway_delegations(
|
||||
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
|
||||
&mut deps.storage,
|
||||
&node_identity,
|
||||
);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000 * DELEGATION_PAGE_DEFAULT_LIMIT;
|
||||
let page1 = query_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
node_identity,
|
||||
None,
|
||||
Option::from(crazy_limit),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
let expected_limit = DELEGATION_PAGE_MAX_LIMIT;
|
||||
assert_eq!(expected_limit, page1.delegations.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pagination_works() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let node_identity: IdentityKey = "foo".into();
|
||||
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save("1".as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
|
||||
let per_page = 2;
|
||||
let page1 = query_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
node_identity.clone(),
|
||||
None,
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.delegations.len());
|
||||
|
||||
// save another
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save("2".as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
|
||||
// page1 should have 2 results on it
|
||||
let page1 = query_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
node_identity.clone(),
|
||||
None,
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(2, page1.delegations.len());
|
||||
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save("3".as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
|
||||
// page1 still has 2 results
|
||||
let page1 = query_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
node_identity.clone(),
|
||||
None,
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(2, page1.delegations.len());
|
||||
|
||||
// retrieving the next page should start after the last key on this page
|
||||
let start_after = Addr::unchecked("2");
|
||||
let page2 = query_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
node_identity.clone(),
|
||||
Option::from(start_after),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, page2.delegations.len());
|
||||
|
||||
// save another one
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save("4".as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
|
||||
let start_after = Addr::unchecked("2");
|
||||
let page2 = query_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
node_identity,
|
||||
Option::from(start_after),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.delegations.len());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_deletion_query_returns_current_delegation_value() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let node_identity: IdentityKey = "foo".into();
|
||||
let delegation_owner = Addr::unchecked("bar");
|
||||
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save(
|
||||
delegation_owner.as_bytes(),
|
||||
&&RawDelegationData::new(42u128.into(), 12_345),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Ok(Delegation::new(
|
||||
delegation_owner.clone(),
|
||||
coin(42, DENOM),
|
||||
12_345
|
||||
)),
|
||||
query_gateway_delegation(deps.as_ref(), node_identity, delegation_owner)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_deletion_query_returns_error_if_delegation_doesnt_exist() {
|
||||
let mut deps = helpers::init_contract();
|
||||
|
||||
let node_identity1: IdentityKey = "foo1".into();
|
||||
let node_identity2: IdentityKey = "foo2".into();
|
||||
let delegation_owner1 = Addr::unchecked("bar");
|
||||
let delegation_owner2 = Addr::unchecked("bar2");
|
||||
|
||||
assert_eq!(
|
||||
Err(ContractError::NoGatewayDelegationFound {
|
||||
identity: node_identity1.clone(),
|
||||
address: delegation_owner1.clone(),
|
||||
}),
|
||||
query_gateway_delegation(
|
||||
deps.as_ref(),
|
||||
node_identity1.clone(),
|
||||
delegation_owner1.clone()
|
||||
)
|
||||
);
|
||||
|
||||
// add delegation from a different address
|
||||
gateway_delegations(&mut deps.storage, &node_identity1)
|
||||
.save(delegation_owner2.as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Err(ContractError::NoGatewayDelegationFound {
|
||||
identity: node_identity1.clone(),
|
||||
address: delegation_owner1.clone(),
|
||||
}),
|
||||
query_gateway_delegation(
|
||||
deps.as_ref(),
|
||||
node_identity1.clone(),
|
||||
delegation_owner1.clone()
|
||||
)
|
||||
);
|
||||
|
||||
// add delegation for a different node
|
||||
gateway_delegations(&mut deps.storage, &node_identity2)
|
||||
.save(delegation_owner1.as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Err(ContractError::NoGatewayDelegationFound {
|
||||
identity: node_identity1.clone(),
|
||||
address: delegation_owner1.clone()
|
||||
}),
|
||||
query_gateway_delegation(deps.as_ref(), node_identity1, delegation_owner1)
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod querying_for_all_gateway_delegations_paged {
|
||||
use super::*;
|
||||
use crate::helpers::identity_and_owner_to_bytes;
|
||||
use crate::storage::gateway_delegations;
|
||||
|
||||
#[test]
|
||||
fn retrieval_obeys_limits() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let limit = 2;
|
||||
let node_identity: IdentityKey = "foo".into();
|
||||
store_n_gateway_delegations(100, &mut deps.storage, &node_identity);
|
||||
|
||||
let page1 =
|
||||
query_all_gateway_delegations_paged(deps.as_ref(), None, Option::from(limit))
|
||||
.unwrap();
|
||||
assert_eq!(limit, page1.delegations.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retrieval_has_default_limit() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let node_identity: IdentityKey = "foo".into();
|
||||
store_n_gateway_delegations(
|
||||
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
|
||||
&mut deps.storage,
|
||||
&node_identity,
|
||||
);
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_all_gateway_delegations_paged(deps.as_ref(), None, None).unwrap();
|
||||
assert_eq!(
|
||||
DELEGATION_PAGE_DEFAULT_LIMIT,
|
||||
page1.delegations.len() as u32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retrieval_has_max_limit() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let node_identity: IdentityKey = "foo".into();
|
||||
store_n_gateway_delegations(
|
||||
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
|
||||
&mut deps.storage,
|
||||
&node_identity,
|
||||
);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000 * DELEGATION_PAGE_DEFAULT_LIMIT;
|
||||
let page1 =
|
||||
query_all_gateway_delegations_paged(deps.as_ref(), None, Option::from(crazy_limit))
|
||||
.unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
let expected_limit = DELEGATION_PAGE_MAX_LIMIT;
|
||||
assert_eq!(expected_limit, page1.delegations.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pagination_works() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let node_identity: IdentityKey = "foo".into();
|
||||
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save("1".as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
|
||||
let per_page = 2;
|
||||
let page1 =
|
||||
query_all_gateway_delegations_paged(deps.as_ref(), None, Option::from(per_page))
|
||||
.unwrap();
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.delegations.len());
|
||||
|
||||
// save another
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save("2".as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
|
||||
// page1 should have 2 results on it
|
||||
let page1 =
|
||||
query_all_gateway_delegations_paged(deps.as_ref(), None, Option::from(per_page))
|
||||
.unwrap();
|
||||
assert_eq!(2, page1.delegations.len());
|
||||
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save("3".as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
|
||||
// page1 still has 2 results
|
||||
let page1 =
|
||||
query_all_gateway_delegations_paged(deps.as_ref(), None, Option::from(per_page))
|
||||
.unwrap();
|
||||
assert_eq!(2, page1.delegations.len());
|
||||
|
||||
// retrieving the next page should start after the last key on this page
|
||||
let start_after = identity_and_owner_to_bytes(&node_identity, &Addr::unchecked("2"));
|
||||
let page2 = query_all_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
Option::from(start_after.clone()),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, page2.delegations.len());
|
||||
|
||||
// save another one
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save("4".as_bytes(), &raw_delegation_fixture(42))
|
||||
.unwrap();
|
||||
|
||||
let page2 = query_all_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
Option::from(start_after),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.delegations.len());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod querying_for_reverse_gateway_delegations_paged {
|
||||
use super::*;
|
||||
use crate::storage::reverse_gateway_delegations;
|
||||
|
||||
fn store_n_reverse_delegations(n: u32, storage: &mut dyn Storage, delegation_owner: &Addr) {
|
||||
for i in 0..n {
|
||||
let node_identity = format!("node{}", i);
|
||||
reverse_gateway_delegations(storage, delegation_owner)
|
||||
.save(node_identity.as_bytes(), &())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retrieval_obeys_limits() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let limit = 2;
|
||||
let delegation_owner = Addr::unchecked("foo");
|
||||
store_n_reverse_delegations(100, &mut deps.storage, &delegation_owner);
|
||||
|
||||
let page1 = query_reverse_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
delegation_owner,
|
||||
None,
|
||||
Option::from(limit),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(limit, page1.delegated_nodes.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retrieval_has_default_limit() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let delegation_owner = Addr::unchecked("foo");
|
||||
store_n_reverse_delegations(
|
||||
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
|
||||
&mut deps.storage,
|
||||
&delegation_owner,
|
||||
);
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_reverse_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
delegation_owner,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
DELEGATION_PAGE_DEFAULT_LIMIT,
|
||||
page1.delegated_nodes.len() as u32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retrieval_has_max_limit() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let delegation_owner = Addr::unchecked("foo");
|
||||
store_n_reverse_delegations(
|
||||
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
|
||||
&mut deps.storage,
|
||||
&delegation_owner,
|
||||
);
|
||||
|
||||
// query with a crazy high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000 * DELEGATION_PAGE_DEFAULT_LIMIT;
|
||||
let page1 = query_reverse_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
delegation_owner,
|
||||
None,
|
||||
Option::from(crazy_limit),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
let expected_limit = DELEGATION_PAGE_MAX_LIMIT;
|
||||
assert_eq!(expected_limit, page1.delegated_nodes.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pagination_works() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let delegation_owner = Addr::unchecked("bar");
|
||||
|
||||
reverse_gateway_delegations(&mut deps.storage, &delegation_owner)
|
||||
.save("1".as_bytes(), &())
|
||||
.unwrap();
|
||||
|
||||
let per_page = 2;
|
||||
let page1 = query_reverse_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
delegation_owner.clone(),
|
||||
None,
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.delegated_nodes.len());
|
||||
|
||||
// save another
|
||||
reverse_gateway_delegations(&mut deps.storage, &delegation_owner)
|
||||
.save("2".as_bytes(), &())
|
||||
.unwrap();
|
||||
|
||||
// page1 should have 2 results on it
|
||||
let page1 = query_reverse_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
delegation_owner.clone(),
|
||||
None,
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(2, page1.delegated_nodes.len());
|
||||
|
||||
reverse_gateway_delegations(&mut deps.storage, &delegation_owner)
|
||||
.save("3".as_bytes(), &())
|
||||
.unwrap();
|
||||
|
||||
// page1 still has 2 results
|
||||
let page1 = query_reverse_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
delegation_owner.clone(),
|
||||
None,
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(2, page1.delegated_nodes.len());
|
||||
|
||||
// retrieving the next page should start after the last key on this page
|
||||
let start_after: IdentityKey = String::from("2");
|
||||
let page2 = query_reverse_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
delegation_owner.clone(),
|
||||
Option::from(start_after),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, page2.delegated_nodes.len());
|
||||
|
||||
// save another one
|
||||
reverse_gateway_delegations(&mut deps.storage, &delegation_owner)
|
||||
.save("4".as_bytes(), &())
|
||||
.unwrap();
|
||||
|
||||
let start_after = String::from("2");
|
||||
let page2 = query_reverse_gateway_delegations_paged(
|
||||
deps.as_ref(),
|
||||
delegation_owner,
|
||||
Option::from(start_after),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.delegated_nodes.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,5 @@ pub struct State {
|
||||
|
||||
// helper values to avoid having to recalculate them on every single payment operation
|
||||
pub mixnode_epoch_bond_reward: Decimal, // reward per epoch expressed as a decimal like 0.05
|
||||
pub gateway_epoch_bond_reward: Decimal, // reward per epoch expressed as a decimal like 0.05
|
||||
pub mixnode_epoch_delegation_reward: Decimal, // reward per epoch expressed as a decimal like 0.05
|
||||
pub gateway_epoch_delegation_reward: Decimal, // reward per epoch expressed as a decimal like 0.05
|
||||
}
|
||||
|
||||
@@ -32,9 +32,7 @@ const PREFIX_GATEWAYS: &[u8] = b"gt";
|
||||
const PREFIX_GATEWAYS_OWNERS: &[u8] = b"go";
|
||||
|
||||
const PREFIX_MIX_DELEGATION: &[u8] = b"md";
|
||||
const PREFIX_GATEWAY_DELEGATION: &[u8] = b"gd";
|
||||
const PREFIX_REVERSE_MIX_DELEGATION: &[u8] = b"dm";
|
||||
const PREFIX_REVERSE_GATEWAY_DELEGATION: &[u8] = b"dg";
|
||||
|
||||
// Contract-level stuff
|
||||
|
||||
@@ -63,14 +61,6 @@ pub(crate) fn read_mixnode_epoch_bond_reward_rate(storage: &dyn Storage) -> Deci
|
||||
.mixnode_epoch_bond_reward
|
||||
}
|
||||
|
||||
pub(crate) fn read_gateway_epoch_bond_reward_rate(storage: &dyn Storage) -> Decimal {
|
||||
// same justification as in `read_state_params` for the unwrap
|
||||
config_read(storage)
|
||||
.load()
|
||||
.unwrap()
|
||||
.gateway_epoch_bond_reward
|
||||
}
|
||||
|
||||
pub(crate) fn read_mixnode_epoch_delegation_reward_rate(storage: &dyn Storage) -> Decimal {
|
||||
// same justification as in `read_state_params` for the unwrap
|
||||
config_read(storage)
|
||||
@@ -79,14 +69,6 @@ pub(crate) fn read_mixnode_epoch_delegation_reward_rate(storage: &dyn Storage) -
|
||||
.mixnode_epoch_delegation_reward
|
||||
}
|
||||
|
||||
pub(crate) fn read_gateway_epoch_delegation_reward_rate(storage: &dyn Storage) -> Decimal {
|
||||
// same justification as in `read_state_params` for the unwrap
|
||||
config_read(storage)
|
||||
.load()
|
||||
.unwrap()
|
||||
.gateway_epoch_delegation_reward
|
||||
}
|
||||
|
||||
pub fn layer_distribution(storage: &mut dyn Storage) -> Singleton<LayerDistribution> {
|
||||
singleton(storage, LAYER_DISTRIBUTION_KEY)
|
||||
}
|
||||
@@ -214,56 +196,6 @@ pub(crate) fn increase_mix_delegated_stakes(
|
||||
Ok(total_rewarded)
|
||||
}
|
||||
|
||||
pub(crate) fn increase_gateway_delegated_stakes(
|
||||
storage: &mut dyn Storage,
|
||||
gateway_identity: IdentityKeyRef,
|
||||
scaled_reward_rate: Decimal,
|
||||
reward_blockstamp: u64,
|
||||
) -> StdResult<Uint128> {
|
||||
let chunk_size = queries::DELEGATION_PAGE_MAX_LIMIT as usize;
|
||||
|
||||
let mut total_rewarded = Uint128::zero();
|
||||
let mut chunk_start: Option<Vec<_>> = None;
|
||||
loop {
|
||||
// get `chunk_size` of delegations
|
||||
let delegations_chunk = gateway_delegations_read(storage, gateway_identity)
|
||||
.range(chunk_start.as_deref(), None, Order::Ascending)
|
||||
.take(chunk_size)
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
if delegations_chunk.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
// append 0 byte to the last value to start with whatever is the next suceeding key
|
||||
chunk_start = Some(
|
||||
delegations_chunk
|
||||
.last()
|
||||
.unwrap()
|
||||
.0
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(std::iter::once(0u8))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
// and for each of them increase the stake proportionally to the reward
|
||||
// if at least `MINIMUM_BLOCK_AGE_FOR_REWARDING` blocks have been created
|
||||
// since they delegated
|
||||
for (delegator_address, mut delegation) in delegations_chunk.into_iter() {
|
||||
if delegation.block_height + MINIMUM_BLOCK_AGE_FOR_REWARDING <= reward_blockstamp {
|
||||
let reward = delegation.amount * scaled_reward_rate;
|
||||
delegation.amount += reward;
|
||||
total_rewarded += reward;
|
||||
gateway_delegations(storage, gateway_identity)
|
||||
.save(&delegator_address, &delegation)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(total_rewarded)
|
||||
}
|
||||
|
||||
// currently not used outside tests
|
||||
#[cfg(test)]
|
||||
pub(crate) fn read_mixnode_bond(
|
||||
@@ -338,53 +270,6 @@ pub fn reverse_mix_delegations_read<'a>(
|
||||
ReadonlyBucket::multilevel(storage, &[PREFIX_REVERSE_MIX_DELEGATION, owner.as_bytes()])
|
||||
}
|
||||
|
||||
pub fn all_gateway_delegations_read<T>(storage: &dyn Storage) -> ReadonlyBucket<T>
|
||||
where
|
||||
T: Serialize + DeserializeOwned,
|
||||
{
|
||||
bucket_read(storage, PREFIX_GATEWAY_DELEGATION)
|
||||
}
|
||||
|
||||
pub fn gateway_delegations<'a>(
|
||||
storage: &'a mut dyn Storage,
|
||||
gateway_identity: IdentityKeyRef,
|
||||
) -> Bucket<'a, RawDelegationData> {
|
||||
Bucket::multilevel(
|
||||
storage,
|
||||
&[PREFIX_GATEWAY_DELEGATION, gateway_identity.as_bytes()],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn gateway_delegations_read<'a>(
|
||||
storage: &'a dyn Storage,
|
||||
gateway_identity: IdentityKeyRef,
|
||||
) -> ReadonlyBucket<'a, RawDelegationData> {
|
||||
ReadonlyBucket::multilevel(
|
||||
storage,
|
||||
&[PREFIX_GATEWAY_DELEGATION, gateway_identity.as_bytes()],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn reverse_gateway_delegations<'a>(
|
||||
storage: &'a mut dyn Storage,
|
||||
owner: &Addr,
|
||||
) -> Bucket<'a, ()> {
|
||||
Bucket::multilevel(
|
||||
storage,
|
||||
&[PREFIX_REVERSE_GATEWAY_DELEGATION, owner.as_bytes()],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn reverse_gateway_delegations_read<'a>(
|
||||
storage: &'a dyn Storage,
|
||||
owner: &Addr,
|
||||
) -> ReadonlyBucket<'a, ()> {
|
||||
ReadonlyBucket::multilevel(
|
||||
storage,
|
||||
&[PREFIX_REVERSE_GATEWAY_DELEGATION, owner.as_bytes()],
|
||||
)
|
||||
}
|
||||
|
||||
// currently not used outside tests
|
||||
#[cfg(test)]
|
||||
pub(crate) fn read_gateway_bond(
|
||||
@@ -396,17 +281,6 @@ pub(crate) fn read_gateway_bond(
|
||||
Ok(node.bond_amount.amount)
|
||||
}
|
||||
|
||||
// currently not used outside tests
|
||||
#[cfg(test)]
|
||||
pub(crate) fn read_gateway_delegation(
|
||||
storage: &dyn Storage,
|
||||
identity: &[u8],
|
||||
) -> StdResult<cosmwasm_std::Uint128> {
|
||||
let bucket = gateways_read(storage);
|
||||
let node = bucket.load(identity)?;
|
||||
Ok(node.total_delegation.amount)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -499,7 +373,6 @@ mod tests {
|
||||
|
||||
let gateway_bond = GatewayBond {
|
||||
bond_amount: coin(bond_value, DENOM),
|
||||
total_delegation: coin(0, DENOM),
|
||||
owner: node_owner.clone(),
|
||||
block_height: 12_345,
|
||||
gateway: Gateway {
|
||||
@@ -551,39 +424,6 @@ mod tests {
|
||||
assert_eq!(raw_delegation2, res2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_gateway_delegations_read_retrieval() {
|
||||
let mut deps = mock_dependencies(&[]);
|
||||
let node_identity1: IdentityKey = "foo1".into();
|
||||
let delegation_owner1 = Addr::unchecked("bar1");
|
||||
let node_identity2: IdentityKey = "foo2".into();
|
||||
let delegation_owner2 = Addr::unchecked("bar2");
|
||||
let raw_delegation1 = RawDelegationData::new(1u128.into(), 1000);
|
||||
let raw_delegation2 = RawDelegationData::new(2u128.into(), 2000);
|
||||
|
||||
gateway_delegations(&mut deps.storage, &node_identity1)
|
||||
.save(delegation_owner1.as_bytes(), &raw_delegation1)
|
||||
.unwrap();
|
||||
gateway_delegations(&mut deps.storage, &node_identity2)
|
||||
.save(delegation_owner2.as_bytes(), &raw_delegation2)
|
||||
.unwrap();
|
||||
|
||||
let res1 = all_gateway_delegations_read::<RawDelegationData>(&deps.storage)
|
||||
.load(&*identity_and_owner_to_bytes(
|
||||
&node_identity1,
|
||||
&delegation_owner1,
|
||||
))
|
||||
.unwrap();
|
||||
let res2 = all_gateway_delegations_read::<RawDelegationData>(&deps.storage)
|
||||
.load(&*identity_and_owner_to_bytes(
|
||||
&node_identity2,
|
||||
&delegation_owner2,
|
||||
))
|
||||
.unwrap();
|
||||
assert_eq!(raw_delegation1, res1);
|
||||
assert_eq!(raw_delegation2, res2);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod increasing_mix_delegated_stakes {
|
||||
use super::*;
|
||||
@@ -858,279 +698,4 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod increasing_gateway_delegated_stakes {
|
||||
use super::*;
|
||||
use crate::queries::query_gateway_delegations_paged;
|
||||
use cosmwasm_std::testing::mock_dependencies;
|
||||
|
||||
#[test]
|
||||
fn when_there_are_no_delegations() {
|
||||
let mut deps = mock_dependencies(&[]);
|
||||
let node_identity: IdentityKey = "nodeidentity".into();
|
||||
|
||||
// 0.001
|
||||
let reward = Decimal::from_ratio(1u128, 1000u128);
|
||||
|
||||
let total_increase = increase_gateway_delegated_stakes(
|
||||
&mut deps.storage,
|
||||
node_identity.as_ref(),
|
||||
reward,
|
||||
42,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// there was no increase
|
||||
assert!(total_increase.is_zero());
|
||||
|
||||
// there are no 'new' delegations magically added
|
||||
assert!(
|
||||
query_gateway_delegations_paged(deps.as_ref(), node_identity, None, None)
|
||||
.unwrap()
|
||||
.delegations
|
||||
.is_empty()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_there_is_a_single_delegation() {
|
||||
let mut deps = mock_dependencies(&[]);
|
||||
let node_identity: IdentityKey = "nodeidentity".into();
|
||||
let delegation_blockstamp = 42;
|
||||
|
||||
// 0.001
|
||||
let reward = Decimal::from_ratio(1u128, 1000u128);
|
||||
|
||||
let delegator_address = Addr::unchecked("bob");
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save(
|
||||
delegator_address.as_bytes(),
|
||||
&RawDelegationData::new(1000u128.into(), delegation_blockstamp),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let total_increase = increase_gateway_delegated_stakes(
|
||||
&mut deps.storage,
|
||||
node_identity.as_ref(),
|
||||
reward,
|
||||
delegation_blockstamp + 2 * MINIMUM_BLOCK_AGE_FOR_REWARDING,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Uint128(1), total_increase);
|
||||
|
||||
// amount is incremented, block height remains the same
|
||||
assert_eq!(
|
||||
RawDelegationData::new(1001u128.into(), 42),
|
||||
gateway_delegations_read(&mut deps.storage, &node_identity)
|
||||
.load(delegator_address.as_bytes())
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_there_is_a_single_delegation_depending_on_blockstamp() {
|
||||
let mut deps = mock_dependencies(&[]);
|
||||
let node_identity: IdentityKey = "nodeidentity".into();
|
||||
let delegation_blockstamp = 42;
|
||||
|
||||
// 0.001
|
||||
let reward = Decimal::from_ratio(1u128, 1000u128);
|
||||
|
||||
let delegator_address = Addr::unchecked("bob");
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save(
|
||||
delegator_address.as_bytes(),
|
||||
&RawDelegationData::new(1000u128.into(), delegation_blockstamp),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let total_increase = increase_gateway_delegated_stakes(
|
||||
&mut deps.storage,
|
||||
node_identity.as_ref(),
|
||||
reward,
|
||||
delegation_blockstamp + MINIMUM_BLOCK_AGE_FOR_REWARDING - 1,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// there was no increase
|
||||
assert!(total_increase.is_zero());
|
||||
|
||||
// amount is not incremented
|
||||
assert_eq!(
|
||||
RawDelegationData::new(1000u128.into(), delegation_blockstamp),
|
||||
gateway_delegations_read(&mut deps.storage, &node_identity)
|
||||
.load(delegator_address.as_bytes())
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let total_increase = increase_gateway_delegated_stakes(
|
||||
&mut deps.storage,
|
||||
node_identity.as_ref(),
|
||||
reward,
|
||||
delegation_blockstamp + MINIMUM_BLOCK_AGE_FOR_REWARDING,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// there is an increase now, that the lock period has passed
|
||||
assert_eq!(Uint128(1), total_increase);
|
||||
|
||||
// amount is incremented
|
||||
assert_eq!(
|
||||
RawDelegationData::new(1001u128.into(), delegation_blockstamp),
|
||||
gateway_delegations_read(&mut deps.storage, &node_identity)
|
||||
.load(delegator_address.as_bytes())
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_there_are_multiple_delegations() {
|
||||
let mut deps = mock_dependencies(&[]);
|
||||
let node_identity: IdentityKey = "nodeidentity".into();
|
||||
let delegation_blockstamp = 42;
|
||||
|
||||
// 0.001
|
||||
let reward = Decimal::from_ratio(1u128, 1000u128);
|
||||
|
||||
for i in 0..100 {
|
||||
let delegator_address = Addr::unchecked(format!("address{}", i));
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save(
|
||||
delegator_address.as_bytes(),
|
||||
&RawDelegationData::new(1000u128.into(), delegation_blockstamp),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let total_increase = increase_gateway_delegated_stakes(
|
||||
&mut deps.storage,
|
||||
node_identity.as_ref(),
|
||||
reward,
|
||||
delegation_blockstamp + 2 * MINIMUM_BLOCK_AGE_FOR_REWARDING,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Uint128(100), total_increase);
|
||||
|
||||
for i in 0..100 {
|
||||
let delegator_address = Addr::unchecked(format!("address{}", i));
|
||||
assert_eq!(
|
||||
raw_delegation_fixture(1001),
|
||||
gateway_delegations_read(&mut deps.storage, &node_identity)
|
||||
.load(delegator_address.as_bytes())
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_there_are_more_delegations_than_page_size() {
|
||||
let mut deps = mock_dependencies(&[]);
|
||||
let node_identity: IdentityKey = "nodeidentity".into();
|
||||
let delegation_blockstamp = 42;
|
||||
|
||||
// 0.001
|
||||
let reward = Decimal::from_ratio(1u128, 1000u128);
|
||||
|
||||
for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
|
||||
let delegator_address = Addr::unchecked(format!("address{}", i));
|
||||
gateway_delegations(&mut deps.storage, &node_identity)
|
||||
.save(
|
||||
delegator_address.as_bytes(),
|
||||
&RawDelegationData::new(1000u128.into(), delegation_blockstamp),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let total_increase = increase_gateway_delegated_stakes(
|
||||
&mut deps.storage,
|
||||
node_identity.as_ref(),
|
||||
reward,
|
||||
delegation_blockstamp + 2 * MINIMUM_BLOCK_AGE_FOR_REWARDING,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Uint128(queries::DELEGATION_PAGE_MAX_LIMIT as u128 * 10),
|
||||
total_increase
|
||||
);
|
||||
|
||||
for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
|
||||
let delegator_address = Addr::unchecked(format!("address{}", i));
|
||||
assert_eq!(
|
||||
raw_delegation_fixture(1001),
|
||||
gateway_delegations_read(&mut deps.storage, &node_identity)
|
||||
.load(delegator_address.as_bytes())
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod reverse_gateway_delegations {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers;
|
||||
|
||||
#[test]
|
||||
fn reverse_gateway_delegation_exists() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let node_identity: IdentityKey = "foo".into();
|
||||
let delegation_owner = Addr::unchecked("bar");
|
||||
|
||||
reverse_gateway_delegations(&mut deps.storage, &delegation_owner)
|
||||
.save(node_identity.as_bytes(), &())
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
reverse_gateway_delegations_read(deps.as_ref().storage, &delegation_owner)
|
||||
.may_load(node_identity.as_bytes())
|
||||
.unwrap()
|
||||
.is_some(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reverse_gateway_delegation_returns_none_if_delegation_doesnt_exist() {
|
||||
let mut deps = helpers::init_contract();
|
||||
|
||||
let node_identity1: IdentityKey = "foo1".into();
|
||||
let node_identity2: IdentityKey = "foo2".into();
|
||||
let delegation_owner1 = Addr::unchecked("bar");
|
||||
let delegation_owner2 = Addr::unchecked("bar2");
|
||||
|
||||
assert!(
|
||||
reverse_gateway_delegations_read(deps.as_ref().storage, &delegation_owner1)
|
||||
.may_load(node_identity1.as_bytes())
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
|
||||
// add delegation for a different node
|
||||
reverse_gateway_delegations(&mut deps.storage, &delegation_owner1)
|
||||
.save(node_identity2.as_bytes(), &())
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
reverse_gateway_delegations_read(deps.as_ref().storage, &delegation_owner1)
|
||||
.may_load(node_identity1.as_bytes())
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
|
||||
// add delegation from a different owner
|
||||
reverse_gateway_delegations(&mut deps.storage, &delegation_owner2)
|
||||
.save(node_identity1.as_bytes(), &())
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
reverse_gateway_delegations_read(deps.as_ref().storage, &delegation_owner1)
|
||||
.may_load(node_identity1.as_bytes())
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -58,15 +58,12 @@ fn main() {
|
||||
unbond_gateway,
|
||||
delegate_to_mixnode,
|
||||
undelegate_from_mixnode,
|
||||
delegate_to_gateway,
|
||||
undelegate_from_gateway,
|
||||
send,
|
||||
create_new_account,
|
||||
get_fee,
|
||||
get_state_params,
|
||||
update_state_params,
|
||||
get_reverse_mix_delegations_paged,
|
||||
get_reverse_gateway_delegations_paged,
|
||||
])
|
||||
.menu(create_menu_items())
|
||||
.run(tauri::generate_context!())
|
||||
|
||||
@@ -16,11 +16,8 @@ pub struct TauriStateParams {
|
||||
minimum_mixnode_bond: String,
|
||||
minimum_gateway_bond: String,
|
||||
mixnode_bond_reward_rate: String,
|
||||
gateway_bond_reward_rate: String,
|
||||
mixnode_delegation_reward_rate: String,
|
||||
gateway_delegation_reward_rate: String,
|
||||
mixnode_active_set_size: u32,
|
||||
gateway_active_set_size: u32,
|
||||
}
|
||||
|
||||
impl From<StateParams> for TauriStateParams {
|
||||
@@ -30,11 +27,8 @@ impl From<StateParams> for TauriStateParams {
|
||||
minimum_mixnode_bond: p.minimum_mixnode_bond.to_string(),
|
||||
minimum_gateway_bond: p.minimum_gateway_bond.to_string(),
|
||||
mixnode_bond_reward_rate: p.mixnode_bond_reward_rate.to_string(),
|
||||
gateway_bond_reward_rate: p.gateway_bond_reward_rate.to_string(),
|
||||
mixnode_delegation_reward_rate: p.mixnode_delegation_reward_rate.to_string(),
|
||||
gateway_delegation_reward_rate: p.gateway_delegation_reward_rate.to_string(),
|
||||
mixnode_active_set_size: p.mixnode_active_set_size,
|
||||
gateway_active_set_size: p.gateway_active_set_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,11 +42,8 @@ impl TryFrom<TauriStateParams> for StateParams {
|
||||
minimum_mixnode_bond: Uint128::try_from(p.minimum_mixnode_bond.as_str())?,
|
||||
minimum_gateway_bond: Uint128::try_from(p.minimum_gateway_bond.as_str())?,
|
||||
mixnode_bond_reward_rate: Decimal::from_str(p.mixnode_bond_reward_rate.as_str())?,
|
||||
gateway_bond_reward_rate: Decimal::from_str(p.gateway_bond_reward_rate.as_str())?,
|
||||
mixnode_delegation_reward_rate: Decimal::from_str(p.mixnode_delegation_reward_rate.as_str())?,
|
||||
gateway_delegation_reward_rate: Decimal::from_str(p.gateway_delegation_reward_rate.as_str())?,
|
||||
mixnode_active_set_size: p.mixnode_active_set_size,
|
||||
gateway_active_set_size: p.gateway_active_set_size,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ use crate::coin::Coin;
|
||||
use crate::format_err;
|
||||
use crate::state::State;
|
||||
use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
use mixnet_contract::{
|
||||
Addr, PagedReverseGatewayDelegationsResponse, PagedReverseMixDelegationsResponse,
|
||||
};
|
||||
use mixnet_contract::{Addr, PagedReverseMixDelegationsResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
@@ -57,45 +55,6 @@ pub async fn undelegate_from_mixnode(
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn delegate_to_gateway(
|
||||
identity: &str,
|
||||
amount: Coin,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<DelegationResult, String> {
|
||||
let r_state = state.read().await;
|
||||
let bond: CosmWasmCoin = match amount.try_into() {
|
||||
Ok(b) => b,
|
||||
Err(e) => return Err(format_err!(e)),
|
||||
};
|
||||
let client = r_state.client()?;
|
||||
match client.delegate_to_gateway(identity, &bond).await {
|
||||
Ok(_result) => Ok(DelegationResult {
|
||||
source_address: client.address().to_string(),
|
||||
target_address: identity.to_string(),
|
||||
amount: Some(bond.into()),
|
||||
}),
|
||||
Err(e) => Err(format_err!(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn undelegate_from_gateway(
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<DelegationResult, String> {
|
||||
let r_state = state.read().await;
|
||||
let client = r_state.client()?;
|
||||
match client.remove_gateway_delegation(identity).await {
|
||||
Ok(_result) => Ok(DelegationResult {
|
||||
source_address: client.address().to_string(),
|
||||
target_address: identity.to_string(),
|
||||
amount: None,
|
||||
}),
|
||||
Err(e) => Err(format_err!(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_reverse_mix_delegations_paged(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
@@ -104,16 +63,6 @@ pub async fn get_reverse_mix_delegations_paged(
|
||||
let client = r_state.client()?;
|
||||
client
|
||||
.get_reverse_mix_delegations_paged(Addr::unchecked(client.address().as_ref()), None, None)
|
||||
.await.map_err(|err| format_err!(err))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_reverse_gateway_delegations_paged(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<PagedReverseGatewayDelegationsResponse, String> {
|
||||
let r_state = state.read().await;
|
||||
let client = r_state.client()?;
|
||||
client
|
||||
.get_reverse_gateway_delegations_paged(Addr::unchecked(client.address().as_ref()), None, None)
|
||||
.await.map_err(|err| format_err!(err))
|
||||
.await
|
||||
.map_err(|err| format_err!(err))
|
||||
}
|
||||
|
||||
@@ -3,9 +3,6 @@ export interface TauriStateParams {
|
||||
minimum_mixnode_bond: string;
|
||||
minimum_gateway_bond: string;
|
||||
mixnode_bond_reward_rate: string;
|
||||
gateway_bond_reward_rate: string;
|
||||
mixnode_delegation_reward_rate: string;
|
||||
gateway_delegation_reward_rate: string;
|
||||
mixnode_active_set_size: number;
|
||||
gateway_active_set_size: number;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
ALTER TABLE rewarding_report RENAME TO _rewarding_report_old;
|
||||
|
||||
CREATE TABLE rewarding_report
|
||||
(
|
||||
epoch_rewarding_id INTEGER NOT NULL,
|
||||
|
||||
eligible_mixnodes INTEGER NOT NULL,
|
||||
|
||||
possibly_unrewarded_mixnodes INTEGER NOT NULL,
|
||||
|
||||
FOREIGN KEY (epoch_rewarding_id) REFERENCES epoch_rewarding (id)
|
||||
);
|
||||
|
||||
INSERT INTO rewarding_report (epoch_rewarding_id, eligible_mixnodes, possibly_unrewarded_mixnodes)
|
||||
SELECT epoch_rewarding_id, eligible_mixnodes, possibly_unrewarded_mixnodes
|
||||
FROM _rewarding_report_old;
|
||||
|
||||
DROP TABLE _rewarding_report_old;
|
||||
DROP TABLE failed_gateway_reward_chunk;
|
||||
DROP TABLE possibly_unrewarded_gateway;
|
||||
Vendored
+1
-66
@@ -33,10 +33,7 @@ struct ValidatorCacheInner {
|
||||
gateways: RwLock<Cache<Vec<GatewayBond>>>,
|
||||
|
||||
active_mixnodes_available: AtomicBool,
|
||||
active_gateways_available: AtomicBool,
|
||||
|
||||
current_mixnode_active_set_size: AtomicUsize,
|
||||
current_gateway_active_set_size: AtomicUsize,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Clone)]
|
||||
@@ -131,7 +128,6 @@ impl ValidatorCache {
|
||||
routes::get_mixnodes,
|
||||
routes::get_gateways,
|
||||
routes::get_active_mixnodes,
|
||||
routes::get_active_gateways
|
||||
],
|
||||
)
|
||||
})
|
||||
@@ -155,28 +151,10 @@ impl ValidatorCache {
|
||||
true
|
||||
}
|
||||
|
||||
// TODO: check if all nodes can be compared together,
|
||||
// i.e. they all have the same denom for bonds and delegations
|
||||
fn verify_gateways(&self, gateways: &[GatewayBond]) -> bool {
|
||||
if gateways.is_empty() {
|
||||
return true;
|
||||
}
|
||||
let expected_denom = &gateways[0].bond_amount.denom;
|
||||
for gateway in gateways {
|
||||
if &gateway.bond_amount.denom != expected_denom
|
||||
|| &gateway.total_delegation.denom != expected_denom
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
async fn update_cache(
|
||||
&self,
|
||||
mut mixnodes: Vec<MixNodeBond>,
|
||||
mut gateways: Vec<GatewayBond>,
|
||||
gateways: Vec<GatewayBond>,
|
||||
state: StateParams,
|
||||
) {
|
||||
// if our data is valid, it means the active sets are available,
|
||||
@@ -199,23 +177,6 @@ impl ValidatorCache {
|
||||
.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if self.verify_gateways(&gateways) {
|
||||
// partial_cmp can only fail if the nodes have different denomination,
|
||||
// but we just checked for that hence the unwraps are fine here
|
||||
// Note the reverse order of comparison so that the "highest" node would be first
|
||||
gateways.sort_by(|a, b| b.partial_cmp(a).unwrap());
|
||||
self.inner
|
||||
.active_gateways_available
|
||||
.store(true, Ordering::SeqCst);
|
||||
self.inner
|
||||
.current_gateway_active_set_size
|
||||
.store(state.gateway_active_set_size as usize, Ordering::SeqCst);
|
||||
} else {
|
||||
self.inner
|
||||
.active_gateways_available
|
||||
.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
self.inner.mixnodes.write().await.set(mixnodes);
|
||||
self.inner.gateways.write().await.set(gateways);
|
||||
}
|
||||
@@ -252,30 +213,6 @@ impl ValidatorCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn active_gateways(&self) -> Option<Cache<Vec<GatewayBond>>> {
|
||||
// if active set is available, it means it is already sorted
|
||||
if self.inner.active_gateways_available.load(Ordering::SeqCst) {
|
||||
let cache = self.inner.gateways.read().await;
|
||||
let timestamp = cache.as_at;
|
||||
let nodes = cache
|
||||
.value
|
||||
.iter()
|
||||
.take(
|
||||
self.inner
|
||||
.current_gateway_active_set_size
|
||||
.load(Ordering::SeqCst),
|
||||
)
|
||||
.cloned()
|
||||
.collect();
|
||||
Some(Cache {
|
||||
value: nodes,
|
||||
as_at: timestamp,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialised(&self) -> bool {
|
||||
self.inner.initialised.load(Ordering::Relaxed)
|
||||
}
|
||||
@@ -300,9 +237,7 @@ impl ValidatorCacheInner {
|
||||
mixnodes: RwLock::new(Cache::default()),
|
||||
gateways: RwLock::new(Cache::default()),
|
||||
active_mixnodes_available: AtomicBool::new(false),
|
||||
active_gateways_available: AtomicBool::new(false),
|
||||
current_mixnode_active_set_size: Default::default(),
|
||||
current_gateway_active_set_size: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
-7
@@ -22,10 +22,3 @@ pub(crate) async fn get_active_mixnodes(
|
||||
) -> Option<Json<Vec<MixNodeBond>>> {
|
||||
cache.active_mixnodes().await.map(|cache| Json(cache.value))
|
||||
}
|
||||
|
||||
#[get("/gateways/active")]
|
||||
pub(crate) async fn get_active_gateways(
|
||||
cache: &State<ValidatorCache>,
|
||||
) -> Option<Json<Vec<GatewayBond>>> {
|
||||
cache.active_gateways().await.map(|cache| Json(cache.value))
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::rewarding::{
|
||||
error::RewardingError, GatewayToReward, MixnodeToReward, GATEWAY_REWARD_OP_BASE_GAS_LIMIT,
|
||||
MIXNODE_REWARD_OP_BASE_GAS_LIMIT, PER_GATEWAY_DELEGATION_GAS_INCREASE,
|
||||
error::RewardingError, MixnodeToReward, MIXNODE_REWARD_OP_BASE_GAS_LIMIT,
|
||||
PER_MIXNODE_DELEGATION_GAS_INCREASE, REWARDING_GAS_LIMIT_MULTIPLIER,
|
||||
};
|
||||
use config::defaults::DEFAULT_VALIDATOR_API_PORT;
|
||||
@@ -129,20 +128,6 @@ impl<C> Client<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_gateway_delegations(
|
||||
&self,
|
||||
identity: IdentityKey,
|
||||
) -> Result<Vec<Delegation>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
self.0
|
||||
.read()
|
||||
.await
|
||||
.get_all_nymd_single_gateway_delegations(identity)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn estimate_mixnode_reward_fees(&self, nodes: usize, total_delegations: usize) -> Fee {
|
||||
let base_gas_limit = MIXNODE_REWARD_OP_BASE_GAS_LIMIT * nodes as u64
|
||||
+ PER_MIXNODE_DELEGATION_GAS_INCREASE * total_delegations as u64;
|
||||
@@ -156,19 +141,6 @@ impl<C> Client<C> {
|
||||
.calculate_custom_fee(total_gas_limit)
|
||||
}
|
||||
|
||||
async fn estimate_gateway_reward_fees(&self, nodes: usize, total_delegations: usize) -> Fee {
|
||||
let base_gas_limit = GATEWAY_REWARD_OP_BASE_GAS_LIMIT * nodes as u64
|
||||
+ PER_GATEWAY_DELEGATION_GAS_INCREASE * total_delegations as u64;
|
||||
|
||||
let total_gas_limit = (base_gas_limit as f64 * REWARDING_GAS_LIMIT_MULTIPLIER) as u64;
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.await
|
||||
.nymd
|
||||
.calculate_custom_fee(total_gas_limit)
|
||||
}
|
||||
|
||||
pub(crate) async fn reward_mixnodes(
|
||||
&self,
|
||||
nodes: &[MixnodeToReward],
|
||||
@@ -236,72 +208,4 @@ impl<C> Client<C> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn reward_gateways(
|
||||
&self,
|
||||
nodes: &[GatewayToReward],
|
||||
) -> Result<(), RewardingError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
let total_delegations = nodes.iter().map(|node| node.total_delegations).sum();
|
||||
let fee = self
|
||||
.estimate_gateway_reward_fees(nodes.len(), total_delegations)
|
||||
.await;
|
||||
let msgs: Vec<(ExecuteMsg, _)> = nodes
|
||||
.iter()
|
||||
.map(Into::into)
|
||||
.zip(std::iter::repeat(Vec::new()))
|
||||
.collect();
|
||||
|
||||
let memo = format!("rewarding {} gateways", msgs.len());
|
||||
|
||||
let contract = self
|
||||
.0
|
||||
.read()
|
||||
.await
|
||||
.get_mixnet_contract_address()
|
||||
.ok_or(RewardingError::UnspecifiedContractAddress)?;
|
||||
|
||||
// grab the write lock here so we're sure nothing else is executing anything on the contract
|
||||
// in the meantime
|
||||
// however, we're not 100% guarded against everything
|
||||
// for example somebody might have taken the mnemonic used by the validator
|
||||
// and sent a transaction manually using the same account. The sequence number
|
||||
// would have gotten incremented, yet the rewarding transaction might have actually not
|
||||
// been included in the block. sadly we can't do much about that.
|
||||
let client_guard = self.0.write().await;
|
||||
let pre_sequence = client_guard.nymd.account_sequence().await?;
|
||||
|
||||
let res = client_guard
|
||||
.nymd
|
||||
.execute_multiple(&contract, msgs.clone(), fee.clone(), memo.clone())
|
||||
.await;
|
||||
|
||||
match res {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
if err.is_tendermint_response_timeout() {
|
||||
// wait until we're sure we're into the next block (remember we're holding the lock)
|
||||
sleep(Duration::from_secs(11)).await;
|
||||
let curr_sequence = client_guard.nymd.account_sequence().await?;
|
||||
if curr_sequence.sequence > pre_sequence.sequence {
|
||||
// unless somebody was messing around doing stuff manually in that tiny time interval
|
||||
// we're good. It was a false negative.
|
||||
Ok(())
|
||||
} else {
|
||||
// the sequence number has not increased, meaning the transaction was not executed
|
||||
// so attempt to send it again
|
||||
client_guard
|
||||
.nymd
|
||||
.execute_multiple(&contract, msgs, fee, memo)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
Err(err.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,6 @@ pub(crate) enum RewardingError {
|
||||
#[error("There were no mixnodes to reward (network is dead)")]
|
||||
NoMixnodesToReward,
|
||||
|
||||
#[error("There were no gateways to reward (network is dead)")]
|
||||
NoGatewaysToReward,
|
||||
|
||||
#[error("Failed to execute the smart contract - {0}")]
|
||||
ContractExecutionFailure(NymdError),
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@ use crate::nymd_client::Client;
|
||||
use crate::rewarding::epoch::Epoch;
|
||||
use crate::rewarding::error::RewardingError;
|
||||
use crate::storage::models::{
|
||||
FailedGatewayRewardChunk, FailedMixnodeRewardChunk, PossiblyUnrewardedGateway,
|
||||
PossiblyUnrewardedMixnode, RewardingReport,
|
||||
FailedMixnodeRewardChunk, PossiblyUnrewardedMixnode, RewardingReport,
|
||||
};
|
||||
use crate::storage::NodeStatusStorage;
|
||||
use log::{error, info};
|
||||
@@ -27,7 +26,6 @@ pub(crate) mod error;
|
||||
// the actual base cost is around 125_000, but let's give ourselves a bit of safety net in case
|
||||
// we introduce some tiny contract changes that would bump that value up
|
||||
pub(crate) const MIXNODE_REWARD_OP_BASE_GAS_LIMIT: u64 = 150_000;
|
||||
pub(crate) const GATEWAY_REWARD_OP_BASE_GAS_LIMIT: u64 = 150_000;
|
||||
|
||||
// For each delegation reward we perform a read and a write is being executed,
|
||||
// which are the most costly parts involved in process. Both of them are ~1000 sdk gas in cost.
|
||||
@@ -36,7 +34,6 @@ pub(crate) const GATEWAY_REWARD_OP_BASE_GAS_LIMIT: u64 = 150_000;
|
||||
// Therefore, since base cost is not tuned to the bare minimum, let's treat all of delegations as extra
|
||||
// 2750 of sdk gas.
|
||||
pub(crate) const PER_MIXNODE_DELEGATION_GAS_INCREASE: u64 = 2750;
|
||||
pub(crate) const PER_GATEWAY_DELEGATION_GAS_INCREASE: u64 = 2750;
|
||||
|
||||
// Another safety net in case of contract changes,
|
||||
// the calculated total gas limit is going to get multiplied by that value.
|
||||
@@ -53,29 +50,14 @@ pub(crate) struct MixnodeToReward {
|
||||
pub(crate) total_delegations: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct GatewayToReward {
|
||||
pub(crate) identity: IdentityKey,
|
||||
pub(crate) uptime: Uptime,
|
||||
|
||||
/// Total number of individual addresses that have delegated to this particular gateway
|
||||
pub(crate) total_delegations: usize,
|
||||
}
|
||||
|
||||
pub(crate) struct FailedMixnodeRewardChunkDetails {
|
||||
possibly_unrewarded: Vec<MixnodeToReward>,
|
||||
error_message: String,
|
||||
}
|
||||
|
||||
pub(crate) struct FailedGatewayRewardChunkDetails {
|
||||
possibly_unrewarded: Vec<GatewayToReward>,
|
||||
error_message: String,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct FailureData {
|
||||
mixnodes: Option<Vec<FailedMixnodeRewardChunkDetails>>,
|
||||
gateways: Option<Vec<FailedGatewayRewardChunkDetails>>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a MixnodeToReward> for ExecuteMsg {
|
||||
@@ -87,15 +69,6 @@ impl<'a> From<&'a MixnodeToReward> for ExecuteMsg {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a GatewayToReward> for ExecuteMsg {
|
||||
fn from(node: &GatewayToReward) -> Self {
|
||||
ExecuteMsg::RewardGateway {
|
||||
identity: node.identity.clone(),
|
||||
uptime: node.uptime.u8() as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Rewarder {
|
||||
nymd_client: Client<SigningNymdClient>,
|
||||
validator_cache: ValidatorCache,
|
||||
@@ -149,22 +122,6 @@ impl Rewarder {
|
||||
.len())
|
||||
}
|
||||
|
||||
/// Obtains the current number of delegators that have delegated their stake towards this particular gateway.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `identity`: identity key of the gateway
|
||||
async fn get_gateway_delegators_count(
|
||||
&self,
|
||||
identity: IdentityKey,
|
||||
) -> Result<usize, RewardingError> {
|
||||
Ok(self
|
||||
.nymd_client
|
||||
.get_gateway_delegations(identity)
|
||||
.await?
|
||||
.len())
|
||||
}
|
||||
|
||||
/// Queries the smart contract in order to obtain the current list of bonded mixnodes and then
|
||||
/// for each mixnode determines how many delegators it has.
|
||||
async fn produce_active_mixnode_delegators_map(
|
||||
@@ -202,30 +159,6 @@ impl Rewarder {
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
/// Queries the smart contract in order to obtain the current list of bonded gateways and then
|
||||
/// for each gateway determines how many delegators it has.
|
||||
async fn produce_active_gateway_delegators_map(
|
||||
&self,
|
||||
) -> Result<HashMap<IdentityKey, usize>, RewardingError> {
|
||||
// look at comments in `produce_mixnode_delegators_map` for some optimisation elaboration
|
||||
let mut map = HashMap::new();
|
||||
|
||||
let active_bonded_gateways = self
|
||||
.validator_cache
|
||||
.active_gateways()
|
||||
.await
|
||||
.ok_or(RewardingError::NoGatewaysToReward)?
|
||||
.into_inner();
|
||||
for gateway in active_bonded_gateways.into_iter() {
|
||||
let delegator_count = self
|
||||
.get_gateway_delegators_count(gateway.gateway.identity_key.clone())
|
||||
.await?;
|
||||
map.insert(gateway.gateway.identity_key, delegator_count);
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
/// Calculates the absolute uptime of given node that is then passed as one of the arguments
|
||||
/// in the smart contract to determine the actual reward value.
|
||||
///
|
||||
@@ -290,46 +223,6 @@ impl Rewarder {
|
||||
Ok(eligible_nodes)
|
||||
}
|
||||
|
||||
/// Given the list of gateways that were tested in the last epoch, tries to determine the
|
||||
/// subset that are eligible for any rewards.
|
||||
///
|
||||
/// As of right now, it is a rather straightforward process. It is checked whether the node
|
||||
/// is currently bonded, has uptime > 0 and is part of the "active" set.
|
||||
/// Unlike the typescript rewards script, it currently does not look at the non-mixing ports are open.
|
||||
///
|
||||
/// The method also obtains the number of delegators towards the node in order to more accurately
|
||||
/// approximate the required gas fees when distributing the rewards.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `active_gateways`: list of the nodes that were tested at least once by the network monitor
|
||||
/// in the last epoch.
|
||||
async fn determine_eligible_gateways(
|
||||
&self,
|
||||
active_gateways: &[GatewayStatusReport],
|
||||
) -> Result<Vec<GatewayToReward>, RewardingError> {
|
||||
let gateway_delegators = self.produce_active_gateway_delegators_map().await?;
|
||||
|
||||
let eligible_nodes = active_gateways
|
||||
.iter()
|
||||
.filter_map(|gateway| {
|
||||
gateway_delegators
|
||||
.get(&gateway.identity)
|
||||
.map(|&total_delegations| GatewayToReward {
|
||||
identity: gateway.identity.clone(),
|
||||
uptime: self.calculate_absolute_uptime(
|
||||
gateway.last_day_ipv4,
|
||||
gateway.last_day_ipv6,
|
||||
),
|
||||
total_delegations,
|
||||
})
|
||||
})
|
||||
.filter(|node| node.uptime.u8() > 0)
|
||||
.collect();
|
||||
|
||||
Ok(eligible_nodes)
|
||||
}
|
||||
|
||||
/// Obtains the lists of all mixnodes and gateways that were tested at least a single time
|
||||
/// by the network monitor in the specified epoch.
|
||||
///
|
||||
@@ -410,58 +303,6 @@ impl Rewarder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Using the list of gateways eligible for rewards, chunks it into pre-defined sized-chunks
|
||||
/// and gives out the rewards by calling the smart contract.
|
||||
///
|
||||
/// Returns an optional vector containing list of chunks that experienced a smart contract
|
||||
/// execution error during reward distribution. However, it does not necessarily imply they
|
||||
/// were not rewarded. There are some edge cases where we time out waiting for block to be included
|
||||
/// yet the transactions went through.
|
||||
///
|
||||
/// Only returns errors for problems originating from before smart contract was called, i.e.
|
||||
/// we know for sure not a single node has been rewarded.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `eligible_gateways`: list of the nodes that are eligible to receive non-zero rewards.
|
||||
async fn distribute_rewards_to_gateways(
|
||||
&self,
|
||||
eligible_gateways: &[GatewayToReward],
|
||||
) -> Option<Vec<FailedGatewayRewardChunkDetails>> {
|
||||
let mut failed_chunks = Vec::new();
|
||||
|
||||
for (i, gateway_chunk) in eligible_gateways.chunks(MAX_TO_REWARD_AT_ONCE).enumerate() {
|
||||
if let Err(err) = self.nymd_client.reward_gateways(gateway_chunk).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
|
||||
if !err.is_tendermint_duplicate() {
|
||||
error!("failed to reward gateways... - {}", err);
|
||||
failed_chunks.push(FailedGatewayRewardChunkDetails {
|
||||
possibly_unrewarded: gateway_chunk.to_vec(),
|
||||
error_message: err.to_string(),
|
||||
});
|
||||
}
|
||||
sleep(Duration::from_secs(11)).await;
|
||||
}
|
||||
|
||||
let rewarded = i * MAX_TO_REWARD_AT_ONCE + gateway_chunk.len();
|
||||
let percentage = rewarded as f32 * 100.0 / eligible_gateways.len() as f32;
|
||||
info!(
|
||||
"Rewarded {} / {} gateways\t{:.2}%",
|
||||
rewarded,
|
||||
eligible_gateways.len(),
|
||||
percentage
|
||||
);
|
||||
}
|
||||
|
||||
if failed_chunks.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(failed_chunks)
|
||||
}
|
||||
}
|
||||
|
||||
/// Using the list of active mixnode and gateways, determine which of them are eligible for
|
||||
/// rewarding and distribute the rewards.
|
||||
///
|
||||
@@ -471,14 +312,10 @@ impl Rewarder {
|
||||
///
|
||||
/// * `active_monitor_mixnodes`: list of the nodes that were tested at least once by the network monitor
|
||||
/// in the last epoch.
|
||||
///
|
||||
/// * `active_monitor_gateways`: 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,
|
||||
active_monitor_mixnodes: &[MixnodeStatusReport],
|
||||
active_monitor_gateways: &[GatewayStatusReport],
|
||||
) -> Result<(RewardingReport, Option<FailureData>), RewardingError> {
|
||||
let mut failure_data = FailureData::default();
|
||||
|
||||
@@ -489,25 +326,13 @@ impl Rewarder {
|
||||
return Err(RewardingError::NoMixnodesToReward);
|
||||
}
|
||||
|
||||
let eligible_gateways = self
|
||||
.determine_eligible_gateways(active_monitor_gateways)
|
||||
.await?;
|
||||
if eligible_gateways.is_empty() {
|
||||
return Err(RewardingError::NoGatewaysToReward);
|
||||
}
|
||||
|
||||
failure_data.mixnodes = self
|
||||
.distribute_rewards_to_mixnodes(&eligible_mixnodes)
|
||||
.await;
|
||||
|
||||
failure_data.gateways = self
|
||||
.distribute_rewards_to_gateways(&eligible_gateways)
|
||||
.await;
|
||||
|
||||
let report = RewardingReport {
|
||||
epoch_rewarding_id,
|
||||
eligible_mixnodes: eligible_mixnodes.len() as i64,
|
||||
eligible_gateways: eligible_gateways.len() as i64,
|
||||
possibly_unrewarded_mixnodes: failure_data
|
||||
.mixnodes
|
||||
.as_ref()
|
||||
@@ -518,19 +343,9 @@ impl Rewarder {
|
||||
.sum::<usize>() as i64
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
possibly_unrewarded_gateways: failure_data
|
||||
.gateways
|
||||
.as_ref()
|
||||
.map(|chunks| {
|
||||
chunks
|
||||
.iter()
|
||||
.map(|chunk| chunk.possibly_unrewarded.len())
|
||||
.sum::<usize>() as i64
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
|
||||
if failure_data.mixnodes.is_none() && failure_data.gateways.is_none() {
|
||||
if failure_data.mixnodes.is_none() {
|
||||
Ok((report, None))
|
||||
} else {
|
||||
Ok((report, Some(failure_data)))
|
||||
@@ -575,30 +390,6 @@ impl Rewarder {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(failed_gateway_chunks) = failure_data.gateways {
|
||||
for failed_chunk in failed_gateway_chunks.into_iter() {
|
||||
// save the chunk
|
||||
let chunk_id = self
|
||||
.storage
|
||||
.insert_failed_gateway_reward_chunk(FailedGatewayRewardChunk {
|
||||
epoch_rewarding_id,
|
||||
error_message: failed_chunk.error_message,
|
||||
})
|
||||
.await?;
|
||||
|
||||
// and then all associated nodes
|
||||
for node in failed_chunk.possibly_unrewarded.into_iter() {
|
||||
self.storage
|
||||
.insert_possibly_unrewarded_gateway(PossiblyUnrewardedGateway {
|
||||
chunk_id,
|
||||
identity: node.identity,
|
||||
uptime: node.uptime.u8(),
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -731,11 +522,7 @@ impl Rewarder {
|
||||
.await?;
|
||||
|
||||
let (report, failure_data) = self
|
||||
.distribute_rewards(
|
||||
epoch_rewarding_id,
|
||||
&active_monitor_mixnodes,
|
||||
&active_monitor_gateways,
|
||||
)
|
||||
.distribute_rewards(epoch_rewarding_id, &active_monitor_mixnodes)
|
||||
.await?;
|
||||
|
||||
self.storage
|
||||
|
||||
@@ -5,8 +5,8 @@ use crate::network_monitor::monitor::summary_producer::NodeResult;
|
||||
use crate::node_status_api::models::{HistoricalUptime, Uptime};
|
||||
use crate::node_status_api::utils::ActiveNodeDayStatuses;
|
||||
use crate::storage::models::{
|
||||
ActiveNode, EpochRewarding, FailedGatewayRewardChunk, FailedMixnodeRewardChunk, NodeStatus,
|
||||
PossiblyUnrewardedGateway, PossiblyUnrewardedMixnode, RewardingReport,
|
||||
ActiveNode, EpochRewarding, FailedMixnodeRewardChunk, NodeStatus, PossiblyUnrewardedMixnode,
|
||||
RewardingReport,
|
||||
};
|
||||
use crate::storage::UnixTimestamp;
|
||||
use std::convert::TryFrom;
|
||||
@@ -791,17 +791,15 @@ impl StorageManager {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO rewarding_report
|
||||
(epoch_rewarding_id, eligible_mixnodes, eligible_gateways, possibly_unrewarded_mixnodes, possibly_unrewarded_gateways)
|
||||
VALUES (?, ?, ?, ?, ?);
|
||||
(epoch_rewarding_id, eligible_mixnodes, possibly_unrewarded_mixnodes)
|
||||
VALUES (?, ?, ?);
|
||||
"#,
|
||||
report.epoch_rewarding_id,
|
||||
report.eligible_mixnodes,
|
||||
report.eligible_gateways,
|
||||
report.possibly_unrewarded_mixnodes,
|
||||
report.possibly_unrewarded_gateways,
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -826,27 +824,6 @@ impl StorageManager {
|
||||
Ok(res.last_insert_rowid())
|
||||
}
|
||||
|
||||
/// Inserts new failed gateway reward chunk information into the database.
|
||||
/// Returns id of the newly created entry.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `failed_chunk`: chunk information to insert.
|
||||
pub(super) async fn insert_failed_gateway_reward_chunk(
|
||||
&self,
|
||||
failed_chunk: FailedGatewayRewardChunk,
|
||||
) -> Result<i64, sqlx::Error> {
|
||||
let res = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO failed_gateway_reward_chunk (error_message, reward_summary_id) VALUES (?, ?)
|
||||
"#,
|
||||
failed_chunk.error_message,
|
||||
failed_chunk.epoch_rewarding_id,
|
||||
).execute(&self.connection_pool).await?;
|
||||
|
||||
Ok(res.last_insert_rowid())
|
||||
}
|
||||
|
||||
/// Inserts information into the database about a mixnode that might have been unfairly unrewarded this epoch.
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -867,26 +844,6 @@ impl StorageManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Inserts information into the database about a gateway that might have been unfairly unrewarded this epoch.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `gateway`: mixnode information to insert.
|
||||
pub(super) async fn insert_possibly_unrewarded_gateway(
|
||||
&self,
|
||||
gateway: PossiblyUnrewardedGateway,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO possibly_unrewarded_gateway (identity, uptime, failed_gateway_reward_chunk_id) VALUES (?, ?, ?)
|
||||
"#,
|
||||
gateway.identity,
|
||||
gateway.uptime,
|
||||
gateway.chunk_id
|
||||
).execute(&self.connection_pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Obtains all statuses of active mixnodes from the specified time interval.
|
||||
///
|
||||
/// # Arguments
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::node_status_api::models::{
|
||||
use crate::node_status_api::{ONE_DAY, ONE_HOUR};
|
||||
use crate::storage::manager::StorageManager;
|
||||
use crate::storage::models::{
|
||||
EpochRewarding, FailedGatewayRewardChunk, FailedMixnodeRewardChunk, NodeStatus,
|
||||
PossiblyUnrewardedGateway, PossiblyUnrewardedMixnode, RewardingReport,
|
||||
EpochRewarding, FailedMixnodeRewardChunk, NodeStatus, PossiblyUnrewardedMixnode,
|
||||
RewardingReport,
|
||||
};
|
||||
use rocket::fairing::{self, AdHoc};
|
||||
use rocket::{Build, Rocket};
|
||||
@@ -648,22 +648,6 @@ impl NodeStatusStorage {
|
||||
.map_err(|_| NodeStatusApiError::InternalDatabaseError)
|
||||
}
|
||||
|
||||
/// Inserts new failed gateway reward chunk information into the database.
|
||||
/// Returns id of the newly created entry.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `failed_chunk`: chunk information to insert.
|
||||
pub(crate) async fn insert_failed_gateway_reward_chunk(
|
||||
&self,
|
||||
failed_chunk: FailedGatewayRewardChunk,
|
||||
) -> Result<i64, NodeStatusApiError> {
|
||||
self.manager
|
||||
.insert_failed_gateway_reward_chunk(failed_chunk)
|
||||
.await
|
||||
.map_err(|_| NodeStatusApiError::InternalDatabaseError)
|
||||
}
|
||||
|
||||
/// Inserts information into the database about a mixnode that might have been unfairly unrewarded this epoch.
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -678,19 +662,4 @@ impl NodeStatusStorage {
|
||||
.await
|
||||
.map_err(|_| NodeStatusApiError::InternalDatabaseError)
|
||||
}
|
||||
|
||||
/// Inserts information into the database about a gateway that might have been unfairly unrewarded this epoch.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `gateway`: mixnode information to insert.
|
||||
pub(crate) async fn insert_possibly_unrewarded_gateway(
|
||||
&self,
|
||||
gateway: PossiblyUnrewardedGateway,
|
||||
) -> Result<(), NodeStatusApiError> {
|
||||
self.manager
|
||||
.insert_possibly_unrewarded_gateway(gateway)
|
||||
.await
|
||||
.map_err(|_| NodeStatusApiError::InternalDatabaseError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,8 @@ pub(crate) struct RewardingReport {
|
||||
pub(crate) epoch_rewarding_id: i64,
|
||||
|
||||
pub(crate) eligible_mixnodes: i64,
|
||||
pub(crate) eligible_gateways: i64,
|
||||
|
||||
pub(crate) possibly_unrewarded_mixnodes: i64,
|
||||
pub(crate) possibly_unrewarded_gateways: i64,
|
||||
}
|
||||
|
||||
pub(crate) struct FailedMixnodeRewardChunk {
|
||||
@@ -47,16 +45,3 @@ pub(crate) struct PossiblyUnrewardedMixnode {
|
||||
pub(crate) identity: String,
|
||||
pub(crate) uptime: u8,
|
||||
}
|
||||
|
||||
pub(crate) struct FailedGatewayRewardChunk {
|
||||
// references particular epoch_rewarding (there can be multiple chunks in a rewarding epoch)
|
||||
pub(crate) epoch_rewarding_id: i64,
|
||||
pub(crate) error_message: String,
|
||||
}
|
||||
|
||||
pub(crate) struct PossiblyUnrewardedGateway {
|
||||
// references particular FailedGatewayRewardChunk (there can be multiple nodes in a chunk)
|
||||
pub(crate) chunk_id: i64,
|
||||
pub(crate) identity: String,
|
||||
pub(crate) uptime: u8,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user