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:
committed by
GitHub
parent
7b02026476
commit
164c8ad05c
+1
-1
@@ -56,4 +56,4 @@ default-members = [
|
||||
"network-monitor",
|
||||
]
|
||||
|
||||
exclude = ["explorer"]
|
||||
exclude = ["explorer", "contracts"]
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user