Feature/total delegation field (#660)

* Changed bond Vec<Coin> into a Coin

* Introduced total_delegation field to bond models

* Restoring total old delegation

* Updating total delegation on new delegations

* Updating total delegation on removal

* Keeping track of delegation increase during rewarding

* Updating total delegation during rewarding + additional response attributes

* Removed irrelevant tests

* Fixed storage-related tests

* Added additional test assertions for delegation increase

* Added additional node rewarding test assertions

* Tests for correct reward calculation + gateway rewarding early termination

* Added delegation field to parsed node type

* Updated typescript bond types

* Moved `OLD_DELEGATIONS_CHUNK_SIZE` to file-wide namespace so that it could be used in tests

* Tests for finding old node delegations

* ibid.

* Issue#657

* Additional test assertions regardingn total delegation

* Missed test field after merge

* ibid

* Cleaning up storage related imports
This commit is contained in:
Jędrzej Stuczyński
2021-06-29 11:29:51 +01:00
committed by GitHub
parent 31f567f1ef
commit 2bdee705b7
19 changed files with 796 additions and 353 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
import { MixNodeBond } from "../types";
import { INetClient } from "../net-client"
import {IQueryClient} from "../query-client";
import {PagedResponse} from "../index";
import {PagedMixnodeResponse} from "../index";
export { MixnodesCache };
@@ -27,7 +27,7 @@ export default class MixnodesCache {
// returns true.
async refreshMixNodes(contractAddress: string): Promise<MixNodeBond[]> {
let newMixnodes: MixNodeBond[] = [];
let response: PagedResponse;
let response: PagedMixnodeResponse;
let next: string | undefined = undefined;
for (;;) {
response = await this.client.getMixNodes(contractAddress, this.perPage, next);
+1 -1
View File
@@ -598,7 +598,7 @@ export default class ValidatorClient {
/// which has just been deleted from the database.
///
/// TODO: more robust error handling on the "deleted key" case.
export type PagedResponse = {
export type PagedMixnodeResponse = {
nodes: MixNodeBond[],
per_page: number, // TODO: camelCase
start_next_after: string, // TODO: camelCase
+3 -3
View File
@@ -4,7 +4,7 @@ import {
GatewayOwnershipResponse,
MixOwnershipResponse, PagedGatewayDelegationsResponse,
PagedGatewayResponse, PagedMixDelegationsResponse,
PagedResponse,
PagedMixnodeResponse,
StateParams
} from "./index";
import { DirectSecp256k1HdWallet, EncodeObject } from "@cosmjs/proto-signing";
@@ -17,7 +17,7 @@ export interface INetClient {
clientAddress: string;
getBalance(address: string, stakeDenom: string): Promise<Coin | null>;
getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedResponse>;
getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedMixnodeResponse>;
getGateways(contractAddress: string, limit: number, start_after?: string): Promise<PagedGatewayResponse>;
getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse>
getMixDelegation(contractAddress: string, mixIdentity: string, delegatorAddress: string): Promise<Delegation>
@@ -71,7 +71,7 @@ export default class NetClient implements INetClient {
this.cosmClient = await SigningCosmWasmClient.connectWithSigner(url, this.wallet, this.signerOptions);
}
public getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedResponse> {
public getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedMixnodeResponse> {
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_nodes: { limit } });
} else {
+3 -3
View File
@@ -5,13 +5,13 @@ import {
GatewayOwnershipResponse,
MixOwnershipResponse, PagedGatewayDelegationsResponse,
PagedGatewayResponse, PagedMixDelegationsResponse,
PagedResponse,
PagedMixnodeResponse,
StateParams
} from "./index";
export interface IQueryClient {
getBalance(address: string, stakeDenom: string): Promise<Coin | null>;
getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedResponse>;
getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedMixnodeResponse>;
getGateways(contractAddress: string, limit: number, start_after?: string): Promise<PagedGatewayResponse>;
getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse>
getMixDelegation(contractAddress: string, mixIdentity: string, delegatorAddress: string): Promise<Delegation>
@@ -47,7 +47,7 @@ export default class QueryClient implements IQueryClient {
this.cosmClient = await CosmWasmClient.connect(url)
}
public getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedResponse> {
public getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedMixnodeResponse> {
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_nodes: { limit } });
} else {
+5 -2
View File
@@ -4,7 +4,8 @@ export type MixNodeBond = { // TODO: change name to MixNodeBond
owner: string,
mix_node: MixNode, // TODO: camelCase this later once everything else works
amount: Coin[],
bond_amount: Coin,
total_delegation: Coin,
}
export type MixNode = {
@@ -22,7 +23,9 @@ export type MixNode = {
export type GatewayBond = {
owner: string
gateway: Gateway,
amount: Coin[],
bond_amount: Coin,
total_delegation: Coin,
}
export type Gateway = {
+1 -1
View File
@@ -1,5 +1,5 @@
import { coins } from "@cosmjs/launchpad";
import {PagedGatewayResponse, PagedResponse} from "../src/net-client";
import {PagedGatewayResponse, PagedMixnodeResponse} from "../src/net-client";
import {GatewayBond, MixNodeBond} from "../src/types"
export namespace Fixtures {
@@ -4,7 +4,8 @@
pub use crate::error::ValidatorClientError;
use crate::models::{QueryRequest, QueryResponse};
use mixnet_contract::{
GatewayBond, IdentityKey, LayerDistribution, MixNodeBond, PagedGatewayResponse, PagedResponse,
GatewayBond, IdentityKey, LayerDistribution, MixNodeBond, PagedGatewayResponse,
PagedMixnodeResponse,
};
use rand::seq::SliceRandom;
use rand::thread_rng;
@@ -155,7 +156,7 @@ impl Client {
async fn get_mix_nodes_paged(
&mut self,
start_after: Option<IdentityKey>,
) -> Result<PagedResponse, ValidatorClientError> {
) -> Result<PagedMixnodeResponse, ValidatorClientError> {
let query_content_json = serde_json::to_string(&QueryRequest::GetMixNodes {
limit: self.config.mixnode_page_limit,
start_after,
+13 -15
View File
@@ -2,7 +2,7 @@
#![allow(clippy::field_reassign_with_default)]
use crate::{IdentityKey, SphinxKey};
use cosmwasm_std::{Addr, Coin};
use cosmwasm_std::{coin, Addr, Coin};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
@@ -21,15 +21,17 @@ pub struct Gateway {
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct GatewayBond {
pub amount: Vec<Coin>,
pub bond_amount: Coin,
pub total_delegation: Coin,
pub owner: Addr,
pub gateway: Gateway,
}
impl GatewayBond {
pub fn new(amount: Vec<Coin>, owner: Addr, gateway: Gateway) -> Self {
pub fn new(bond_amount: Coin, owner: Addr, gateway: Gateway) -> Self {
GatewayBond {
amount,
total_delegation: coin(0, &bond_amount.denom),
bond_amount,
owner,
gateway,
}
@@ -39,8 +41,8 @@ impl GatewayBond {
&self.gateway.identity_key
}
pub fn amount(&self) -> &[Coin] {
&self.amount
pub fn bond_amount(&self) -> Coin {
self.bond_amount.clone()
}
pub fn owner(&self) -> &Addr {
@@ -54,15 +56,11 @@ impl GatewayBond {
impl Display for GatewayBond {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.amount.len() != 1 {
write!(f, "amount: {:?}, owner: {}", self.amount, self.owner)
} else {
write!(
f,
"amount: {} {}, owner: {}",
self.amount[0].amount, self.amount[0].denom, self.owner
)
}
write!(
f,
"amount: {} {}, owner: {}, identity: {}",
self.bond_amount.amount, self.bond_amount.denom, self.owner, self.gateway.identity_key
)
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ mod mixnode;
pub use cosmwasm_std::{Addr, Coin};
pub use delegation::{Delegation, PagedGatewayDelegationsResponse, PagedMixDelegationsResponse};
pub use gateway::{Gateway, GatewayBond, GatewayOwnershipResponse, PagedGatewayResponse};
pub use mixnode::{MixNode, MixNodeBond, MixOwnershipResponse, PagedResponse};
pub use mixnode::{MixNode, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse};
#[derive(Debug, Default, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]
pub struct LayerDistribution {
+16 -34
View File
@@ -2,7 +2,7 @@
#![allow(clippy::field_reassign_with_default)]
use crate::{IdentityKey, SphinxKey};
use cosmwasm_std::{Addr, Coin};
use cosmwasm_std::{coin, Addr, Coin};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
@@ -22,27 +22,17 @@ pub struct MixNode {
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct MixNodeBond {
// TODO:
// JS: When we go onto the next testnet and we have to make incompatible changes in the contract (such as upgrade to cosmwasm 0.14+)
// I'd change `amount` from `Vec<Coin>` to just `Coin` (or maybe even `Uint128` since denomination is implicit)
// I would also put here field like `total_delegation` which would also be a `Coin` or `Uint128` that
// indicates the sum of all delegations towards this node
//
// I would also modify the `MixNode` struct:
// - remove `location` field
// - introduce `rest_api_port` field
// - [POTENTIALLY] introduce `verloc_port` field or keep it accessible via http api
//
// I would also introduce the identical changes to GatewayBond
pub amount: Vec<Coin>,
pub bond_amount: Coin,
pub total_delegation: Coin,
pub owner: Addr,
pub mix_node: MixNode,
}
impl MixNodeBond {
pub fn new(amount: Vec<Coin>, owner: Addr, mix_node: MixNode) -> Self {
pub fn new(bond_amount: Coin, owner: Addr, mix_node: MixNode) -> Self {
MixNodeBond {
amount,
total_delegation: coin(0, &bond_amount.denom),
bond_amount,
owner,
mix_node,
}
@@ -52,8 +42,8 @@ impl MixNodeBond {
&self.mix_node.identity_key
}
pub fn amount(&self) -> &[Coin] {
&self.amount
pub fn bond_amount(&self) -> Coin {
self.bond_amount.clone()
}
pub fn owner(&self) -> &Addr {
@@ -67,36 +57,28 @@ impl MixNodeBond {
impl Display for MixNodeBond {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
// Write strictly the first element into the supplied output
// stream: `f`. Returns `fmt::Result` which indicates whether the
// operation succeeded or failed. Note that `write!` uses syntax which
// is very similar to `println!`.
if self.amount.len() != 1 {
write!(f, "amount: {:?}, owner: {}", self.amount, self.owner)
} else {
write!(
f,
"amount: {} {}, owner: {}",
self.amount[0].amount, self.amount[0].denom, self.owner
)
}
write!(
f,
"amount: {} {}, owner: {}, identity: {}",
self.bond_amount.amount, self.bond_amount.denom, self.owner, self.mix_node.identity_key
)
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedResponse {
pub struct PagedMixnodeResponse {
pub nodes: Vec<MixNodeBond>,
pub per_page: usize,
pub start_next_after: Option<IdentityKey>,
}
impl PagedResponse {
impl PagedMixnodeResponse {
pub fn new(
nodes: Vec<MixNodeBond>,
per_page: usize,
start_next_after: Option<IdentityKey>,
) -> Self {
PagedResponse {
PagedMixnodeResponse {
nodes,
per_page,
start_next_after,
+4
View File
@@ -216,6 +216,7 @@ mod message_receiver {
vec![mix::Node {
owner: "foomp1".to_string(),
stake: 123,
delegation: 456,
host: "10.20.30.40".parse().unwrap(),
mix_host: "10.20.30.40:1789".parse().unwrap(),
identity_key: identity::PublicKey::from_base58_string(
@@ -236,6 +237,7 @@ mod message_receiver {
vec![mix::Node {
owner: "foomp2".to_string(),
stake: 123,
delegation: 456,
host: "11.21.31.41".parse().unwrap(),
mix_host: "11.21.31.41:1789".parse().unwrap(),
identity_key: identity::PublicKey::from_base58_string(
@@ -256,6 +258,7 @@ mod message_receiver {
vec![mix::Node {
owner: "foomp3".to_string(),
stake: 123,
delegation: 456,
host: "12.22.32.42".parse().unwrap(),
mix_host: "12.22.32.42:1789".parse().unwrap(),
identity_key: identity::PublicKey::from_base58_string(
@@ -277,6 +280,7 @@ 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(),
+3 -9
View File
@@ -83,6 +83,7 @@ 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
@@ -124,10 +125,6 @@ impl<'a> TryFrom<&'a GatewayBond> for Node {
type Error = GatewayConversionError;
fn try_from(bond: &'a GatewayBond) -> Result<Self, Self::Error> {
if bond.amount.len() > 1 {
return Err(GatewayConversionError::InvalidStake);
}
let host: NetworkAddress = bond.gateway.host.parse().map_err(|err| {
GatewayConversionError::InvalidAddress(bond.gateway.host.clone(), err)
})?;
@@ -140,11 +137,8 @@ impl<'a> TryFrom<&'a GatewayBond> for Node {
Ok(Node {
owner: bond.owner.as_str().to_owned(),
stake: bond
.amount
.first()
.map(|stake| stake.amount.into())
.unwrap_or(0),
stake: bond.bond_amount.amount.into(),
delegation: bond.total_delegation.amount.into(),
location: bond.gateway.location.clone(),
host,
mix_host,
+1
View File
@@ -289,6 +289,7 @@ mod converting_mixes_to_vec {
let node1 = mix::Node {
owner: "N/A".to_string(),
stake: 0,
delegation: 0,
host: "3.3.3.3".parse().unwrap(),
mix_host: "3.3.3.3:1789".parse().unwrap(),
identity_key: identity::PublicKey::from_base58_string(
+3 -9
View File
@@ -83,6 +83,7 @@ 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 host: NetworkAddress,
// we're keeping this as separate resolved field since we do not want to be resolving the potential
// hostname every time we want to construct a path via this node
@@ -113,10 +114,6 @@ impl<'a> TryFrom<&'a MixNodeBond> for Node {
type Error = MixnodeConversionError;
fn try_from(bond: &'a MixNodeBond) -> Result<Self, Self::Error> {
if bond.amount.len() > 1 {
return Err(MixnodeConversionError::InvalidStake);
}
let host: NetworkAddress = bond.mix_node.host.parse().map_err(|err| {
MixnodeConversionError::InvalidAddress(bond.mix_node.host.clone(), err)
})?;
@@ -131,11 +128,8 @@ impl<'a> TryFrom<&'a MixNodeBond> for Node {
Ok(Node {
owner: bond.owner.as_str().to_owned(),
stake: bond
.amount
.first()
.map(|stake| stake.amount.into())
.unwrap_or(0),
stake: bond.bond_amount.amount.into(),
delegation: bond.total_delegation.amount.into(),
host,
mix_host,
identity_key: identity::PublicKey::from_base58_string(&bond.mix_node.identity_key)?,
+2 -2
View File
@@ -178,7 +178,7 @@ pub mod tests {
use crate::support::tests::helpers::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, from_binary};
use mixnet_contract::PagedResponse;
use mixnet_contract::PagedMixnodeResponse;
#[test]
fn initialize_contract() {
@@ -200,7 +200,7 @@ pub mod tests {
},
)
.unwrap();
let page: PagedResponse = from_binary(&res).unwrap();
let page: PagedMixnodeResponse = from_binary(&res).unwrap();
assert_eq!(0, page.nodes.len()); // there are no mixnodes in the list when it's just been initialized
// Contract balance should match what we initialized it as
+3 -3
View File
@@ -12,7 +12,7 @@ use cosmwasm_std::{coin, Addr};
use mixnet_contract::{
Delegation, GatewayBond, GatewayOwnershipResponse, IdentityKey, LayerDistribution, MixNodeBond,
MixOwnershipResponse, PagedGatewayDelegationsResponse, PagedGatewayResponse,
PagedMixDelegationsResponse, PagedResponse,
PagedMixDelegationsResponse, PagedMixnodeResponse,
};
const BOND_PAGE_MAX_LIMIT: u32 = 100;
@@ -26,7 +26,7 @@ pub fn query_mixnodes_paged(
deps: Deps,
start_after: Option<IdentityKey>,
limit: Option<u32>,
) -> StdResult<PagedResponse> {
) -> StdResult<PagedMixnodeResponse> {
let limit = limit
.unwrap_or(BOND_PAGE_DEFAULT_LIMIT)
.min(BOND_PAGE_MAX_LIMIT) as usize;
@@ -40,7 +40,7 @@ pub fn query_mixnodes_paged(
let start_next_after = nodes.last().map(|node| node.identity().clone());
Ok(PagedResponse::new(nodes, limit, start_next_after))
Ok(PagedMixnodeResponse::new(nodes, limit, start_next_after))
}
pub(crate) fn query_gateways_paged(
+85 -132
View File
@@ -1,6 +1,6 @@
use crate::queries;
use crate::state::{State, StateParams};
use cosmwasm_std::{Decimal, Order, StdError, StdResult, Storage, Uint128};
use cosmwasm_std::{Decimal, Order, StdResult, Storage, Uint128};
use cosmwasm_storage::{
bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton,
Singleton,
@@ -162,33 +162,18 @@ pub fn mixnodes_owners_read(storage: &dyn Storage) -> ReadonlyBucket<IdentityKey
}
// helpers
pub(crate) fn increase_mixnode_bond(
storage: &mut dyn Storage,
mut bond: MixNodeBond,
scaled_reward_rate: Decimal,
) -> StdResult<()> {
if bond.amount.len() != 1 {
return Err(StdError::generic_err(
"mixnode seems to have been bonded with multiple coin types",
));
}
let reward = bond.amount[0].amount * scaled_reward_rate;
bond.amount[0].amount += reward;
mixnodes(storage).save(bond.identity().as_bytes(), &bond)
}
pub(crate) fn increase_mix_delegated_stakes(
storage: &mut dyn Storage,
mix_identity: IdentityKey,
mix_identity: IdentityKeyRef,
scaled_reward_rate: Decimal,
) -> StdResult<()> {
) -> 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 = mix_delegations_read(storage, &mix_identity)
let delegations_chunk = mix_delegations_read(storage, mix_identity)
.range(chunk_start.as_deref(), None, Order::Ascending)
.take(chunk_size)
.collect::<StdResult<Vec<_>>>()?;
@@ -197,7 +182,7 @@ pub(crate) fn increase_mix_delegated_stakes(
break;
}
// append 0 byte to the last value to start with whatever is the next suceeding key
// append 0 byte to the last value to start with whatever is the next succeeding key
chunk_start = Some(
delegations_chunk
.last()
@@ -213,24 +198,26 @@ pub(crate) fn increase_mix_delegated_stakes(
for (delegator_address, amount) in delegations_chunk.into_iter() {
let reward = amount * scaled_reward_rate;
let new_amount = amount + reward;
mix_delegations(storage, &mix_identity).save(&delegator_address, &new_amount)?;
total_rewarded += reward;
mix_delegations(storage, mix_identity).save(&delegator_address, &new_amount)?;
}
}
Ok(())
Ok(total_rewarded)
}
pub(crate) fn increase_gateway_delegated_stakes(
storage: &mut dyn Storage,
gateway_identity: IdentityKey,
gateway_identity: IdentityKeyRef,
scaled_reward_rate: Decimal,
) -> StdResult<()> {
) -> 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)
let delegations_chunk = gateway_delegations_read(storage, gateway_identity)
.range(chunk_start.as_deref(), None, Order::Ascending)
.take(chunk_size)
.collect::<StdResult<Vec<_>>>()?;
@@ -255,12 +242,12 @@ pub(crate) fn increase_gateway_delegated_stakes(
for (delegator_address, amount) in delegations_chunk.into_iter() {
let reward = amount * scaled_reward_rate;
let new_amount = amount + reward;
gateway_delegations(storage, &gateway_identity)
.save(&delegator_address, &new_amount)?;
total_rewarded += reward;
gateway_delegations(storage, gateway_identity).save(&delegator_address, &new_amount)?;
}
}
Ok(())
Ok(total_rewarded)
}
// currently not used outside tests
@@ -271,12 +258,7 @@ pub(crate) fn read_mixnode_bond(
) -> StdResult<cosmwasm_std::Uint128> {
let bucket = mixnodes_read(storage);
let node = bucket.load(identity)?;
if node.amount.len() != 1 {
return Err(StdError::generic_err(
"mixnode seems to have been bonded with multiple coin types",
));
}
Ok(node.amount[0].amount)
Ok(node.bond_amount.amount)
}
// Gateway-related stuff
@@ -298,22 +280,6 @@ pub fn gateways_owners_read(storage: &dyn Storage) -> ReadonlyBucket<IdentityKey
bucket_read(storage, PREFIX_GATEWAYS_OWNERS)
}
// helpers
pub(crate) fn increase_gateway_bond(
storage: &mut dyn Storage,
mut bond: GatewayBond,
scaled_reward_rate: Decimal,
) -> StdResult<()> {
if bond.amount.len() != 1 {
return Err(StdError::generic_err(
"gateway seems to have been bonded with multiple coin types",
));
}
let reward = bond.amount[0].amount * scaled_reward_rate;
bond.amount[0].amount += reward;
gateways(storage).save(bond.identity().as_bytes(), &bond)
}
// delegation related
pub fn mix_delegations<'a>(
storage: &'a mut dyn Storage,
@@ -357,12 +323,7 @@ pub(crate) fn read_gateway_bond(
) -> StdResult<cosmwasm_std::Uint128> {
let bucket = gateways_read(storage);
let node = bucket.load(identity)?;
if node.amount.len() != 1 {
return Err(StdError::generic_err(
"gateway seems to have been bonded with multiple coin types",
));
}
Ok(node.amount[0].amount)
Ok(node.bond_amount.amount)
}
#[cfg(test)]
@@ -373,7 +334,7 @@ mod tests {
gateway_bond_fixture, gateway_fixture, mix_node_fixture, mixnode_bond_fixture,
};
use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::{coins, Addr, Uint128};
use cosmwasm_std::{coin, Addr, Uint128};
use mixnet_contract::{Gateway, MixNode};
#[test]
@@ -404,34 +365,6 @@ mod tests {
assert_eq!(bond2, res2);
}
#[test]
fn increasing_mixnode_bond() {
let mut storage = MockStorage::new();
let node_owner: Addr = Addr::unchecked("node-owner");
let node_identity: IdentityKey = "nodeidentity".into();
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
// increases the reward appropriately
let mixnode_bond = MixNodeBond {
amount: coins(1000, DENOM),
owner: node_owner.clone(),
mix_node: MixNode {
identity_key: node_identity.clone(),
..mix_node_fixture()
},
};
mixnodes(&mut storage)
.save(node_identity.as_bytes(), &mixnode_bond)
.unwrap();
increase_mixnode_bond(&mut storage, mixnode_bond, reward).unwrap();
let new_bond = read_mixnode_bond(&storage, node_identity.as_bytes()).unwrap();
assert_eq!(Uint128(1001), new_bond);
}
#[test]
fn reading_mixnode_bond() {
let mut storage = MockStorage::new();
@@ -446,7 +379,8 @@ mod tests {
let bond_value = 1000;
let mixnode_bond = MixNodeBond {
amount: coins(bond_value, DENOM),
bond_amount: coin(bond_value, DENOM),
total_delegation: coin(0, DENOM),
owner: node_owner.clone(),
mix_node: MixNode {
identity_key: node_identity.clone(),
@@ -464,34 +398,6 @@ mod tests {
);
}
#[test]
fn increasing_gateway_bond() {
let mut storage = MockStorage::new();
let node_owner: Addr = Addr::unchecked("node-owner");
let node_identity: IdentityKey = "nodeidentity".into();
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
// increases the reward appropriately
let gateway_bond = GatewayBond {
amount: coins(1000, DENOM),
owner: node_owner.clone(),
gateway: Gateway {
identity_key: node_identity.clone(),
..gateway_fixture()
},
};
gateways(&mut storage)
.save(node_identity.as_bytes(), &gateway_bond)
.unwrap();
increase_gateway_bond(&mut storage, gateway_bond, reward).unwrap();
let new_bond = read_gateway_bond(&storage, node_identity.as_bytes()).unwrap();
assert_eq!(Uint128(1001), new_bond);
}
#[test]
fn reading_gateway_bond() {
let mut storage = MockStorage::new();
@@ -506,7 +412,8 @@ mod tests {
let bond_value = 1000;
let gateway_bond = GatewayBond {
amount: coins(bond_value, DENOM),
bond_amount: coin(bond_value, DENOM),
total_delegation: coin(0, DENOM),
owner: node_owner.clone(),
gateway: Gateway {
identity_key: node_identity.clone(),
@@ -538,8 +445,12 @@ mod tests {
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
increase_mix_delegated_stakes(&mut deps.storage, node_identity.clone(), reward)
.unwrap();
let total_increase =
increase_mix_delegated_stakes(&mut deps.storage, node_identity.as_ref(), reward)
.unwrap();
// there was no increase
assert!(total_increase.is_zero());
// there are no 'new' delegations magically added
assert!(
@@ -563,8 +474,12 @@ mod tests {
.save(delegator_address.as_bytes(), &Uint128(1000))
.unwrap();
increase_mix_delegated_stakes(&mut deps.storage, node_identity.clone(), reward)
.unwrap();
let total_increase =
increase_mix_delegated_stakes(&mut deps.storage, node_identity.as_ref(), reward)
.unwrap();
assert_eq!(Uint128(1), total_increase);
assert_eq!(
Uint128(1001),
mix_delegations_read(&mut deps.storage, &node_identity)
@@ -588,8 +503,11 @@ mod tests {
.unwrap();
}
increase_mix_delegated_stakes(&mut deps.storage, node_identity.clone(), reward)
.unwrap();
let total_increase =
increase_mix_delegated_stakes(&mut deps.storage, node_identity.as_ref(), reward)
.unwrap();
assert_eq!(Uint128(100), total_increase);
for i in 0..100 {
let delegator_address = Addr::unchecked(format!("address{}", i));
@@ -617,8 +535,14 @@ mod tests {
.unwrap();
}
increase_mix_delegated_stakes(&mut deps.storage, node_identity.clone(), reward)
.unwrap();
let total_increase =
increase_mix_delegated_stakes(&mut deps.storage, node_identity.as_ref(), reward)
.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));
@@ -646,8 +570,15 @@ mod tests {
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
increase_gateway_delegated_stakes(&mut deps.storage, node_identity.clone(), reward)
.unwrap();
let total_increase = increase_gateway_delegated_stakes(
&mut deps.storage,
node_identity.as_ref(),
reward,
)
.unwrap();
// there was no increase
assert!(total_increase.is_zero());
// there are no 'new' delegations magically added
assert!(
@@ -671,8 +602,15 @@ mod tests {
.save(delegator_address.as_bytes(), &Uint128(1000))
.unwrap();
increase_gateway_delegated_stakes(&mut deps.storage, node_identity.clone(), reward)
.unwrap();
let total_increase = increase_gateway_delegated_stakes(
&mut deps.storage,
node_identity.as_ref(),
reward,
)
.unwrap();
assert_eq!(Uint128(1), total_increase);
assert_eq!(
Uint128(1001),
gateway_delegations_read(&mut deps.storage, &node_identity)
@@ -696,8 +634,14 @@ mod tests {
.unwrap();
}
increase_gateway_delegated_stakes(&mut deps.storage, node_identity.clone(), reward)
.unwrap();
let total_increase = increase_gateway_delegated_stakes(
&mut deps.storage,
node_identity.as_ref(),
reward,
)
.unwrap();
assert_eq!(Uint128(100), total_increase);
for i in 0..100 {
let delegator_address = Addr::unchecked(format!("address{}", i));
@@ -725,8 +669,17 @@ mod tests {
.unwrap();
}
increase_gateway_delegated_stakes(&mut deps.storage, node_identity.clone(), reward)
.unwrap();
let total_increase = increase_gateway_delegated_stakes(
&mut deps.storage,
node_identity.as_ref(),
reward,
)
.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));
+5 -5
View File
@@ -7,7 +7,7 @@ pub mod helpers {
use crate::msg::InstantiateMsg;
use crate::msg::QueryMsg;
use crate::transactions::{try_add_gateway, try_add_mixnode};
use cosmwasm_std::coins;
use cosmwasm_std::coin;
use cosmwasm_std::from_binary;
use cosmwasm_std::testing::mock_dependencies;
use cosmwasm_std::testing::mock_env;
@@ -20,7 +20,7 @@ pub mod helpers {
use cosmwasm_std::OwnedDeps;
use cosmwasm_std::{Empty, MemoryStorage};
use mixnet_contract::{
Gateway, GatewayBond, MixNode, MixNodeBond, PagedGatewayResponse, PagedResponse,
Gateway, GatewayBond, MixNode, MixNodeBond, PagedGatewayResponse, PagedMixnodeResponse,
};
pub fn add_mixnode(
@@ -55,7 +55,7 @@ pub mod helpers {
)
.unwrap();
let page: PagedResponse = from_binary(&result).unwrap();
let page: PagedMixnodeResponse = from_binary(&result).unwrap();
page.nodes
}
@@ -128,7 +128,7 @@ pub mod helpers {
identity_key: "aaaa".to_string(),
version: "0.10.0".to_string(),
};
MixNodeBond::new(coins(50, DENOM), Addr::unchecked("foo"), mix_node)
MixNodeBond::new(coin(50, DENOM), Addr::unchecked("foo"), mix_node)
}
pub fn gateway_fixture() -> Gateway {
@@ -154,7 +154,7 @@ pub mod helpers {
identity_key: "identity".to_string(),
version: "0.10.0".to_string(),
};
GatewayBond::new(coins(50, DENOM), Addr::unchecked("foo"), gateway)
GatewayBond::new(coin(50, DENOM), Addr::unchecked("foo"), gateway)
}
pub fn query_contract_balance(
File diff suppressed because it is too large Load Diff