Feature/reverse contract mapping (#640)

* WIP

* One fewer todos

* Migrated transactions,queries + query tests

* Transaction-related tests restored

* Import cleanup

* Initial draft of the migration procedure

* Future migration test

* Fixed rust compilation errors

* Unbonding no longer requires explicit identity

* Delegation API changes

* Fixed incorrect gateway delegation object
This commit is contained in:
Jędrzej Stuczyński
2021-06-14 17:55:41 +01:00
committed by GitHub
parent 7b02026476
commit 164c8ad05c
18 changed files with 1332 additions and 1155 deletions
+1 -1
View File
@@ -56,4 +56,4 @@ default-members = [
"network-monitor",
]
exclude = ["explorer"]
exclude = ["explorer", "contracts"]
+29 -29
View File
@@ -282,14 +282,14 @@ export default class ValidatorClient {
/**
* Delegates specified amount of stake to particular mixnode.
*
* @param mixOwner address of the owner of the node to which the delegation should be applied
* @param mixIdentity identity of the node to which the delegation should be applied
* @param amount desired amount of coins to delegate to the node
*/
// requires coin type to ensure correct denomination (
async delegateToMixnode(mixOwner: string, amount: Coin): Promise<ExecuteResult> {
async delegateToMixnode(mixIdentity: string, amount: Coin): Promise<ExecuteResult> {
if (this.client instanceof NetClient) {
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, { delegate_to_mixnode: { mix_owner: mixOwner } }, `delegating to ${mixOwner}`, [amount]).catch((err) => this.handleRequestFailure(err))
console.log(`account ${this.client.clientAddress} delegated ${amount} to mixnode owned by ${mixOwner}`);
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, { delegate_to_mixnode: { mix_identity: mixIdentity } }, `delegating to ${mixIdentity}`, [amount]).catch((err) => this.handleRequestFailure(err))
console.log(`account ${this.client.clientAddress} delegated ${amount} to mixnode ${mixIdentity}`);
return result;
} else {
throw new Error("Tried to delegate stake with a query client")
@@ -299,12 +299,12 @@ export default class ValidatorClient {
/**
* Removes stake delegation from a particular mixnode.
*
* @param mixOwner address of the owner of the node from which the delegation should get removed
* @param mixIdentity identity of the node from which the delegation should get removed
*/
async removeMixnodeDelegation(mixOwner: string): Promise<ExecuteResult> {
async removeMixnodeDelegation(mixIdentity: string): Promise<ExecuteResult> {
if (this.client instanceof NetClient) {
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, { undelegate_from_mixnode: { mix_owner: mixOwner } }).catch((err) => this.handleRequestFailure(err))
console.log(`account ${this.client.clientAddress} removed delegation from mixnode owned by ${mixOwner}`);
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, { undelegate_from_mixnode: { mix_identity: mixIdentity } }).catch((err) => this.handleRequestFailure(err))
console.log(`account ${this.client.clientAddress} removed delegation from mixnode ${mixIdentity}`);
return result;
} else {
throw new Error("Tried to remove stake delegation with a query client")
@@ -314,14 +314,14 @@ export default class ValidatorClient {
/**
* Delegates specified amount of stake to particular gateway.
*
* @param gatewayOwner address of the owner of the node to which the delegation should be applied
* @param gatewayIdentity identity of the gateway to which the delegation should be applied
* @param amount desired amount of coins to delegate to the node
*/
// requires coin type to ensure correct denomination (
async delegateToGateway(gatewayOwner: string, amount: Coin): Promise<ExecuteResult> {
async delegateToGateway(gatewayIdentity: string, amount: Coin): Promise<ExecuteResult> {
if (this.client instanceof NetClient) {
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, { delegate_to_gateway: { gateway_owner: gatewayOwner } }, `delegating to ${gatewayOwner}`, [amount]).catch((err) => this.handleRequestFailure(err))
console.log(`account ${this.client.clientAddress} delegated ${amount} to gateway owned by ${gatewayOwner}`);
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, { delegate_to_gateway: { gateway_identity: gatewayIdentity } }, `delegating to ${gatewayIdentity}`, [amount]).catch((err) => this.handleRequestFailure(err))
console.log(`account ${this.client.clientAddress} delegated ${amount} to gateway ${gatewayIdentity}`);
return result;
} else {
throw new Error("Tried to delegate stake with a query client")
@@ -331,12 +331,12 @@ export default class ValidatorClient {
/**
* Removes stake delegation from a particular gateway.
*
* @param gatewayOwner address of the owner of the node from which the delegation should get removed
* @param gatewayIdentity identity of the gateway from which the delegation should get removed
*/
async removeGatewayDelegation(gatewayOwner: string): Promise<ExecuteResult> {
async removeGatewayDelegation(gatewayIdentity: string): Promise<ExecuteResult> {
if (this.client instanceof NetClient) {
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, { undelegate_from_gateway: { gateway_owner: gatewayOwner } }).catch((err) => this.handleRequestFailure(err))
console.log(`account ${this.client.clientAddress} removed delegation from gateway owned by ${gatewayOwner}`);
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, { undelegate_from_gateway: { gateway_identity: gatewayIdentity } }).catch((err) => this.handleRequestFailure(err))
console.log(`account ${this.client.clientAddress} removed delegation from gateway ${gatewayIdentity}`);
return result;
} else {
throw new Error("Tried to remove stake delegation with a query client")
@@ -436,9 +436,9 @@ export default class ValidatorClient {
/**
* Gets list of all delegations towards particular mixnode.
*
* @param mixOwner address of the owner of the node to which the delegation was sent
* @param mixIdentity identity of the node to which the delegation was sent
*/
public async getMixDelegations(mixOwner: string): Promise<Delegation[]> {
public async getMixDelegations(mixIdentity: string): Promise<Delegation[]> {
// make this configurable somewhere
const limit = 500
@@ -446,7 +446,7 @@ export default class ValidatorClient {
let response: PagedMixDelegationsResponse
let next: string | undefined = undefined;
for (;;) {
response = await this.client.getMixDelegations(this.contractAddress, mixOwner, limit, next)
response = await this.client.getMixDelegations(this.contractAddress, mixIdentity, limit, next)
delegations = delegations.concat(response.delegations)
next = response.start_next_after
// if `start_next_after` is not set, we're done
@@ -461,19 +461,19 @@ export default class ValidatorClient {
/**
* Checks value of delegation of given client towards particular mixnode.
*
* @param mixOwner address of the owner of the node to which the delegation was sent
* @param mixIdentity identity of the node to which the delegation was sent
* @param delegatorAddress address of the client who delegated the stake
*/
public getMixDelegation(mixOwner: string, delegatorAddress: string): Promise<Delegation> {
return this.client.getMixDelegation(this.contractAddress, mixOwner, delegatorAddress);
public getMixDelegation(mixIdentity: string, delegatorAddress: string): Promise<Delegation> {
return this.client.getMixDelegation(this.contractAddress, mixIdentity, delegatorAddress);
}
/**
* Gets list of all delegations towards particular mixnode.
*
* @param gatewayOwner address of the owner of the gateway to which the delegation was sent
* @param gatewayIdentity identity of the gateway to which the delegation was sent
*/
public async getGatewayDelegations(gatewayOwner: string): Promise<Delegation[]> {
public async getGatewayDelegations(gatewayIdentity: string): Promise<Delegation[]> {
// make this configurable somewhere
const limit = 500
@@ -481,7 +481,7 @@ export default class ValidatorClient {
let response: PagedGatewayDelegationsResponse
let next: string | undefined = undefined;
for (;;) {
response = await this.client.getGatewayDelegations(this.contractAddress, gatewayOwner, limit, next)
response = await this.client.getGatewayDelegations(this.contractAddress, gatewayIdentity, limit, next)
delegations = delegations.concat(response.delegations)
next = response.start_next_after
// if `start_next_after` is not set, we're done
@@ -496,11 +496,11 @@ export default class ValidatorClient {
/**
* Checks value of delegation of given client towards particular gateway.
*
* @param gatewayOwner address of the owner of the node to which the delegation was sent
* @param gatewayIdentity identity of the gateway to which the delegation was sent
* @param delegatorAddress address of the client who delegated the stake
*/
public getGatewayDelegation(gatewayOwner: string, delegatorAddress: string): Promise<Delegation> {
return this.client.getGatewayDelegation(this.contractAddress, gatewayOwner, delegatorAddress);
public getGatewayDelegation(gatewayIdentity: string, delegatorAddress: string): Promise<Delegation> {
return this.client.getGatewayDelegation(this.contractAddress, gatewayIdentity, delegatorAddress);
}
// TODO: if we just keep a reference to the SigningCosmWasmClient somewhere we can probably go direct
@@ -637,7 +637,7 @@ export type StateParams = {
}
export type Delegation = {
owner: string,
node_identity: string,
amount: Coin,
}
+14 -14
View File
@@ -19,10 +19,10 @@ export interface INetClient {
getBalance(address: string, stakeDenom: string): Promise<Coin | null>;
getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedResponse>;
getGateways(contractAddress: string, limit: number, start_after?: string): Promise<PagedGatewayResponse>;
getMixDelegations(contractAddress: string, mixOwner: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse>
getMixDelegation(contractAddress: string, mixOwner: string, delegatorAddress: string): Promise<Delegation>
getGatewayDelegations(contractAddress: string, gatewayOwner: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse>
getGatewayDelegation(contractAddress: string, gatewayOwner: string, delegatorAddress: string): Promise<Delegation>
getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse>
getMixDelegation(contractAddress: string, mixIdentity: string, delegatorAddress: string): Promise<Delegation>
getGatewayDelegations(contractAddress: string, gatewayIdentity: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse>
getGatewayDelegation(contractAddress: string, gatewayIdentity: string, delegatorAddress: string): Promise<Delegation>
ownsMixNode(contractAddress: string, address: string): Promise<MixOwnershipResponse>;
ownsGateway(contractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
getStateParams(contractAddress: string): Promise<StateParams>;
@@ -87,28 +87,28 @@ export default class NetClient implements INetClient {
}
}
public getMixDelegations(contractAddress: string, mixOwner: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse> {
public getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse> {
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_delegations: { mix_owner: mixOwner, limit } });
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegations: { mix_identity: mixIdentity, limit } });
} else {
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegations: { mix_owner: mixOwner, limit, start_after } });
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegations: { mix_identity: mixIdentity, limit, start_after } });
}
}
public getMixDelegation(contractAddress: string, mixOwner: string, delegatorAddress: string): Promise<Delegation> {
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegation: { mix_owner: mixOwner, address: delegatorAddress } });
public getMixDelegation(contractAddress: string, mixIdentity: string, delegatorAddress: string): Promise<Delegation> {
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegation: { mix_identity: mixIdentity, address: delegatorAddress } });
}
public getGatewayDelegations(contractAddress: string, gatewayOwner: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse> {
public getGatewayDelegations(contractAddress: string, gatewayIdentity: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse> {
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_gateway_delegations: { gateway_owner: gatewayOwner, limit } });
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegations: { gateway_identity: gatewayIdentity, limit } });
} else {
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegations: { gateway_owner: gatewayOwner, limit, start_after } });
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegations: { gateway_identity: gatewayIdentity, limit, start_after } });
}
}
public getGatewayDelegation(contractAddress: string, gatewayOwner: string, delegatorAddress: string): Promise<Delegation> {
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegation: { gateway_owner: gatewayOwner, address: delegatorAddress } });
public getGatewayDelegation(contractAddress: string, gatewayIdentity: string, delegatorAddress: string): Promise<Delegation> {
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegation: { gateway_identity: gatewayIdentity, address: delegatorAddress } });
}
public ownsMixNode(contractAddress: string, address: string): Promise<MixOwnershipResponse> {
+14 -14
View File
@@ -13,10 +13,10 @@ export interface IQueryClient {
getBalance(address: string, stakeDenom: string): Promise<Coin | null>;
getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedResponse>;
getGateways(contractAddress: string, limit: number, start_after?: string): Promise<PagedGatewayResponse>;
getMixDelegations(contractAddress: string, mixOwner: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse>
getMixDelegation(contractAddress: string, mixOwner: string, delegatorAddress: string): Promise<Delegation>
getGatewayDelegations(contractAddress: string, gatewayOwner: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse>
getGatewayDelegation(contractAddress: string, gatewayOwner: string, delegatorAddress: string): Promise<Delegation>
getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse>
getMixDelegation(contractAddress: string, mixIdentity: string, delegatorAddress: string): Promise<Delegation>
getGatewayDelegations(contractAddress: string, gatewayIdentity: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse>
getGatewayDelegation(contractAddress: string, gatewayIdentity: string, delegatorAddress: string): Promise<Delegation>
ownsMixNode(contractAddress: string, address: string): Promise<MixOwnershipResponse>;
ownsGateway(contractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
getStateParams(contractAddress: string): Promise<StateParams>;
@@ -63,28 +63,28 @@ export default class QueryClient implements IQueryClient {
}
}
public getMixDelegations(contractAddress: string, mixOwner: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse> {
public getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse> {
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_delegations: { mix_owner: mixOwner, limit } });
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegations: { mix_identity: mixIdentity, limit } });
} else {
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegations: { mix_owner: mixOwner, limit, start_after } });
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegations: { mix_identity: mixIdentity, limit, start_after } });
}
}
public getMixDelegation(contractAddress: string, mixOwner: string, delegatorAddress: string): Promise<Delegation> {
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegation: { mix_owner: mixOwner, address: delegatorAddress } });
public getMixDelegation(contractAddress: string, mixIdentity: string, delegatorAddress: string): Promise<Delegation> {
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegation: { mix_identity: mixIdentity, address: delegatorAddress } });
}
public getGatewayDelegations(contractAddress: string, gatewayOwner: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse> {
public getGatewayDelegations(contractAddress: string, gatewayIdentity: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse> {
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_gateway_delegations: { gateway_owner: gatewayOwner, limit } });
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegations: { gateway_identity: gatewayIdentity, limit } });
} else {
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegations: { gateway_owner: gatewayOwner, limit, start_after } });
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegations: { gateway_identity: gatewayIdentity, limit, start_after } });
}
}
public getGatewayDelegation(contractAddress: string, gatewayOwner: string, delegatorAddress: string): Promise<Delegation> {
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegation: { gateway_owner: gatewayOwner, address: delegatorAddress } });
public getGatewayDelegation(contractAddress: string, gatewayIdentity: string, delegatorAddress: string): Promise<Delegation> {
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegation: { gateway_identity: gatewayIdentity, address: delegatorAddress } });
}
public ownsMixNode(contractAddress: string, address: string): Promise<MixOwnershipResponse> {
@@ -5,7 +5,7 @@ use crate::models::{QueryRequest, QueryResponse};
use crate::ValidatorClientError::ValidatorError;
use core::fmt::{self, Display, Formatter};
use mixnet_contract::{
GatewayBond, HumanAddr, LayerDistribution, MixNodeBond, PagedGatewayResponse, PagedResponse,
GatewayBond, LayerDistribution, MixNodeBond, PagedGatewayResponse, PagedResponse,
};
use rand::seq::SliceRandom;
use rand::thread_rng;
@@ -177,7 +177,7 @@ impl Client {
async fn get_mix_nodes_paged(
&mut self,
start_after: Option<HumanAddr>,
start_after: Option<String>,
) -> Result<PagedResponse, ValidatorClientError> {
let query_content_json = serde_json::to_string(&QueryRequest::GetMixNodes {
limit: self.config.mixnode_page_limit,
@@ -210,7 +210,7 @@ impl Client {
async fn get_gateways_paged(
&mut self,
start_after: Option<HumanAddr>,
start_after: Option<String>,
) -> Result<PagedGatewayResponse, ValidatorClientError> {
let query_content_json = serde_json::to_string(&QueryRequest::GetGateways {
limit: self.config.gateway_page_limit,
@@ -3,7 +3,6 @@
use crate::serde_helpers::{de_i64_from_str, de_paged_query_response_from_str};
use core::fmt::{self, Display, Formatter};
use mixnet_contract::HumanAddr;
use serde::{Deserialize, Serialize};
// TODO: this is a duplicate code but it really does not feel
@@ -14,10 +13,10 @@ use serde::{Deserialize, Serialize};
pub(super) enum QueryRequest {
GetMixNodes {
limit: Option<u32>,
start_after: Option<HumanAddr>,
start_after: Option<String>,
},
GetGateways {
start_after: Option<HumanAddr>,
start_after: Option<String>,
limit: Option<u32>,
},
LayerDistribution {},
@@ -130,8 +130,8 @@ impl PublicKey {
bs58::encode(self.to_bytes()).into_string()
}
pub fn from_base58_string<S: Into<String>>(val: S) -> Result<Self, KeyRecoveryError> {
let bytes = bs58::decode(val.into()).into_vec()?;
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, KeyRecoveryError> {
let bytes = bs58::decode(val).into_vec()?;
Self::from_bytes(&bytes)
}
}
@@ -185,8 +185,8 @@ impl PrivateKey {
bs58::encode(&self.to_bytes()).into_string()
}
pub fn from_base58_string<S: Into<String>>(val: S) -> Result<Self, KeyRecoveryError> {
let bytes = bs58::decode(val.into()).into_vec()?;
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, KeyRecoveryError> {
let bytes = bs58::decode(val).into_vec()?;
Self::from_bytes(&bytes)
}
+4 -4
View File
@@ -134,8 +134,8 @@ impl PublicKey {
bs58::encode(self.to_bytes()).into_string()
}
pub fn from_base58_string<S: Into<String>>(val: S) -> Result<Self, KeyRecoveryError> {
let bytes = bs58::decode(val.into()).into_vec()?;
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, KeyRecoveryError> {
let bytes = bs58::decode(val).into_vec()?;
Self::from_bytes(&bytes)
}
@@ -189,8 +189,8 @@ impl PrivateKey {
bs58::encode(&self.to_bytes()).into_string()
}
pub fn from_base58_string<S: Into<String>>(val: S) -> Result<Self, KeyRecoveryError> {
let bytes = bs58::decode(val.into()).into_vec()?;
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, KeyRecoveryError> {
let bytes = bs58::decode(val).into_vec()?;
Self::from_bytes(&bytes)
}
+21 -18
View File
@@ -1,28 +1,31 @@
// due to code generated by JsonSchema
#![allow(clippy::field_reassign_with_default)]
use cosmwasm_std::{Coin, HumanAddr};
use cosmwasm_std::Coin;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct Delegation {
owner: HumanAddr,
node_identity: String,
amount: Coin,
}
impl Delegation {
pub fn new(owner: HumanAddr, amount: Coin) -> Self {
Delegation { owner, amount }
pub fn new(node_identity: String, amount: Coin) -> Self {
Delegation {
node_identity,
amount,
}
}
pub fn amount(&self) -> &Coin {
&self.amount
}
pub fn owner(&self) -> &HumanAddr {
&self.owner
pub fn node_identity(&self) -> &String {
&self.node_identity
}
}
@@ -30,27 +33,27 @@ impl Display for Delegation {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{} {} delegated by {}",
self.amount.amount, self.amount.denom, self.owner
"{} {} delegated to {}",
self.amount.amount, self.amount.denom, self.node_identity
)
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedMixDelegationsResponse {
pub node_owner: HumanAddr,
pub node_identity: String,
pub delegations: Vec<Delegation>,
pub start_next_after: Option<HumanAddr>,
pub start_next_after: Option<String>,
}
impl PagedMixDelegationsResponse {
pub fn new(
node_owner: HumanAddr,
node_identity: String,
delegations: Vec<Delegation>,
start_next_after: Option<HumanAddr>,
start_next_after: Option<String>,
) -> Self {
PagedMixDelegationsResponse {
node_owner,
node_identity,
delegations,
start_next_after,
}
@@ -59,19 +62,19 @@ impl PagedMixDelegationsResponse {
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedGatewayDelegationsResponse {
pub node_owner: HumanAddr,
pub node_identity: String,
pub delegations: Vec<Delegation>,
pub start_next_after: Option<HumanAddr>,
pub start_next_after: Option<String>,
}
impl PagedGatewayDelegationsResponse {
pub fn new(
node_owner: HumanAddr,
node_identity: String,
delegations: Vec<Delegation>,
start_next_after: Option<HumanAddr>,
start_next_after: Option<String>,
) -> Self {
PagedGatewayDelegationsResponse {
node_owner,
node_identity,
delegations,
start_next_after,
}
+6 -6
View File
@@ -62,6 +62,10 @@ impl GatewayBond {
}
}
pub fn identity(&self) -> &String {
&self.gateway.identity_key
}
pub fn amount(&self) -> &[Coin] {
&self.amount
}
@@ -93,15 +97,11 @@ impl Display for GatewayBond {
pub struct PagedGatewayResponse {
pub nodes: Vec<GatewayBond>,
pub per_page: usize,
pub start_next_after: Option<HumanAddr>,
pub start_next_after: Option<String>,
}
impl PagedGatewayResponse {
pub fn new(
nodes: Vec<GatewayBond>,
per_page: usize,
start_next_after: Option<HumanAddr>,
) -> Self {
pub fn new(nodes: Vec<GatewayBond>, per_page: usize, start_next_after: Option<String>) -> Self {
PagedGatewayResponse {
nodes,
per_page,
+6 -6
View File
@@ -81,6 +81,10 @@ impl MixNodeBond {
}
}
pub fn identity(&self) -> &String {
&self.mix_node.identity_key
}
pub fn amount(&self) -> &[Coin] {
&self.amount
}
@@ -116,15 +120,11 @@ impl Display for MixNodeBond {
pub struct PagedResponse {
pub nodes: Vec<MixNodeBond>,
pub per_page: usize,
pub start_next_after: Option<HumanAddr>,
pub start_next_after: Option<String>,
}
impl PagedResponse {
pub fn new(
nodes: Vec<MixNodeBond>,
per_page: usize,
start_next_after: Option<HumanAddr>,
) -> Self {
pub fn new(nodes: Vec<MixNodeBond>, per_page: usize, start_next_after: Option<String>) -> Self {
PagedResponse {
nodes,
per_page,
+287 -160
View File
@@ -1,13 +1,15 @@
use crate::helpers::calculate_epoch_reward_rate;
use crate::msg::{HandleMsg, InitMsg, MigrateMsg, QueryMsg};
use crate::state::{State, StateParams};
use crate::storage::{config, layer_distribution};
use crate::storage::{
config, gateways, gateways_owners, layer_distribution, mixnodes, mixnodes_owners,
};
use crate::{error::ContractError, queries, transactions};
use cosmwasm_std::{
to_binary, Decimal, Deps, DepsMut, Env, HandleResponse, HumanAddr, InitResponse, MessageInfo,
MigrateResponse, QueryResponse, Uint128,
MigrateResponse, QueryResponse, StdResult, Uint128,
};
use mixnet_contract::LayerDistribution;
use mixnet_contract::{GatewayBond, MixNodeBond};
pub const INITIAL_DEFAULT_EPOCH_LENGTH: u32 = 2;
@@ -87,23 +89,23 @@ pub fn handle(
HandleMsg::UpdateStateParams(params) => {
transactions::try_update_state_params(deps, info, params)
}
HandleMsg::RewardMixnode { owner, uptime } => {
transactions::try_reward_mixnode(deps, info, owner, uptime)
HandleMsg::RewardMixnode { identity, uptime } => {
transactions::try_reward_mixnode(deps, info, identity, uptime)
}
HandleMsg::RewardGateway { owner, uptime } => {
transactions::try_reward_gateway(deps, info, owner, uptime)
HandleMsg::RewardGateway { identity, uptime } => {
transactions::try_reward_gateway(deps, info, identity, uptime)
}
HandleMsg::DelegateToMixnode { mix_owner } => {
transactions::try_delegate_to_mixnode(deps, info, mix_owner)
HandleMsg::DelegateToMixnode { mix_identity } => {
transactions::try_delegate_to_mixnode(deps, info, mix_identity)
}
HandleMsg::UndelegateFromMixnode { mix_owner } => {
transactions::try_remove_delegation_from_mixnode(deps, info, env, mix_owner)
HandleMsg::UndelegateFromMixnode { mix_identity } => {
transactions::try_remove_delegation_from_mixnode(deps, info, env, mix_identity)
}
HandleMsg::DelegateToGateway { gateway_owner } => {
transactions::try_delegate_to_gateway(deps, info, gateway_owner)
HandleMsg::DelegateToGateway { gateway_identity } => {
transactions::try_delegate_to_gateway(deps, info, gateway_identity)
}
HandleMsg::UndelegateFromGateway { gateway_owner } => {
transactions::try_remove_delegation_from_gateway(deps, info, env, gateway_owner)
HandleMsg::UndelegateFromGateway { gateway_identity } => {
transactions::try_remove_delegation_from_gateway(deps, info, env, gateway_identity)
}
}
}
@@ -125,34 +127,39 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
QueryMsg::StateParams {} => to_binary(&queries::query_state_params(deps)),
QueryMsg::LayerDistribution {} => to_binary(&queries::query_layer_distribution(deps)),
QueryMsg::GetMixDelegations {
mix_owner,
mix_identity,
start_after,
limit,
} => to_binary(&queries::query_mixnode_delegations_paged(
deps,
mix_owner,
mix_identity,
start_after,
limit,
)?),
QueryMsg::GetMixDelegation { mix_owner, address } => to_binary(
&queries::query_mixnode_delegation(deps, mix_owner, address)?,
),
QueryMsg::GetMixDelegation {
mix_identity,
address,
} => to_binary(&queries::query_mixnode_delegation(
deps,
mix_identity,
address,
)?),
QueryMsg::GetGatewayDelegations {
gateway_owner,
gateway_identity,
start_after,
limit,
} => to_binary(&queries::query_gateway_delegations_paged(
deps,
gateway_owner,
gateway_identity,
start_after,
limit,
)?),
QueryMsg::GetGatewayDelegation {
gateway_owner,
gateway_identity,
address,
} => to_binary(&queries::query_gateway_delegation(
deps,
gateway_owner,
gateway_identity,
address,
)?),
};
@@ -166,51 +173,113 @@ pub fn migrate(
_info: MessageInfo,
_msg: MigrateMsg,
) -> Result<MigrateResponse, ContractError> {
// load all mixnodes and gateways and build up layer distribution
let mut layers: LayerDistribution = Default::default();
// Mixnode migration
// go through mixnodes...
let mut start_after = None;
// What we do here for mixnodes is the following (the procedure is be identical for gateways):
// 1. Load mixnodes (page by page) using the PREFIX_MIXNODES_OLD
// 2. Save that the same data using PREFIX_MIXNODES, but the data key will be the value.mix_node.identity instead
// 3. Load mixnode owners (page by page) using PREFIX_MIXNODES_OWNERS_OLD
// 4. Save the data in reverse order using PREFIX_MIXNODES_OWNERS such that the key becomes the value and vice versa
use cosmwasm_std::Order;
use cosmwasm_std::Storage;
use cosmwasm_storage::{bucket, Bucket};
// those shouldn't be accessible ANYWHERE outside the migration
const PREFIX_MIXNODES_OLD: &[u8] = b"mixnodes";
const PREFIX_MIXNODES_OWNERS_OLD: &[u8] = b"mix-owners";
const PREFIX_GATEWAYS_OLD: &[u8] = b"gateways";
const PREFIX_GATEWAYS_OWNERS_OLD: &[u8] = b"gateway-owners";
fn mixnodes_owners_old(storage: &mut dyn Storage) -> Bucket<HumanAddr> {
bucket(storage, PREFIX_MIXNODES_OWNERS_OLD)
}
fn gateways_owners_old(storage: &mut dyn Storage) -> Bucket<HumanAddr> {
bucket(storage, PREFIX_GATEWAYS_OWNERS_OLD)
}
fn mixnodes_old(storage: &mut dyn Storage) -> Bucket<MixNodeBond> {
bucket(storage, PREFIX_MIXNODES_OLD)
}
fn gateways_old(storage: &mut dyn Storage) -> Bucket<GatewayBond> {
bucket(storage, PREFIX_GATEWAYS_OLD)
}
// go through all stored mixnodes
let bond_page_limit = 100;
loop {
let response = queries::query_mixnodes_paged(deps.as_ref(), start_after, None)?;
start_after = response.start_next_after;
if start_after.is_none() {
break;
// we have to do it in a paged manner to prevent allocating too much memory
let nodes = mixnodes_old(deps.storage)
.range(None, None, Order::Ascending)
.take(bond_page_limit)
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<MixNodeBond>>>()?;
for node in nodes.iter() {
// save bond data under identity key
mixnodes(deps.storage).save(node.identity().as_bytes(), node)?;
// and remove it from under the old owner key
mixnodes_old(deps.storage).remove(node.owner.as_bytes());
// add new mixnodes_owners data under owner key
mixnodes_owners(deps.storage).save(node.owner.as_bytes(), node.identity())?;
// and remove it from under the old identity key
mixnodes_owners_old(deps.storage).remove(node.identity().as_bytes())
}
for node in response.nodes.into_iter() {
match node.mix_node.layer {
n if n == 1 => layers.layer1 += 1,
n if n == 2 => layers.layer2 += 1,
n if n == 3 => layers.layer3 += 1,
_ => layers.invalid += 1,
}
// this was the last page
if nodes.len() < bond_page_limit {
break;
}
}
// go through gateways...
// repeat the procedure for gateways
loop {
let response = queries::query_gateways_paged(deps.as_ref(), start_after, None)?;
start_after = response.start_next_after;
if start_after.is_none() {
// we have to do it in a paged manner to prevent allocating too much memory
let nodes = gateways_old(deps.storage)
.range(None, None, Order::Ascending)
.take(bond_page_limit)
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<GatewayBond>>>()?;
for node in nodes.iter() {
// save bond data under identity key
gateways(deps.storage).save(node.identity().as_bytes(), node)?;
// and remove it from under the old owner key
gateways_old(deps.storage).remove(node.owner.as_bytes());
// add new mixnodes_owners data under owner key
gateways_owners(deps.storage).save(node.owner.as_bytes(), node.identity())?;
// and remove it from under the old identity key
gateways_owners_old(deps.storage).remove(node.identity().as_bytes())
}
// this was the last page
if nodes.len() < bond_page_limit {
break;
}
layers.gateways += response.nodes.len() as u64;
}
layer_distribution(deps.storage).save(&layers)?;
Ok(Default::default())
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::storage::{gateways, layer_distribution_read, mixnodes};
use crate::storage::{
gateways_owners_read, gateways_read, mixnodes_owners_read, mixnodes_read,
};
use crate::support::tests::helpers;
use crate::support::tests::helpers::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, from_binary};
use mixnet_contract::{Gateway, GatewayBond, MixNode, MixNodeBond, PagedResponse};
use mixnet_contract::{Gateway, MixNode, PagedResponse};
#[test]
fn initialize_contract() {
@@ -245,133 +314,191 @@ pub mod tests {
// TODO: this test will have to be removed once the migration happens and we are working on yet another
// version of the contract
#[test]
fn migration_to_layer_distribution() {
let layer_ones = 42;
let layer_twos = 123;
let layer_threes = 111;
let invalid = 30;
let gateways_count = 24;
fn migration_to_identity_key_based_storage() {
use cosmwasm_std::Storage;
use cosmwasm_storage::{bucket, Bucket};
// just some helpers
const PREFIX_MIXNODES_OLD: &[u8] = b"mixnodes";
const PREFIX_MIXNODES_OWNERS_OLD: &[u8] = b"mix-owners";
const PREFIX_GATEWAYS_OLD: &[u8] = b"gateways";
const PREFIX_GATEWAYS_OWNERS_OLD: &[u8] = b"gateway-owners";
fn mixnodes_owners_old(storage: &mut dyn Storage) -> Bucket<HumanAddr> {
bucket(storage, PREFIX_MIXNODES_OWNERS_OLD)
}
fn gateways_owners_old(storage: &mut dyn Storage) -> Bucket<HumanAddr> {
bucket(storage, PREFIX_GATEWAYS_OWNERS_OLD)
}
fn mixnodes_old(storage: &mut dyn Storage) -> Bucket<MixNodeBond> {
bucket(storage, PREFIX_MIXNODES_OLD)
}
fn gateways_old(storage: &mut dyn Storage) -> Bucket<GatewayBond> {
bucket(storage, PREFIX_GATEWAYS_OLD)
}
// bond some nodes
let mut deps = helpers::init_contract();
let env = mock_env();
for i in 0..layer_ones {
let owner = HumanAddr::from(format!("owner{}{}", 1, i));
mixnodes(&mut deps.storage)
.save(
owner.clone().as_bytes(),
&MixNodeBond {
amount: coins(1000, "uhal"),
owner,
mix_node: MixNode {
host: "1.1.1.1:1111".to_string(),
layer: 1,
location: "aaaa".to_string(),
sphinx_key: "bbbb".to_string(),
identity_key: format!("identity{}{}", 1, i),
version: "0.10.1".to_string(),
},
},
)
// 'simulate' what we're going to start off with, so about 7000-8000 mixnodes and couple of gateways
// NOTE: since the tests are not run inside wasm VM, we don't have to worry about memory allocation limits
// (as long as it's reasonable enough)
let total_mixnodes = 7871;
let total_gateways = 424;
for mix_i in 0..total_mixnodes {
// make each mixnode have initial_bond + i of tokens to make sure
// it's correctly preserved
let bond_value = INITIAL_MIXNODE_BOND.u128() + mix_i as u128;
let bond = MixNodeBond {
amount: coins(bond_value, DENOM),
owner: format!("mix-owner{}", mix_i).into(),
mix_node: MixNode {
host: format!("host{}", mix_i),
layer: mix_i % 3 + 1,
location: format!("location{}", mix_i),
sphinx_key: format!("sphinx{}", mix_i),
identity_key: format!("identity{}", mix_i),
version: format!("version{}", mix_i),
},
};
mixnodes_old(&mut deps.storage)
.save(bond.owner.as_bytes(), &bond)
.unwrap();
mixnodes_owners_old(&mut deps.storage)
.save(bond.identity().as_bytes(), bond.owner())
.unwrap();
}
for i in 0..layer_twos {
let owner = HumanAddr::from(format!("owner{}{}", 2, i));
mixnodes(&mut deps.storage)
.save(
owner.clone().as_bytes(),
&MixNodeBond {
amount: coins(1000, "uhal"),
owner,
mix_node: MixNode {
host: "1.1.1.1:1111".to_string(),
layer: 2,
location: "aaaa".to_string(),
sphinx_key: "bbbb".to_string(),
identity_key: format!("identity{}{}", 2, i),
version: "0.10.1".to_string(),
},
},
)
for gate_i in 0..total_gateways {
// make each gateway have initial_bond + i of tokens to make sure
// it's correctly preserved
let bond_value = INITIAL_GATEWAY_BOND.u128() + gate_i as u128;
let bond = GatewayBond {
amount: coins(bond_value, DENOM),
owner: format!("gateway-owner{}", gate_i).into(),
gateway: Gateway {
mix_host: format!("mixhost{}", gate_i),
clients_host: format!("clientshost{}", gate_i),
location: format!("location{}", gate_i),
sphinx_key: format!("sphinx{}", gate_i),
identity_key: format!("identity{}", gate_i),
version: format!("version{}", gate_i),
},
};
gateways_old(&mut deps.storage)
.save(bond.owner.as_bytes(), &bond)
.unwrap();
gateways_owners_old(&mut deps.storage)
.save(bond.identity().as_bytes(), bond.owner())
.unwrap();
}
for i in 0..layer_threes {
let owner = HumanAddr::from(format!("owner{}{}", 3, i));
mixnodes(&mut deps.storage)
.save(
owner.clone().as_bytes(),
&MixNodeBond {
amount: coins(1000, "uhal"),
owner,
mix_node: MixNode {
host: "1.1.1.1:1111".to_string(),
layer: 3,
location: "aaaa".to_string(),
sphinx_key: "bbbb".to_string(),
identity_key: format!("identity{}{}", 3, i),
version: "0.10.1".to_string(),
},
},
)
.unwrap();
// perform migration
assert!(migrate(
deps.as_mut(),
mock_env(),
mock_info("sender", &[]),
MigrateMsg {}
)
.is_ok());
// make sure all data under old keys is purged
for mix_i in 0..total_mixnodes {
let owner = format!("mix-owner{}", mix_i);
let identity = format!("identity{}", mix_i);
assert!(mixnodes_old(&mut deps.storage)
.may_load(owner.as_bytes())
.unwrap()
.is_none());
assert!(mixnodes_owners_old(&mut deps.storage)
.may_load(identity.as_bytes())
.unwrap()
.is_none());
}
for i in 0..invalid {
let owner = HumanAddr::from(format!("owner{}{}", 42, i));
mixnodes(&mut deps.storage)
.save(
owner.clone().as_bytes(),
&MixNodeBond {
amount: coins(1000, "uhal"),
owner,
mix_node: MixNode {
host: "1.1.1.1:1111".to_string(),
layer: 42,
location: "aaaa".to_string(),
sphinx_key: "bbbb".to_string(),
identity_key: format!("identity{}{}", 42, i),
version: "0.10.1".to_string(),
},
},
)
.unwrap();
for gate_i in 0..total_gateways {
let owner = format!("gateway-owner{}", gate_i);
let identity = format!("identity{}", gate_i);
assert!(gateways_old(&mut deps.storage)
.may_load(owner.as_bytes())
.unwrap()
.is_none());
assert!(gateways_owners_old(&mut deps.storage)
.may_load(identity.as_bytes())
.unwrap()
.is_none());
}
for i in 0..gateways_count {
let owner = HumanAddr::from(format!("owner{}{}", "gateway", i));
gateways(&mut deps.storage)
.save(
owner.clone().as_bytes(),
&GatewayBond {
amount: coins(1000, "uhal"),
owner,
gateway: Gateway {
mix_host: "1.1.1.1:1111".to_string(),
clients_host: "ws://1.1.1.1:1112".to_string(),
location: "aaaa".to_string(),
sphinx_key: "bbbb".to_string(),
identity_key: format!("identity{}{}", "gateway", i),
version: "0.10.1".to_string(),
},
},
)
.unwrap();
// and make sure it exists under new keys now
for mix_i in 0..total_mixnodes {
let owner = format!("mix-owner{}", mix_i);
let identity = format!("identity{}", mix_i);
let expected_bond_value = INITIAL_MIXNODE_BOND.u128() + mix_i as u128;
let expected_bond = MixNodeBond {
amount: coins(expected_bond_value, DENOM),
owner: format!("mix-owner{}", mix_i).into(),
mix_node: MixNode {
host: format!("host{}", mix_i),
layer: mix_i % 3 + 1,
location: format!("location{}", mix_i),
sphinx_key: format!("sphinx{}", mix_i),
identity_key: format!("identity{}", mix_i),
version: format!("version{}", mix_i),
},
};
assert_eq!(
expected_bond,
mixnodes_read(&deps.storage)
.load(identity.as_bytes())
.unwrap()
);
assert_eq!(
identity,
mixnodes_owners_read(&deps.storage)
.load(owner.as_bytes())
.unwrap()
);
}
let migrate_res = migrate(deps.as_mut(), env, mock_info("creator", &[]), MigrateMsg {});
assert!(migrate_res.is_ok());
for gate_i in 0..total_gateways {
let owner = format!("gateway-owner{}", gate_i);
let identity = format!("identity{}", gate_i);
let layers = layer_distribution_read(&deps.storage).load().unwrap();
let expected = LayerDistribution {
gateways: gateways_count,
layer1: layer_ones,
layer2: layer_twos,
layer3: layer_threes,
invalid,
};
assert_eq!(expected, layers);
let expected_bond_value = INITIAL_GATEWAY_BOND.u128() + gate_i as u128;
let expected_bond = GatewayBond {
amount: coins(expected_bond_value, DENOM),
owner: format!("gateway-owner{}", gate_i).into(),
gateway: Gateway {
mix_host: format!("mixhost{}", gate_i),
clients_host: format!("clientshost{}", gate_i),
location: format!("location{}", gate_i),
sphinx_key: format!("sphinx{}", gate_i),
identity_key: format!("identity{}", gate_i),
version: format!("version{}", gate_i),
},
};
assert_eq!(
expected_bond,
gateways_read(&deps.storage)
.load(identity.as_bytes())
.unwrap()
);
assert_eq!(
identity,
gateways_owners_read(&deps.storage)
.load(owner.as_bytes())
.unwrap()
);
}
}
}
+18 -9
View File
@@ -16,22 +16,28 @@ pub enum ContractError {
)]
InsufficientMixNodeBond { received: u128, minimum: u128 },
#[error("Account does not own any mixnode bonds")]
MixNodeBondNotFound {},
#[error("Mixnode ({identity:?}) does not exist")]
MixNodeBondNotFound { identity: String },
#[error(
"Not enough funds sent for gateway bond. (received {received:?}, minimum {minimum:?})"
)]
InsufficientGatewayBond { received: u128, minimum: u128 },
#[error("Account ({account:?}) does not own any gateway bonds")]
GatewayBondNotFound { account: HumanAddr },
#[error("Gateway ({identity:?}) does not exist")]
GatewayBondNotFound { identity: String },
#[error("{owner:?} does not seem to own any mixnodes")]
NoAssociatedMixNodeBond { owner: HumanAddr },
#[error("{owner:?} does not seem to own any gateways")]
NoAssociatedGatewayBond { owner: HumanAddr },
#[error("Unauthorized")]
Unauthorized,
#[error("Wrong coin denomination, you must send {}", DENOM)]
WrongDenom {},
WrongDenom,
#[error("Received multiple coin types during staking")]
MultipleDenoms,
@@ -63,9 +69,12 @@ pub enum ContractError {
#[error("No funds were provided for the delegation")]
EmptyDelegation,
#[error("Could not find any delegation information associated with mixnode owned by {mixnode_owner:?}")]
NoMixnodeDelegationFound { mixnode_owner: HumanAddr },
#[error("Request did not come from the node owner ({owner:?})")]
InvalidSender { owner: HumanAddr },
#[error("Could not find any delegation information associated with gateway owned by {gateway_owner:?}")]
NoGatewayDelegationFound { gateway_owner: HumanAddr },
#[error("Could not find any delegation information associated with mixnode {identity:?}")]
NoMixnodeDelegationFound { identity: String },
#[error("Could not find any delegation information associated with gateway {identity:?}")]
NoGatewayDelegationFound { identity: String },
}
+14 -14
View File
@@ -21,29 +21,29 @@ pub enum HandleMsg {
UpdateStateParams(StateParams),
DelegateToMixnode {
mix_owner: HumanAddr,
mix_identity: String,
},
UndelegateFromMixnode {
mix_owner: HumanAddr,
mix_identity: String,
},
DelegateToGateway {
gateway_owner: HumanAddr,
gateway_identity: String,
},
UndelegateFromGateway {
gateway_owner: HumanAddr,
gateway_identity: String,
},
RewardMixnode {
owner: HumanAddr,
identity: String,
// percentage value in range 0-100
uptime: u32,
},
RewardGateway {
owner: HumanAddr,
identity: String,
// percentage value in range 0-100
uptime: u32,
},
@@ -54,10 +54,10 @@ pub enum HandleMsg {
pub enum QueryMsg {
GetMixNodes {
limit: Option<u32>,
start_after: Option<HumanAddr>,
start_after: Option<String>,
},
GetGateways {
start_after: Option<HumanAddr>,
start_after: Option<String>,
limit: Option<u32>,
},
OwnsMixnode {
@@ -68,21 +68,21 @@ pub enum QueryMsg {
},
StateParams {},
GetMixDelegations {
mix_owner: HumanAddr,
start_after: Option<HumanAddr>,
mix_identity: String,
start_after: Option<String>,
limit: Option<u32>,
},
GetMixDelegation {
mix_owner: HumanAddr,
mix_identity: String,
address: HumanAddr,
},
GetGatewayDelegations {
gateway_owner: HumanAddr,
start_after: Option<HumanAddr>,
gateway_identity: String,
start_after: Option<String>,
limit: Option<u32>,
},
GetGatewayDelegation {
gateway_owner: HumanAddr,
gateway_identity: String,
address: HumanAddr,
},
LayerDistribution {},
+145 -126
View File
@@ -2,8 +2,8 @@ use crate::contract::DENOM;
use crate::error::ContractError;
use crate::state::StateParams;
use crate::storage::{
gateway_delegations_read, gateways_read, mix_delegations_read, mixnodes_read,
read_layer_distribution, read_state_params,
gateway_delegations_read, gateways_owners_read, gateways_read, mix_delegations_read,
mixnodes_owners_read, mixnodes_read, read_layer_distribution, read_state_params,
};
use cosmwasm_std::Deps;
use cosmwasm_std::Order;
@@ -24,7 +24,7 @@ const DELEGATION_PAGE_DEFAULT_LIMIT: u32 = 500;
pub fn query_mixnodes_paged(
deps: Deps,
start_after: Option<HumanAddr>,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedResponse> {
let limit = limit
@@ -38,14 +38,14 @@ pub fn query_mixnodes_paged(
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<MixNodeBond>>>()?;
let start_next_after = nodes.last().map(|node| node.owner().clone());
let start_next_after = nodes.last().map(|node| node.identity().clone());
Ok(PagedResponse::new(nodes, limit, start_next_after))
}
pub(crate) fn query_gateways_paged(
deps: Deps,
start_after: Option<HumanAddr>,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedGatewayResponse> {
let limit = limit
@@ -59,7 +59,7 @@ pub(crate) fn query_gateways_paged(
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<GatewayBond>>>()?;
let start_next_after = nodes.last().map(|node| node.owner().clone());
let start_next_after = nodes.last().map(|node| node.identity().clone());
Ok(PagedGatewayResponse::new(nodes, limit, start_next_after))
}
@@ -68,7 +68,7 @@ pub(crate) fn query_owns_mixnode(
deps: Deps,
address: HumanAddr,
) -> StdResult<MixOwnershipResponse> {
let has_node = mixnodes_read(deps.storage)
let has_node = mixnodes_owners_read(deps.storage)
.may_load(address.as_ref())?
.is_some();
Ok(MixOwnershipResponse { address, has_node })
@@ -78,7 +78,7 @@ pub(crate) fn query_owns_gateway(
deps: Deps,
address: HumanAddr,
) -> StdResult<GatewayOwnershipResponse> {
let has_gateway = gateways_read(deps.storage)
let has_gateway = gateways_owners_read(deps.storage)
.may_load(address.as_ref())?
.is_some();
Ok(GatewayOwnershipResponse {
@@ -97,20 +97,21 @@ pub(crate) fn query_layer_distribution(deps: Deps) -> LayerDistribution {
/// Adds a 0 byte to terminate the `start_after` value given. This allows CosmWasm
/// to get the succeeding key as the start of the next page.
fn calculate_start_value(
start_after: std::option::Option<cosmwasm_std::HumanAddr>,
) -> Option<Vec<u8>> {
start_after.as_ref().map(|addr| {
let mut bytes = addr.as_bytes().to_owned();
bytes.push(0);
bytes
fn calculate_start_value(start_after: Option<String>) -> Option<Vec<u8>> {
start_after.as_ref().map(|identity| {
identity
.as_bytes()
.iter()
.cloned()
.chain(std::iter::once(0))
.collect()
})
}
pub(crate) fn query_mixnode_delegations_paged(
deps: Deps,
mix_owner: HumanAddr,
start_after: Option<HumanAddr>,
mix_identity: String,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedMixDelegationsResponse> {
let limit = limit
@@ -118,13 +119,13 @@ pub(crate) fn query_mixnode_delegations_paged(
.min(DELEGATION_PAGE_MAX_LIMIT) as usize;
let start = calculate_start_value(start_after);
let delegations = mix_delegations_read(deps.storage, &mix_owner)
let delegations = mix_delegations_read(deps.storage, &mix_identity)
.range(start.as_deref(), None, Order::Ascending)
.take(limit)
.map(|res| {
res.map(|entry| {
Delegation::new(
HumanAddr::from(String::from_utf8(entry.0).unwrap()),
String::from_utf8(entry.0).unwrap(),
coin(entry.1.u128(), DENOM),
)
})
@@ -133,10 +134,10 @@ pub(crate) fn query_mixnode_delegations_paged(
let start_next_after = delegations
.last()
.map(|delegation| delegation.owner().clone());
.map(|delegation| delegation.node_identity().clone());
Ok(PagedMixDelegationsResponse::new(
mix_owner,
mix_identity,
delegations,
start_next_after,
))
@@ -145,24 +146,24 @@ pub(crate) fn query_mixnode_delegations_paged(
// queries for delegation value of given address for particular node
pub(crate) fn query_mixnode_delegation(
deps: Deps,
mix_owner: HumanAddr,
mix_identity: String,
address: HumanAddr,
) -> Result<Delegation, ContractError> {
match mix_delegations_read(deps.storage, &mix_owner).may_load(address.as_bytes())? {
match mix_delegations_read(deps.storage, &mix_identity).may_load(address.as_bytes())? {
Some(delegation_value) => Ok(Delegation::new(
address,
mix_identity,
coin(delegation_value.u128(), DENOM),
)),
None => Err(ContractError::NoMixnodeDelegationFound {
mixnode_owner: mix_owner,
identity: mix_identity,
}),
}
}
pub(crate) fn query_gateway_delegations_paged(
deps: Deps,
gateway_owner: HumanAddr,
start_after: Option<HumanAddr>,
gateway_identity: String,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedGatewayDelegationsResponse> {
let limit = limit
@@ -170,13 +171,13 @@ pub(crate) fn query_gateway_delegations_paged(
.min(DELEGATION_PAGE_MAX_LIMIT) as usize;
let start = calculate_start_value(start_after);
let delegations = gateway_delegations_read(deps.storage, &gateway_owner)
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(
HumanAddr::from(String::from_utf8(entry.0).unwrap()),
String::from_utf8(entry.0).unwrap(),
coin(entry.1.u128(), DENOM),
)
})
@@ -185,10 +186,10 @@ pub(crate) fn query_gateway_delegations_paged(
let start_next_after = delegations
.last()
.map(|delegation| delegation.owner().clone());
.map(|delegation| delegation.node_identity().clone());
Ok(PagedGatewayDelegationsResponse::new(
gateway_owner,
gateway_identity,
delegations,
start_next_after,
))
@@ -197,15 +198,17 @@ pub(crate) fn query_gateway_delegations_paged(
// queries for delegation value of given address for particular node
pub(crate) fn query_gateway_delegation(
deps: Deps,
gateway_owner: HumanAddr,
gateway_identity: String,
address: HumanAddr,
) -> Result<Delegation, ContractError> {
match gateway_delegations_read(deps.storage, &gateway_owner).may_load(address.as_bytes())? {
match gateway_delegations_read(deps.storage, &gateway_identity).may_load(address.as_bytes())? {
Some(delegation_value) => Ok(Delegation::new(
address,
gateway_identity,
coin(delegation_value.u128(), DENOM),
)),
None => Err(ContractError::NoGatewayDelegationFound { gateway_owner }),
None => Err(ContractError::NoGatewayDelegationFound {
identity: gateway_identity,
}),
}
}
@@ -215,7 +218,11 @@ mod tests {
use crate::state::State;
use crate::storage::{config, gateway_delegations, gateways, mix_delegations, mixnodes};
use crate::support::tests::helpers;
use crate::support::tests::helpers::{good_gateway_bond, good_mixnode_bond};
use crate::transactions;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{Storage, Uint128};
use mixnet_contract::{Gateway, MixNode};
#[test]
fn mixnodes_empty_on_init() {
@@ -307,7 +314,7 @@ mod tests {
assert_eq!(2, page1.nodes.len());
// retrieving the next page should start after the last key on this page
let start_after = HumanAddr::from("2");
let start_after = String::from("2");
let page2 = query_mixnodes_paged(
deps.as_ref(),
Option::from(start_after),
@@ -322,7 +329,7 @@ mod tests {
.save("4".as_bytes(), &node)
.unwrap();
let start_after = HumanAddr::from("2");
let start_after = String::from("2");
let page2 = query_mixnodes_paged(
deps.as_ref(),
Option::from(start_after),
@@ -419,7 +426,7 @@ mod tests {
assert_eq!(2, page1.nodes.len());
// retrieving the next page should start after the last key on this page
let start_after = HumanAddr::from("2");
let start_after = String::from("2");
let page2 = query_gateways_paged(
deps.as_ref(),
Option::from(start_after),
@@ -434,7 +441,7 @@ mod tests {
.save("4".as_bytes(), &node)
.unwrap();
let start_after = HumanAddr::from("2");
let start_after = String::from("2");
let page2 = query_gateways_paged(
deps.as_ref(),
Option::from(start_after),
@@ -449,31 +456,37 @@ mod tests {
#[test]
fn query_for_mixnode_owner_works() {
let mut deps = helpers::init_contract();
let env = mock_env();
// "fred" does not own a mixnode if there are no mixnodes
let res = query_owns_mixnode(deps.as_ref(), "fred".into()).unwrap();
assert!(!res.has_node);
// mixnode was added to "bob", "fred" still does not own one
let node = helpers::mixnode_bond_fixture();
mixnodes(&mut deps.storage)
.save("bob".as_bytes(), &node)
let node = MixNode {
identity_key: "bobsnode".into(),
..helpers::mix_node_fixture()
};
transactions::try_add_mixnode(deps.as_mut(), mock_info("bob", &good_mixnode_bond()), node)
.unwrap();
let res = query_owns_mixnode(deps.as_ref(), "fred".into()).unwrap();
assert!(!res.has_node);
// "fred" now owns a mixnode!
let node2 = helpers::mixnode_bond_fixture();
mixnodes(&mut deps.storage)
.save("fred".as_bytes(), &node2)
let node = MixNode {
identity_key: "fredsnode".into(),
..helpers::mix_node_fixture()
};
transactions::try_add_mixnode(deps.as_mut(), mock_info("fred", &good_mixnode_bond()), node)
.unwrap();
let res = query_owns_mixnode(deps.as_ref(), "fred".into()).unwrap();
assert!(res.has_node);
// but after unbonding it, he doesn't own one anymore
mixnodes(&mut deps.storage).remove("fred".as_bytes());
transactions::try_remove_mixnode(deps.as_mut(), mock_info("fred", &[]), env).unwrap();
let res = query_owns_mixnode(deps.as_ref(), "fred".into()).unwrap();
assert!(!res.has_node);
}
@@ -481,31 +494,37 @@ mod tests {
#[test]
fn query_for_gateway_owner_works() {
let mut deps = helpers::init_contract();
let env = mock_env();
// "fred" does not own a mixnode if there are no mixnodes
let res = query_owns_gateway(deps.as_ref(), "fred".into()).unwrap();
assert!(!res.has_gateway);
// mixnode was added to "bob", "fred" still does not own one
let node = helpers::gateway_bond_fixture();
gateways(&mut deps.storage)
.save("bob".as_bytes(), &node)
let node = Gateway {
identity_key: "bobsnode".into(),
..helpers::gateway_fixture()
};
transactions::try_add_gateway(deps.as_mut(), mock_info("bob", &good_gateway_bond()), node)
.unwrap();
let res = query_owns_gateway(deps.as_ref(), "fred".into()).unwrap();
assert!(!res.has_gateway);
// "fred" now owns a mixnode!
let node2 = helpers::gateway_bond_fixture();
gateways(&mut deps.storage)
.save("fred".as_bytes(), &node2)
// "fred" now owns a gateway!
let node = Gateway {
identity_key: "fredsnode".into(),
..helpers::gateway_fixture()
};
transactions::try_add_gateway(deps.as_mut(), mock_info("fred", &good_gateway_bond()), node)
.unwrap();
let res = query_owns_gateway(deps.as_ref(), "fred".into()).unwrap();
assert!(res.has_gateway);
// but after unbonding it, he doesn't own one anymore
gateways(&mut deps.storage).remove("fred".as_bytes());
transactions::try_remove_gateway(deps.as_mut(), mock_info("fred", &[]), env).unwrap();
let res = query_owns_gateway(deps.as_ref(), "fred".into()).unwrap();
assert!(!res.has_gateway);
}
@@ -540,10 +559,10 @@ mod tests {
use crate::storage::mix_delegations;
use cosmwasm_std::Uint128;
fn store_n_delegations(n: u32, storage: &mut dyn Storage, node_owner: &HumanAddr) {
fn store_n_delegations(n: u32, storage: &mut dyn Storage, node_identity: &str) {
for i in 0..n {
let address = format!("address{}", i);
mix_delegations(storage, node_owner)
mix_delegations(storage, node_identity)
.save(address.as_bytes(), &Uint128(42))
.unwrap();
}
@@ -553,12 +572,12 @@ mod tests {
fn retrieval_obeys_limits() {
let mut deps = helpers::init_contract();
let limit = 2;
let node_owner: HumanAddr = "foo".into();
store_n_delegations(100, &mut deps.storage, &node_owner);
let node_identity: String = "foo".into();
store_n_delegations(100, &mut deps.storage, &node_identity);
let page1 = query_mixnode_delegations_paged(
deps.as_ref(),
node_owner,
node_identity,
None,
Option::from(limit),
)
@@ -569,16 +588,16 @@ mod tests {
#[test]
fn retrieval_has_default_limit() {
let mut deps = helpers::init_contract();
let node_owner: HumanAddr = "foo".into();
let node_identity: String = "foo".into();
store_n_delegations(
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
&mut deps.storage,
&node_owner,
&node_identity,
);
// query without explicitly setting a limit
let page1 =
query_mixnode_delegations_paged(deps.as_ref(), node_owner, None, None).unwrap();
query_mixnode_delegations_paged(deps.as_ref(), node_identity, None, None).unwrap();
assert_eq!(
DELEGATION_PAGE_DEFAULT_LIMIT,
page1.delegations.len() as u32
@@ -588,18 +607,18 @@ mod tests {
#[test]
fn retrieval_has_max_limit() {
let mut deps = helpers::init_contract();
let node_owner: HumanAddr = "foo".into();
let node_identity: String = "foo".into();
store_n_delegations(
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
&mut deps.storage,
&node_owner,
&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_mixnode_delegations_paged(
deps.as_ref(),
node_owner,
node_identity,
None,
Option::from(crazy_limit),
)
@@ -613,16 +632,16 @@ mod tests {
#[test]
fn pagination_works() {
let mut deps = helpers::init_contract();
let node_owner: HumanAddr = "foo".into();
let node_identity: String = "foo".into();
mix_delegations(&mut deps.storage, &node_owner)
mix_delegations(&mut deps.storage, &node_identity)
.save("1".as_bytes(), &Uint128(42))
.unwrap();
let per_page = 2;
let page1 = query_mixnode_delegations_paged(
deps.as_ref(),
node_owner.clone(),
node_identity.clone(),
None,
Option::from(per_page),
)
@@ -632,28 +651,28 @@ mod tests {
assert_eq!(1, page1.delegations.len());
// save another
mix_delegations(&mut deps.storage, &node_owner)
mix_delegations(&mut deps.storage, &node_identity)
.save("2".as_bytes(), &Uint128(42))
.unwrap();
// page1 should have 2 results on it
let page1 = query_mixnode_delegations_paged(
deps.as_ref(),
node_owner.clone(),
node_identity.clone(),
None,
Option::from(per_page),
)
.unwrap();
assert_eq!(2, page1.delegations.len());
mix_delegations(&mut deps.storage, &node_owner)
mix_delegations(&mut deps.storage, &node_identity)
.save("3".as_bytes(), &Uint128(42))
.unwrap();
// page1 still has 2 results
let page1 = query_mixnode_delegations_paged(
deps.as_ref(),
node_owner.clone(),
node_identity.clone(),
None,
Option::from(per_page),
)
@@ -661,10 +680,10 @@ mod tests {
assert_eq!(2, page1.delegations.len());
// retrieving the next page should start after the last key on this page
let start_after = HumanAddr::from("2");
let start_after = String::from("2");
let page2 = query_mixnode_delegations_paged(
deps.as_ref(),
node_owner.clone(),
node_identity.clone(),
Option::from(start_after),
Option::from(per_page),
)
@@ -673,14 +692,14 @@ mod tests {
assert_eq!(1, page2.delegations.len());
// save another one
mix_delegations(&mut deps.storage, &node_owner)
mix_delegations(&mut deps.storage, &node_identity)
.save("4".as_bytes(), &Uint128(42))
.unwrap();
let start_after = HumanAddr::from("2");
let start_after = String::from("2");
let page2 = query_mixnode_delegations_paged(
deps.as_ref(),
node_owner,
node_identity,
Option::from(start_after),
Option::from(per_page),
)
@@ -694,52 +713,52 @@ mod tests {
#[test]
fn mix_deletion_query_returns_current_delegation_value() {
let mut deps = helpers::init_contract();
let node_owner: HumanAddr = "foo".into();
let node_identity: String = "foo".into();
mix_delegations(&mut deps.storage, &node_owner)
mix_delegations(&mut deps.storage, &node_identity)
.save("foo".as_bytes(), &Uint128(42))
.unwrap();
assert_eq!(
Ok(Delegation::new(node_owner.clone(), coin(42, DENOM))),
query_mixnode_delegation(deps.as_ref(), node_owner, "foo".into())
Ok(Delegation::new(node_identity.clone(), coin(42, DENOM))),
query_mixnode_delegation(deps.as_ref(), node_identity, "foo".into())
)
}
#[test]
fn mix_deletion_query_returns_error_if_delegation_doesnt_exist() {
let mut deps = helpers::init_contract();
let node_owner: HumanAddr = "foo".into();
let node_identity: String = "foo".into();
assert_eq!(
Err(ContractError::NoMixnodeDelegationFound {
mixnode_owner: node_owner.clone(),
identity: node_identity.clone(),
}),
query_mixnode_delegation(deps.as_ref(), node_owner.clone(), "foo".into())
query_mixnode_delegation(deps.as_ref(), node_identity.clone(), "foo".into())
);
// add delegation from a different address
mix_delegations(&mut deps.storage, &node_owner)
mix_delegations(&mut deps.storage, &node_identity)
.save("bar".as_bytes(), &Uint128(42))
.unwrap();
assert_eq!(
Err(ContractError::NoMixnodeDelegationFound {
mixnode_owner: node_owner.clone(),
identity: node_identity.clone(),
}),
query_mixnode_delegation(deps.as_ref(), node_owner.clone(), "foo".into())
query_mixnode_delegation(deps.as_ref(), node_identity.clone(), "foo".into())
);
// add delegation for a different node
mix_delegations(&mut deps.storage, &"differentnode".into())
mix_delegations(&mut deps.storage, "differentnode")
.save("foo".as_bytes(), &Uint128(42))
.unwrap();
assert_eq!(
Err(ContractError::NoMixnodeDelegationFound {
mixnode_owner: node_owner.clone(),
identity: node_identity.clone(),
}),
query_mixnode_delegation(deps.as_ref(), node_owner.clone(), "foo".into())
query_mixnode_delegation(deps.as_ref(), node_identity.clone(), "foo".into())
)
}
@@ -749,10 +768,10 @@ mod tests {
use crate::storage::gateway_delegations;
use cosmwasm_std::Uint128;
fn store_n_delegations(n: u32, storage: &mut dyn Storage, node_owner: &HumanAddr) {
fn store_n_delegations(n: u32, storage: &mut dyn Storage, node_identity: &str) {
for i in 0..n {
let address = format!("address{}", i);
gateway_delegations(storage, node_owner)
gateway_delegations(storage, node_identity)
.save(address.as_bytes(), &Uint128(42))
.unwrap();
}
@@ -762,12 +781,12 @@ mod tests {
fn retrieval_obeys_limits() {
let mut deps = helpers::init_contract();
let limit = 2;
let node_owner: HumanAddr = "foo".into();
store_n_delegations(100, &mut deps.storage, &node_owner);
let node_identity: String = "foo".into();
store_n_delegations(100, &mut deps.storage, &node_identity);
let page1 = query_gateway_delegations_paged(
deps.as_ref(),
node_owner,
node_identity,
None,
Option::from(limit),
)
@@ -778,16 +797,16 @@ mod tests {
#[test]
fn retrieval_has_default_limit() {
let mut deps = helpers::init_contract();
let node_owner: HumanAddr = "foo".into();
let node_identity: String = "foo".into();
store_n_delegations(
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
&mut deps.storage,
&node_owner,
&node_identity,
);
// query without explicitly setting a limit
let page1 =
query_gateway_delegations_paged(deps.as_ref(), node_owner, None, None).unwrap();
query_gateway_delegations_paged(deps.as_ref(), node_identity, None, None).unwrap();
assert_eq!(
DELEGATION_PAGE_DEFAULT_LIMIT,
page1.delegations.len() as u32
@@ -797,18 +816,18 @@ mod tests {
#[test]
fn retrieval_has_max_limit() {
let mut deps = helpers::init_contract();
let node_owner: HumanAddr = "foo".into();
let node_identity: String = "foo".into();
store_n_delegations(
DELEGATION_PAGE_DEFAULT_LIMIT * 10,
&mut deps.storage,
&node_owner,
&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_owner,
node_identity,
None,
Option::from(crazy_limit),
)
@@ -822,16 +841,16 @@ mod tests {
#[test]
fn pagination_works() {
let mut deps = helpers::init_contract();
let node_owner: HumanAddr = "foo".into();
let node_identity: String = "foo".into();
gateway_delegations(&mut deps.storage, &node_owner)
gateway_delegations(&mut deps.storage, &node_identity)
.save("1".as_bytes(), &Uint128(42))
.unwrap();
let per_page = 2;
let page1 = query_gateway_delegations_paged(
deps.as_ref(),
node_owner.clone(),
node_identity.clone(),
None,
Option::from(per_page),
)
@@ -841,28 +860,28 @@ mod tests {
assert_eq!(1, page1.delegations.len());
// save another
gateway_delegations(&mut deps.storage, &node_owner)
gateway_delegations(&mut deps.storage, &node_identity)
.save("2".as_bytes(), &Uint128(42))
.unwrap();
// page1 should have 2 results on it
let page1 = query_gateway_delegations_paged(
deps.as_ref(),
node_owner.clone(),
node_identity.clone(),
None,
Option::from(per_page),
)
.unwrap();
assert_eq!(2, page1.delegations.len());
gateway_delegations(&mut deps.storage, &node_owner)
gateway_delegations(&mut deps.storage, &node_identity)
.save("3".as_bytes(), &Uint128(42))
.unwrap();
// page1 still has 2 results
let page1 = query_gateway_delegations_paged(
deps.as_ref(),
node_owner.clone(),
node_identity.clone(),
None,
Option::from(per_page),
)
@@ -870,10 +889,10 @@ mod tests {
assert_eq!(2, page1.delegations.len());
// retrieving the next page should start after the last key on this page
let start_after = HumanAddr::from("2");
let start_after = String::from("2");
let page2 = query_gateway_delegations_paged(
deps.as_ref(),
node_owner.clone(),
node_identity.clone(),
Option::from(start_after),
Option::from(per_page),
)
@@ -882,14 +901,14 @@ mod tests {
assert_eq!(1, page2.delegations.len());
// save another one
gateway_delegations(&mut deps.storage, &node_owner)
gateway_delegations(&mut deps.storage, &node_identity)
.save("4".as_bytes(), &Uint128(42))
.unwrap();
let start_after = HumanAddr::from("2");
let start_after = String::from("2");
let page2 = query_gateway_delegations_paged(
deps.as_ref(),
node_owner,
node_identity,
Option::from(start_after),
Option::from(per_page),
)
@@ -903,52 +922,52 @@ mod tests {
#[test]
fn gateway_deletion_query_returns_current_delegation_value() {
let mut deps = helpers::init_contract();
let node_owner: HumanAddr = "foo".into();
let node_identity: String = "foo".into();
gateway_delegations(&mut deps.storage, &node_owner)
gateway_delegations(&mut deps.storage, &node_identity)
.save("foo".as_bytes(), &Uint128(42))
.unwrap();
assert_eq!(
Ok(Delegation::new(node_owner.clone(), coin(42, DENOM))),
query_gateway_delegation(deps.as_ref(), node_owner, "foo".into())
Ok(Delegation::new(node_identity.clone(), coin(42, DENOM))),
query_gateway_delegation(deps.as_ref(), node_identity, "foo".into())
)
}
#[test]
fn gateway_deletion_query_returns_error_if_delegation_doesnt_exist() {
let mut deps = helpers::init_contract();
let node_owner: HumanAddr = "foo".into();
let node_identity: String = "foo".into();
assert_eq!(
Err(ContractError::NoGatewayDelegationFound {
gateway_owner: node_owner.clone(),
identity: node_identity.clone(),
}),
query_gateway_delegation(deps.as_ref(), node_owner.clone(), "foo".into())
query_gateway_delegation(deps.as_ref(), node_identity.clone(), "foo".into())
);
// add delegation from a different address
gateway_delegations(&mut deps.storage, &node_owner)
gateway_delegations(&mut deps.storage, &node_identity)
.save("bar".as_bytes(), &Uint128(42))
.unwrap();
assert_eq!(
Err(ContractError::NoGatewayDelegationFound {
gateway_owner: node_owner.clone(),
identity: node_identity.clone(),
}),
query_gateway_delegation(deps.as_ref(), node_owner.clone(), "foo".into())
query_gateway_delegation(deps.as_ref(), node_identity.clone(), "foo".into())
);
// add delegation for a different node
gateway_delegations(&mut deps.storage, &"differentnode".into())
gateway_delegations(&mut deps.storage, "differentnode")
.save("foo".as_bytes(), &Uint128(42))
.unwrap();
assert_eq!(
Err(ContractError::NoGatewayDelegationFound {
gateway_owner: node_owner.clone(),
identity: node_identity.clone(),
}),
query_gateway_delegation(deps.as_ref(), node_owner.clone(), "foo".into())
query_gateway_delegation(deps.as_ref(), node_identity.clone(), "foo".into())
)
}
}
+373 -375
View File
@@ -1,6 +1,6 @@
use crate::queries;
use crate::state::{State, StateParams};
use cosmwasm_std::{Decimal, HumanAddr, Order, StdError, StdResult, Storage, Uint128};
use cosmwasm_std::{Decimal, Order, StdError, StdResult, Storage, Uint128};
use cosmwasm_storage::{
bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton,
Singleton,
@@ -8,23 +8,19 @@ use cosmwasm_storage::{
use mixnet_contract::{GatewayBond, LayerDistribution, MixNodeBond};
// storage prefixes
// all of them must be unique and presumably not be a prefix od a different one
// all of them must be unique and presumably not be a prefix of a different one
// keeping them as short as possible is also desirable as they are part of each stored key
// it's not as important for singletons, but is a nice optimisation for buckets
// the legacy prefixes can't be changed without redeploying contract or doing fancy migration
// for time being let's leave them as they are, but when we're going to make incompatible
// contract changes, let's shorten all of them
// singletons
const CONFIG_KEY: &[u8] = b"config";
const LAYER_DISTRIBUTION_KEY: &[u8] = b"layers";
// buckets
const PREFIX_MIXNODES: &[u8] = b"mixnodes";
const PREFIX_MIXNODES_OWNERS: &[u8] = b"mix-owners";
const PREFIX_GATEWAYS: &[u8] = b"gateways";
const PREFIX_GATEWAYS_OWNERS: &[u8] = b"gateway-owners";
const PREFIX_MIXNODES: &[u8] = b"mn";
const PREFIX_MIXNODES_OWNERS: &[u8] = b"mo";
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";
@@ -156,11 +152,12 @@ pub fn mixnodes_read(storage: &dyn Storage) -> ReadonlyBucket<MixNodeBond> {
bucket_read(storage, PREFIX_MIXNODES)
}
pub fn mixnodes_owners(storage: &mut dyn Storage) -> Bucket<HumanAddr> {
// owner address -> node identity
pub fn mixnodes_owners(storage: &mut dyn Storage) -> Bucket<String> {
bucket(storage, PREFIX_MIXNODES_OWNERS)
}
pub fn mixnodes_owners_read(storage: &dyn Storage) -> ReadonlyBucket<HumanAddr> {
pub fn mixnodes_owners_read(storage: &dyn Storage) -> ReadonlyBucket<String> {
bucket_read(storage, PREFIX_MIXNODES_OWNERS)
}
@@ -178,12 +175,12 @@ pub(crate) fn increase_mixnode_bond(
let reward = bond.amount[0].amount * scaled_reward_rate;
bond.amount[0].amount += reward;
mixnodes(storage).save(bond.owner.as_bytes(), &bond)
mixnodes(storage).save(bond.identity().as_bytes(), &bond)
}
pub(crate) fn increase_mix_delegated_stakes(
storage: &mut dyn Storage,
mix_owner: &HumanAddr,
mix_identity: &str,
scaled_reward_rate: Decimal,
) -> StdResult<()> {
let chunk_size = queries::DELEGATION_PAGE_MAX_LIMIT as usize;
@@ -191,7 +188,7 @@ pub(crate) fn increase_mix_delegated_stakes(
let mut chunk_start: Option<Vec<_>> = None;
loop {
// get `chunk_size` of delegations
let delegations_chunk = mix_delegations_read(storage, mix_owner)
let delegations_chunk = mix_delegations_read(storage, mix_identity)
.range(chunk_start.as_deref(), None, Order::Ascending)
.take(chunk_size)
.collect::<StdResult<Vec<_>>>()?;
@@ -216,7 +213,7 @@ 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_owner).save(&delegator_address, &new_amount)?;
mix_delegations(storage, mix_identity).save(&delegator_address, &new_amount)?;
}
}
@@ -225,7 +222,7 @@ pub(crate) fn increase_mix_delegated_stakes(
pub(crate) fn increase_gateway_delegated_stakes(
storage: &mut dyn Storage,
gateway_owner: &HumanAddr,
gateway_identity: &str,
scaled_reward_rate: Decimal,
) -> StdResult<()> {
let chunk_size = queries::DELEGATION_PAGE_MAX_LIMIT as usize;
@@ -233,7 +230,7 @@ pub(crate) fn increase_gateway_delegated_stakes(
let mut chunk_start: Option<Vec<_>> = None;
loop {
// get `chunk_size` of delegations
let delegations_chunk = gateway_delegations_read(storage, gateway_owner)
let delegations_chunk = gateway_delegations_read(storage, gateway_identity)
.range(chunk_start.as_deref(), None, Order::Ascending)
.take(chunk_size)
.collect::<StdResult<Vec<_>>>()?;
@@ -258,7 +255,7 @@ 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_owner).save(&delegator_address, &new_amount)?;
gateway_delegations(storage, gateway_identity).save(&delegator_address, &new_amount)?;
}
}
@@ -269,10 +266,10 @@ pub(crate) fn increase_gateway_delegated_stakes(
#[cfg(test)]
pub(crate) fn read_mixnode_bond(
storage: &dyn Storage,
owner: &[u8],
identity: &[u8],
) -> StdResult<cosmwasm_std::Uint128> {
let bucket = mixnodes_read(storage);
let node = bucket.load(owner)?;
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",
@@ -291,11 +288,12 @@ pub fn gateways_read(storage: &dyn Storage) -> ReadonlyBucket<GatewayBond> {
bucket_read(storage, PREFIX_GATEWAYS)
}
pub fn gateways_owners(storage: &mut dyn Storage) -> Bucket<HumanAddr> {
// owner address -> node identity
pub fn gateways_owners(storage: &mut dyn Storage) -> Bucket<String> {
bucket(storage, PREFIX_GATEWAYS_OWNERS)
}
pub fn gateways_owners_read(storage: &dyn Storage) -> ReadonlyBucket<HumanAddr> {
pub fn gateways_owners_read(storage: &dyn Storage) -> ReadonlyBucket<String> {
bucket_read(storage, PREFIX_GATEWAYS_OWNERS)
}
@@ -312,41 +310,41 @@ pub(crate) fn increase_gateway_bond(
}
let reward = bond.amount[0].amount * scaled_reward_rate;
bond.amount[0].amount += reward;
gateways(storage).save(bond.owner.as_bytes(), &bond)
gateways(storage).save(bond.identity().as_bytes(), &bond)
}
// delegation related
pub fn mix_delegations<'a>(
storage: &'a mut dyn Storage,
mix_owner: &'a HumanAddr,
mix_identity: &'a str,
) -> Bucket<'a, Uint128> {
Bucket::multilevel(storage, &[PREFIX_MIX_DELEGATION, mix_owner.as_bytes()])
Bucket::multilevel(storage, &[PREFIX_MIX_DELEGATION, mix_identity.as_bytes()])
}
pub fn mix_delegations_read<'a>(
storage: &'a dyn Storage,
mix_owner: &'a HumanAddr,
mix_identity: &'a str,
) -> ReadonlyBucket<'a, Uint128> {
ReadonlyBucket::multilevel(storage, &[PREFIX_MIX_DELEGATION, mix_owner.as_bytes()])
ReadonlyBucket::multilevel(storage, &[PREFIX_MIX_DELEGATION, mix_identity.as_bytes()])
}
pub fn gateway_delegations<'a>(
storage: &'a mut dyn Storage,
gateway_owner: &'a HumanAddr,
gateway_identity: &'a str,
) -> Bucket<'a, Uint128> {
Bucket::multilevel(
storage,
&[PREFIX_GATEWAY_DELEGATION, gateway_owner.as_bytes()],
&[PREFIX_GATEWAY_DELEGATION, gateway_identity.as_bytes()],
)
}
pub fn gateway_delegations_read<'a>(
storage: &'a dyn Storage,
gateway_owner: &'a HumanAddr,
gateway_identity: &'a str,
) -> ReadonlyBucket<'a, Uint128> {
ReadonlyBucket::multilevel(
storage,
&[PREFIX_GATEWAY_DELEGATION, gateway_owner.as_bytes()],
&[PREFIX_GATEWAY_DELEGATION, gateway_identity.as_bytes()],
)
}
@@ -354,10 +352,10 @@ pub fn gateway_delegations_read<'a>(
#[cfg(test)]
pub(crate) fn read_gateway_bond(
storage: &dyn Storage,
owner: &[u8],
identity: &[u8],
) -> StdResult<cosmwasm_std::Uint128> {
let bucket = gateways_read(storage);
let node = bucket.load(owner)?;
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",
@@ -365,344 +363,344 @@ pub(crate) fn read_gateway_bond(
}
Ok(node.amount[0].amount)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::contract::DENOM;
use crate::support::tests::helpers::{
gateway_bond_fixture, gateway_fixture, mix_node_fixture, mixnode_bond_fixture,
};
use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::{coins, Uint128};
#[test]
fn mixnode_single_read_retrieval() {
let mut storage = MockStorage::new();
let bond1 = mixnode_bond_fixture();
let bond2 = mixnode_bond_fixture();
mixnodes(&mut storage).save(b"bond1", &bond1).unwrap();
mixnodes(&mut storage).save(b"bond2", &bond2).unwrap();
let res1 = mixnodes_read(&storage).load(b"bond1").unwrap();
let res2 = mixnodes_read(&storage).load(b"bond2").unwrap();
assert_eq!(bond1, res1);
assert_eq!(bond2, res2);
}
#[test]
fn gateway_single_read_retrieval() {
let mut storage = MockStorage::new();
let bond1 = gateway_bond_fixture();
let bond2 = gateway_bond_fixture();
gateways(&mut storage).save(b"bond1", &bond1).unwrap();
gateways(&mut storage).save(b"bond2", &bond2).unwrap();
let res1 = gateways_read(&storage).load(b"bond1").unwrap();
let res2 = gateways_read(&storage).load(b"bond2").unwrap();
assert_eq!(bond1, res1);
assert_eq!(bond2, res2);
}
#[test]
fn increasing_mixnode_bond() {
let mut storage = MockStorage::new();
let node_owner = b"owner";
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
// increases the reward appropriately
let mixnode_bond = MixNodeBond {
amount: coins(1000, DENOM),
owner: std::str::from_utf8(node_owner).unwrap().into(),
mix_node: mix_node_fixture(),
};
mixnodes(&mut storage)
.save(node_owner, &mixnode_bond)
.unwrap();
increase_mixnode_bond(&mut storage, mixnode_bond, reward).unwrap();
let new_bond = read_mixnode_bond(&storage, node_owner).unwrap();
assert_eq!(Uint128(1001), new_bond);
}
#[test]
fn reading_mixnode_bond() {
let mut storage = MockStorage::new();
let node_owner = b"owner";
// produces an error if target mixnode doesn't exist
let res = read_mixnode_bond(&storage, node_owner);
assert!(res.is_err());
// returns appropriate value otherwise
let bond_value = 1000;
let mixnode_bond = MixNodeBond {
amount: coins(bond_value, DENOM),
owner: std::str::from_utf8(node_owner).unwrap().into(),
mix_node: mix_node_fixture(),
};
mixnodes(&mut storage)
.save(node_owner, &mixnode_bond)
.unwrap();
assert_eq!(
Uint128(bond_value),
read_mixnode_bond(&storage, node_owner).unwrap()
);
}
#[test]
fn increasing_gateway_bond() {
let mut storage = MockStorage::new();
let node_owner = b"owner";
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
// increases the reward appropriately
let gateway_bond = GatewayBond {
amount: coins(1000, DENOM),
owner: std::str::from_utf8(node_owner).unwrap().into(),
gateway: gateway_fixture(),
};
gateways(&mut storage)
.save(node_owner, &gateway_bond)
.unwrap();
increase_gateway_bond(&mut storage, gateway_bond, reward).unwrap();
let new_bond = read_gateway_bond(&storage, node_owner).unwrap();
assert_eq!(Uint128(1001), new_bond);
}
#[test]
fn reading_gateway_bond() {
let mut storage = MockStorage::new();
let node_owner = b"owner";
// produces an error if target mixnode doesn't exist
let res = read_gateway_bond(&storage, node_owner);
assert!(res.is_err());
// returns appropriate value otherwise
let bond_value = 1000;
let gateway_bond = GatewayBond {
amount: coins(1000, DENOM),
owner: std::str::from_utf8(node_owner).unwrap().into(),
gateway: gateway_fixture(),
};
gateways(&mut storage)
.save(node_owner, &gateway_bond)
.unwrap();
assert_eq!(
Uint128(bond_value),
read_gateway_bond(&storage, node_owner).unwrap()
);
}
#[cfg(test)]
mod increasing_mix_delegated_stakes {
use super::*;
use crate::queries::query_mixnode_delegations_paged;
use cosmwasm_std::testing::mock_dependencies;
#[test]
fn when_there_are_no_delegations() {
let mut deps = mock_dependencies(&[]);
let node_owner = HumanAddr::from("owner");
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
increase_mix_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
// there are no 'new' delegations magically added
assert!(
query_mixnode_delegations_paged(deps.as_ref(), node_owner, None, None)
.unwrap()
.delegations
.is_empty()
)
}
#[test]
fn when_there_is_a_single_delegation() {
let mut deps = mock_dependencies(&[]);
let node_owner = HumanAddr::from("owner");
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
let delegator_address = HumanAddr::from("bob");
mix_delegations(&mut deps.storage, &node_owner)
.save(delegator_address.as_bytes(), &Uint128(1000))
.unwrap();
increase_mix_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
assert_eq!(
Uint128(1001),
mix_delegations_read(&mut deps.storage, &node_owner)
.load(delegator_address.as_bytes())
.unwrap()
)
}
#[test]
fn when_there_are_multiple_delegations() {
let mut deps = mock_dependencies(&[]);
let node_owner = HumanAddr::from("owner");
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
for i in 0..100 {
let delegator_address = HumanAddr::from(format!("address{}", i));
mix_delegations(&mut deps.storage, &node_owner)
.save(delegator_address.as_bytes(), &Uint128(1000))
.unwrap();
}
increase_mix_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
for i in 0..100 {
let delegator_address = HumanAddr::from(format!("address{}", i));
assert_eq!(
Uint128(1001),
mix_delegations_read(&mut deps.storage, &node_owner)
.load(delegator_address.as_bytes())
.unwrap()
)
}
}
#[test]
fn when_there_are_more_delegations_than_page_size() {
let mut deps = mock_dependencies(&[]);
let node_owner = HumanAddr::from("owner");
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
let delegator_address = HumanAddr::from(format!("address{}", i));
mix_delegations(&mut deps.storage, &node_owner)
.save(delegator_address.as_bytes(), &Uint128(1000))
.unwrap();
}
increase_mix_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
let delegator_address = HumanAddr::from(format!("address{}", i));
assert_eq!(
Uint128(1001),
mix_delegations_read(&mut deps.storage, &node_owner)
.load(delegator_address.as_bytes())
.unwrap()
)
}
}
}
#[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_owner = HumanAddr::from("owner");
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
increase_gateway_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
// there are no 'new' delegations magically added
assert!(
query_gateway_delegations_paged(deps.as_ref(), node_owner, None, None)
.unwrap()
.delegations
.is_empty()
)
}
#[test]
fn when_there_is_a_single_delegation() {
let mut deps = mock_dependencies(&[]);
let node_owner = HumanAddr::from("owner");
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
let delegator_address = HumanAddr::from("bob");
gateway_delegations(&mut deps.storage, &node_owner)
.save(delegator_address.as_bytes(), &Uint128(1000))
.unwrap();
increase_gateway_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
assert_eq!(
Uint128(1001),
gateway_delegations_read(&mut deps.storage, &node_owner)
.load(delegator_address.as_bytes())
.unwrap()
)
}
#[test]
fn when_there_are_multiple_delegations() {
let mut deps = mock_dependencies(&[]);
let node_owner = HumanAddr::from("owner");
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
for i in 0..100 {
let delegator_address = HumanAddr::from(format!("address{}", i));
gateway_delegations(&mut deps.storage, &node_owner)
.save(delegator_address.as_bytes(), &Uint128(1000))
.unwrap();
}
increase_gateway_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
for i in 0..100 {
let delegator_address = HumanAddr::from(format!("address{}", i));
assert_eq!(
Uint128(1001),
gateway_delegations_read(&mut deps.storage, &node_owner)
.load(delegator_address.as_bytes())
.unwrap()
)
}
}
#[test]
fn when_there_are_more_delegations_than_page_size() {
let mut deps = mock_dependencies(&[]);
let node_owner = HumanAddr::from("owner");
// 0.001
let reward = Decimal::from_ratio(1u128, 1000u128);
for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
let delegator_address = HumanAddr::from(format!("address{}", i));
gateway_delegations(&mut deps.storage, &node_owner)
.save(delegator_address.as_bytes(), &Uint128(1000))
.unwrap();
}
increase_gateway_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
let delegator_address = HumanAddr::from(format!("address{}", i));
assert_eq!(
Uint128(1001),
gateway_delegations_read(&mut deps.storage, &node_owner)
.load(delegator_address.as_bytes())
.unwrap()
)
}
}
}
}
//
// #[cfg(test)]
// mod tests {
// use super::*;
// use crate::contract::DENOM;
// use crate::support::tests::helpers::{
// gateway_bond_fixture, gateway_fixture, mix_node_fixture, mixnode_bond_fixture,
// };
// use cosmwasm_std::testing::MockStorage;
// use cosmwasm_std::{coins, Uint128};
//
// #[test]
// fn mixnode_single_read_retrieval() {
// let mut storage = MockStorage::new();
// let bond1 = mixnode_bond_fixture();
// let bond2 = mixnode_bond_fixture();
// mixnodes(&mut storage).save(b"bond1", &bond1).unwrap();
// mixnodes(&mut storage).save(b"bond2", &bond2).unwrap();
//
// let res1 = mixnodes_read(&storage).load(b"bond1").unwrap();
// let res2 = mixnodes_read(&storage).load(b"bond2").unwrap();
// assert_eq!(bond1, res1);
// assert_eq!(bond2, res2);
// }
//
// #[test]
// fn gateway_single_read_retrieval() {
// let mut storage = MockStorage::new();
// let bond1 = gateway_bond_fixture();
// let bond2 = gateway_bond_fixture();
// gateways(&mut storage).save(b"bond1", &bond1).unwrap();
// gateways(&mut storage).save(b"bond2", &bond2).unwrap();
//
// let res1 = gateways_read(&storage).load(b"bond1").unwrap();
// let res2 = gateways_read(&storage).load(b"bond2").unwrap();
// assert_eq!(bond1, res1);
// assert_eq!(bond2, res2);
// }
//
// #[test]
// fn increasing_mixnode_bond() {
// let mut storage = MockStorage::new();
// let node_owner = b"owner";
// // 0.001
// let reward = Decimal::from_ratio(1u128, 1000u128);
//
// // increases the reward appropriately
// let mixnode_bond = MixNodeBond {
// amount: coins(1000, DENOM),
// owner: std::str::from_utf8(node_owner).unwrap().into(),
// mix_node: mix_node_fixture(),
// };
//
// mixnodes(&mut storage)
// .save(node_owner, &mixnode_bond)
// .unwrap();
//
// increase_mixnode_bond(&mut storage, mixnode_bond, reward).unwrap();
// let new_bond = read_mixnode_bond(&storage, node_owner).unwrap();
// assert_eq!(Uint128(1001), new_bond);
// }
//
// #[test]
// fn reading_mixnode_bond() {
// let mut storage = MockStorage::new();
// let node_owner = b"owner";
//
// // produces an error if target mixnode doesn't exist
// let res = read_mixnode_bond(&storage, node_owner);
// assert!(res.is_err());
//
// // returns appropriate value otherwise
// let bond_value = 1000;
//
// let mixnode_bond = MixNodeBond {
// amount: coins(bond_value, DENOM),
// owner: std::str::from_utf8(node_owner).unwrap().into(),
// mix_node: mix_node_fixture(),
// };
//
// mixnodes(&mut storage)
// .save(node_owner, &mixnode_bond)
// .unwrap();
//
// assert_eq!(
// Uint128(bond_value),
// read_mixnode_bond(&storage, node_owner).unwrap()
// );
// }
//
// #[test]
// fn increasing_gateway_bond() {
// let mut storage = MockStorage::new();
// let node_owner = b"owner";
// // 0.001
// let reward = Decimal::from_ratio(1u128, 1000u128);
//
// // increases the reward appropriately
// let gateway_bond = GatewayBond {
// amount: coins(1000, DENOM),
// owner: std::str::from_utf8(node_owner).unwrap().into(),
// gateway: gateway_fixture(),
// };
//
// gateways(&mut storage)
// .save(node_owner, &gateway_bond)
// .unwrap();
//
// increase_gateway_bond(&mut storage, gateway_bond, reward).unwrap();
// let new_bond = read_gateway_bond(&storage, node_owner).unwrap();
// assert_eq!(Uint128(1001), new_bond);
// }
//
// #[test]
// fn reading_gateway_bond() {
// let mut storage = MockStorage::new();
// let node_owner = b"owner";
//
// // produces an error if target mixnode doesn't exist
// let res = read_gateway_bond(&storage, node_owner);
// assert!(res.is_err());
//
// // returns appropriate value otherwise
// let bond_value = 1000;
//
// let gateway_bond = GatewayBond {
// amount: coins(1000, DENOM),
// owner: std::str::from_utf8(node_owner).unwrap().into(),
// gateway: gateway_fixture(),
// };
//
// gateways(&mut storage)
// .save(node_owner, &gateway_bond)
// .unwrap();
//
// assert_eq!(
// Uint128(bond_value),
// read_gateway_bond(&storage, node_owner).unwrap()
// );
// }
//
// #[cfg(test)]
// mod increasing_mix_delegated_stakes {
// use super::*;
// use crate::queries::query_mixnode_delegations_paged;
// use cosmwasm_std::testing::mock_dependencies;
//
// #[test]
// fn when_there_are_no_delegations() {
// let mut deps = mock_dependencies(&[]);
// let node_owner = HumanAddr::from("owner");
// // 0.001
// let reward = Decimal::from_ratio(1u128, 1000u128);
//
// increase_mix_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
//
// // there are no 'new' delegations magically added
// assert!(
// query_mixnode_delegations_paged(deps.as_ref(), node_owner, None, None)
// .unwrap()
// .delegations
// .is_empty()
// )
// }
//
// #[test]
// fn when_there_is_a_single_delegation() {
// let mut deps = mock_dependencies(&[]);
// let node_owner = HumanAddr::from("owner");
// // 0.001
// let reward = Decimal::from_ratio(1u128, 1000u128);
//
// let delegator_address = HumanAddr::from("bob");
// mix_delegations(&mut deps.storage, &node_owner)
// .save(delegator_address.as_bytes(), &Uint128(1000))
// .unwrap();
//
// increase_mix_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
// assert_eq!(
// Uint128(1001),
// mix_delegations_read(&mut deps.storage, &node_owner)
// .load(delegator_address.as_bytes())
// .unwrap()
// )
// }
//
// #[test]
// fn when_there_are_multiple_delegations() {
// let mut deps = mock_dependencies(&[]);
// let node_owner = HumanAddr::from("owner");
// // 0.001
// let reward = Decimal::from_ratio(1u128, 1000u128);
//
// for i in 0..100 {
// let delegator_address = HumanAddr::from(format!("address{}", i));
// mix_delegations(&mut deps.storage, &node_owner)
// .save(delegator_address.as_bytes(), &Uint128(1000))
// .unwrap();
// }
//
// increase_mix_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
//
// for i in 0..100 {
// let delegator_address = HumanAddr::from(format!("address{}", i));
// assert_eq!(
// Uint128(1001),
// mix_delegations_read(&mut deps.storage, &node_owner)
// .load(delegator_address.as_bytes())
// .unwrap()
// )
// }
// }
//
// #[test]
// fn when_there_are_more_delegations_than_page_size() {
// let mut deps = mock_dependencies(&[]);
// let node_owner = HumanAddr::from("owner");
// // 0.001
// let reward = Decimal::from_ratio(1u128, 1000u128);
//
// for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
// let delegator_address = HumanAddr::from(format!("address{}", i));
// mix_delegations(&mut deps.storage, &node_owner)
// .save(delegator_address.as_bytes(), &Uint128(1000))
// .unwrap();
// }
//
// increase_mix_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
//
// for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
// let delegator_address = HumanAddr::from(format!("address{}", i));
// assert_eq!(
// Uint128(1001),
// mix_delegations_read(&mut deps.storage, &node_owner)
// .load(delegator_address.as_bytes())
// .unwrap()
// )
// }
// }
// }
//
// #[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_owner = HumanAddr::from("owner");
// // 0.001
// let reward = Decimal::from_ratio(1u128, 1000u128);
//
// increase_gateway_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
//
// // there are no 'new' delegations magically added
// assert!(
// query_gateway_delegations_paged(deps.as_ref(), node_owner, None, None)
// .unwrap()
// .delegations
// .is_empty()
// )
// }
//
// #[test]
// fn when_there_is_a_single_delegation() {
// let mut deps = mock_dependencies(&[]);
// let node_owner = HumanAddr::from("owner");
// // 0.001
// let reward = Decimal::from_ratio(1u128, 1000u128);
//
// let delegator_address = HumanAddr::from("bob");
// gateway_delegations(&mut deps.storage, &node_owner)
// .save(delegator_address.as_bytes(), &Uint128(1000))
// .unwrap();
//
// increase_gateway_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
// assert_eq!(
// Uint128(1001),
// gateway_delegations_read(&mut deps.storage, &node_owner)
// .load(delegator_address.as_bytes())
// .unwrap()
// )
// }
//
// #[test]
// fn when_there_are_multiple_delegations() {
// let mut deps = mock_dependencies(&[]);
// let node_owner = HumanAddr::from("owner");
// // 0.001
// let reward = Decimal::from_ratio(1u128, 1000u128);
//
// for i in 0..100 {
// let delegator_address = HumanAddr::from(format!("address{}", i));
// gateway_delegations(&mut deps.storage, &node_owner)
// .save(delegator_address.as_bytes(), &Uint128(1000))
// .unwrap();
// }
//
// increase_gateway_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
//
// for i in 0..100 {
// let delegator_address = HumanAddr::from(format!("address{}", i));
// assert_eq!(
// Uint128(1001),
// gateway_delegations_read(&mut deps.storage, &node_owner)
// .load(delegator_address.as_bytes())
// .unwrap()
// )
// }
// }
//
// #[test]
// fn when_there_are_more_delegations_than_page_size() {
// let mut deps = mock_dependencies(&[]);
// let node_owner = HumanAddr::from("owner");
// // 0.001
// let reward = Decimal::from_ratio(1u128, 1000u128);
//
// for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
// let delegator_address = HumanAddr::from(format!("address{}", i));
// gateway_delegations(&mut deps.storage, &node_owner)
// .save(delegator_address.as_bytes(), &Uint128(1000))
// .unwrap();
// }
//
// increase_gateway_delegated_stakes(&mut deps.storage, &node_owner, reward).unwrap();
//
// for i in 0..queries::DELEGATION_PAGE_MAX_LIMIT * 10 {
// let delegator_address = HumanAddr::from(format!("address{}", i));
// assert_eq!(
// Uint128(1001),
// gateway_delegations_read(&mut deps.storage, &node_owner)
// .load(delegator_address.as_bytes())
// .unwrap()
// )
// }
// }
// }
// }
+27 -9
View File
@@ -1,9 +1,9 @@
#[cfg(test)]
pub mod helpers {
use super::*;
use crate::contract::init;
use crate::contract::query;
use crate::contract::DENOM;
use crate::contract::{init, INITIAL_MIXNODE_BOND};
use crate::msg::InitMsg;
use crate::msg::QueryMsg;
use crate::transactions::{try_add_gateway, try_add_mixnode};
@@ -24,20 +24,22 @@ pub mod helpers {
};
pub fn add_mixnode(
pubkey: &str,
sender: &str,
stake: Vec<Coin>,
deps: &mut OwnedDeps<MockStorage, MockApi, MockQuerier>,
) {
let info = mock_info(pubkey, &stake);
) -> String {
let info = mock_info(sender, &stake);
let key = format!("{}mixnode", sender);
try_add_mixnode(
deps.as_mut(),
info,
MixNode {
identity_key: format!("{}mixnode", pubkey),
identity_key: key.clone(),
..helpers::mix_node_fixture()
},
)
.unwrap();
key
}
pub fn get_mix_nodes(
@@ -58,20 +60,22 @@ pub mod helpers {
}
pub fn add_gateway(
pubkey: &str,
sender: &str,
stake: Vec<Coin>,
deps: &mut OwnedDeps<MockStorage, MockApi, MockQuerier>,
) {
let info = mock_info(pubkey, &stake);
) -> String {
let info = mock_info(sender, &stake);
let key = format!("{}gateway", sender);
try_add_gateway(
deps.as_mut(),
info,
Gateway {
identity_key: format!("{}gateway", pubkey),
identity_key: key.clone(),
..helpers::gateway_fixture()
},
)
.unwrap();
key
}
pub fn get_gateways(
@@ -153,4 +157,18 @@ pub mod helpers {
let querier = deps.as_ref().querier;
vec![querier.query_balance(address, DENOM).unwrap()]
}
pub fn good_mixnode_bond() -> Vec<Coin> {
vec![Coin {
denom: DENOM.to_string(),
amount: INITIAL_MIXNODE_BOND,
}]
}
pub fn good_gateway_bond() -> Vec<Coin> {
vec![Coin {
denom: DENOM.to_string(),
amount: INITIAL_MIXNODE_BOND,
}]
}
}
File diff suppressed because it is too large Load Diff