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:
committed by
GitHub
parent
31f567f1ef
commit
2bdee705b7
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)?,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user