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:
Jędrzej Stuczyński
2021-11-03 15:01:59 +00:00
committed by GitHub
parent ef88ce9252
commit b19528d47e
32 changed files with 73 additions and 3520 deletions
@@ -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;
+1 -1
View File
@@ -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,
-42
View File
@@ -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>>,
+11 -43
View File
@@ -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
+1 -2
View File
@@ -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};
-32
View File
@@ -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 {},
}
+1 -18
View File
@@ -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
)
}
}
-1
View File
@@ -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(),
-2
View File
@@ -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,
-56
View File
@@ -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?)
-12
View File
@@ -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,
},
}
+1 -66
View File
@@ -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()),
);
}
}
+6 -636
View File
@@ -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());
}
}
}
-2
View File
@@ -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
}
-435
View File
@@ -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
-3
View File
@@ -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;
+1 -66
View File
@@ -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(),
}
}
}
-7
View File
@@ -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))
}
+1 -97
View File
@@ -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())
}
}
}
}
}
-3
View File
@@ -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),
+3 -216
View File
@@ -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
+6 -49
View File
@@ -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
+2 -33
View File
@@ -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)
}
}
-15
View File
@@ -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,
}