Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d2e911625 | |||
| 667e41d4ec | |||
| c3d9b8b21b | |||
| 99f2380346 | |||
| c926da46c7 | |||
| fb178d9767 | |||
| 8c6b3567a6 | |||
| b803b2f667 | |||
| 542e9baacf | |||
| 7e9187ced6 | |||
| d636fa71fb | |||
| d35c762180 | |||
| 440c1e5fa3 | |||
| 1216eb94c5 | |||
| 2f87481348 | |||
| a86cac5b85 | |||
| aa9609ec6d |
Generated
+2
@@ -3212,6 +3212,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"nym-coconut-bandwidth-contract-common",
|
"nym-coconut-bandwidth-contract-common",
|
||||||
"nym-coconut-dkg-common",
|
"nym-coconut-dkg-common",
|
||||||
|
"nym-contracts-common",
|
||||||
"nym-mixnet-contract-common",
|
"nym-mixnet-contract-common",
|
||||||
"nym-multisig-contract-common",
|
"nym-multisig-contract-common",
|
||||||
"nym-network-defaults",
|
"nym-network-defaults",
|
||||||
@@ -3507,6 +3508,7 @@ dependencies = [
|
|||||||
"mixnode-common",
|
"mixnode-common",
|
||||||
"nym-bin-common",
|
"nym-bin-common",
|
||||||
"nym-config",
|
"nym-config",
|
||||||
|
"nym-contracts-common",
|
||||||
"nym-crypto",
|
"nym-crypto",
|
||||||
"nym-nonexhaustive-delayqueue",
|
"nym-nonexhaustive-delayqueue",
|
||||||
"nym-pemstore",
|
"nym-pemstore",
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ pub use cosmrs::tendermint::Time as TendermintTime;
|
|||||||
pub use cosmrs::tx::{self, Gas};
|
pub use cosmrs::tx::{self, Gas};
|
||||||
pub use cosmrs::Coin as CosmosCoin;
|
pub use cosmrs::Coin as CosmosCoin;
|
||||||
pub use cosmrs::{bip32, AccountId, Decimal, Denom};
|
pub use cosmrs::{bip32, AccountId, Decimal, Denom};
|
||||||
|
use cosmwasm_std::Addr;
|
||||||
pub use cosmwasm_std::Coin as CosmWasmCoin;
|
pub use cosmwasm_std::Coin as CosmWasmCoin;
|
||||||
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
|
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
|
||||||
pub use signing_client::Client as SigningNyxdClient;
|
pub use signing_client::Client as SigningNyxdClient;
|
||||||
@@ -369,6 +370,15 @@ where
|
|||||||
&self.client_address.as_ref().unwrap()[0]
|
&self.client_address.as_ref().unwrap()[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cw_address(&self) -> Addr
|
||||||
|
where
|
||||||
|
C: SigningCosmWasmClient,
|
||||||
|
{
|
||||||
|
// the call to unchecked is fine here as we're converting directly from `AccountId`
|
||||||
|
// which must have been a valid bech32 address
|
||||||
|
Addr::unchecked(self.address().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn signer(&self) -> &DirectSecp256k1HdWallet
|
pub fn signer(&self) -> &DirectSecp256k1HdWallet
|
||||||
where
|
where
|
||||||
C: SigningCosmWasmClient,
|
C: SigningCosmWasmClient,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use crate::nyxd::error::NyxdError;
|
|||||||
use crate::nyxd::NyxdClient;
|
use crate::nyxd::NyxdClient;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cosmrs::AccountId;
|
use cosmrs::AccountId;
|
||||||
|
use nym_contracts_common::signing::Nonce;
|
||||||
use nym_mixnet_contract_common::delegation::{MixNodeDelegationResponse, OwnerProxySubKey};
|
use nym_mixnet_contract_common::delegation::{MixNodeDelegationResponse, OwnerProxySubKey};
|
||||||
use nym_mixnet_contract_common::families::Family;
|
use nym_mixnet_contract_common::families::Family;
|
||||||
use nym_mixnet_contract_common::mixnode::{
|
use nym_mixnet_contract_common::mixnode::{
|
||||||
@@ -390,6 +391,13 @@ pub trait MixnetQueryClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
|
||||||
|
self.query_mixnet_contract(MixnetQueryMsg::GetSigningNonce {
|
||||||
|
address: address.to_string(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_node_family_by_label(&self, label: &str) -> Result<Option<Family>, NyxdError> {
|
async fn get_node_family_by_label(&self, label: &str) -> Result<Option<Family>, NyxdError> {
|
||||||
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel {
|
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel {
|
||||||
label: label.to_string(),
|
label: label.to_string(),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use crate::nyxd::error::NyxdError;
|
|||||||
use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient};
|
use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cosmrs::AccountId;
|
use cosmrs::AccountId;
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||||
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
|
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
|
||||||
use nym_mixnet_contract_common::{
|
use nym_mixnet_contract_common::{
|
||||||
@@ -290,7 +291,7 @@ pub trait MixnetSigningClient {
|
|||||||
&self,
|
&self,
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
@@ -311,7 +312,7 @@ pub trait MixnetSigningClient {
|
|||||||
owner: AccountId,
|
owner: AccountId,
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
@@ -442,7 +443,7 @@ pub trait MixnetSigningClient {
|
|||||||
async fn bond_gateway(
|
async fn bond_gateway(
|
||||||
&self,
|
&self,
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
@@ -461,7 +462,7 @@ pub trait MixnetSigningClient {
|
|||||||
&self,
|
&self,
|
||||||
owner: AccountId,
|
owner: AccountId,
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use crate::nyxd::cosmwasm_client::types::ExecuteResult;
|
|||||||
use crate::nyxd::error::NyxdError;
|
use crate::nyxd::error::NyxdError;
|
||||||
use crate::nyxd::{Coin, Fee, NyxdClient};
|
use crate::nyxd::{Coin, Fee, NyxdClient};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||||
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
|
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
|
||||||
use nym_vesting_contract_common::messages::{
|
use nym_vesting_contract_common::messages::{
|
||||||
@@ -43,7 +44,7 @@ pub trait VestingSigningClient {
|
|||||||
async fn vesting_bond_gateway(
|
async fn vesting_bond_gateway(
|
||||||
&self,
|
&self,
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: &str,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError>;
|
||||||
@@ -61,7 +62,7 @@ pub trait VestingSigningClient {
|
|||||||
&self,
|
&self,
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: &str,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError>;
|
||||||
@@ -208,14 +209,14 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
|
|||||||
async fn vesting_bond_gateway(
|
async fn vesting_bond_gateway(
|
||||||
&self,
|
&self,
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: &str,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||||
let req = VestingExecuteMsg::BondGateway {
|
let req = VestingExecuteMsg::BondGateway {
|
||||||
gateway,
|
gateway,
|
||||||
owner_signature: owner_signature.to_string(),
|
owner_signature,
|
||||||
amount: pledge.into(),
|
amount: pledge.into(),
|
||||||
};
|
};
|
||||||
self.client
|
self.client
|
||||||
@@ -272,7 +273,7 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
|
|||||||
&self,
|
&self,
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: &str,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
@@ -281,7 +282,7 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
|
|||||||
VestingExecuteMsg::BondMixnode {
|
VestingExecuteMsg::BondMixnode {
|
||||||
mix_node,
|
mix_node,
|
||||||
cost_params,
|
cost_params,
|
||||||
owner_signature: owner_signature.to_string(),
|
owner_signature,
|
||||||
amount: pledge.into(),
|
amount: pledge.into(),
|
||||||
},
|
},
|
||||||
vec![],
|
vec![],
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ cosmwasm-std = { version = "1.0.0" }
|
|||||||
|
|
||||||
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
|
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
|
||||||
nym-network-defaults = { path = "../network-defaults" }
|
nym-network-defaults = { path = "../network-defaults" }
|
||||||
|
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common" }
|
||||||
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
||||||
nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" }
|
nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" }
|
||||||
nym-coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
nym-coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use cosmrs::AccountId;
|
||||||
|
use cosmwasm_std::{Addr, Coin as CosmWasmCoin, Decimal};
|
||||||
|
use log::error;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use cosmwasm_std::{Coin as CosmWasmCoin, Decimal};
|
|
||||||
use log::error;
|
|
||||||
use validator_client::nyxd::Coin;
|
use validator_client::nyxd::Coin;
|
||||||
|
|
||||||
|
// TODO: perhaps it should be moved to some global common crate?
|
||||||
|
pub fn account_id_to_cw_addr(account_id: &AccountId) -> Addr {
|
||||||
|
// the call to unchecked is fine here as we're converting directly from `AccountId`
|
||||||
|
// which must have been a valid bech32 address
|
||||||
|
Addr::unchecked(account_id.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pretty_coin(coin: &Coin) -> String {
|
pub fn pretty_coin(coin: &Coin) -> String {
|
||||||
let amount = Decimal::from_ratio(coin.amount, 1_000_000u128);
|
let amount = Decimal::from_ratio(coin.amount, 1_000_000u128);
|
||||||
let denom = if coin.denom.starts_with('u') {
|
let denom = if coin.denom.starts_with('u') {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_mixnet_contract_common::Coin;
|
use nym_mixnet_contract_common::Coin;
|
||||||
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
||||||
use validator_client::nyxd::traits::MixnetSigningClient;
|
use validator_client::nyxd::traits::MixnetSigningClient;
|
||||||
@@ -14,7 +15,7 @@ pub struct Args {
|
|||||||
pub host: String,
|
pub host: String,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub signature: String,
|
pub signature: MessageSignature,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub mix_port: Option<u16>,
|
pub mix_port: Option<u16>,
|
||||||
|
|||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::context::SigningClient;
|
||||||
|
use crate::utils::account_id_to_cw_addr;
|
||||||
|
use clap::Parser;
|
||||||
|
use cosmwasm_std::Coin;
|
||||||
|
use nym_mixnet_contract_common::construct_gateway_bonding_sign_payload;
|
||||||
|
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
||||||
|
use validator_client::nyxd::traits::MixnetQueryClient;
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct Args {
|
||||||
|
#[clap(long)]
|
||||||
|
pub host: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub mix_port: Option<u16>,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub clients_port: Option<u16>,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub location: Option<String>,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub sphinx_key: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub identity_key: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub version: String,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||||
|
)]
|
||||||
|
pub amount: u128,
|
||||||
|
|
||||||
|
/// Indicates whether the gateway is going to get bonded via a vesting account
|
||||||
|
#[arg(long)]
|
||||||
|
pub with_vesting_account: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_payload(args: Args, client: SigningClient) {
|
||||||
|
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||||
|
|
||||||
|
let gateway = nym_mixnet_contract_common::Gateway {
|
||||||
|
host: args.host,
|
||||||
|
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
|
||||||
|
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
|
||||||
|
location: args
|
||||||
|
.location
|
||||||
|
.unwrap_or_else(|| "secret gateway location".to_owned()),
|
||||||
|
sphinx_key: args.sphinx_key,
|
||||||
|
identity_key: args.identity_key,
|
||||||
|
version: args.version,
|
||||||
|
};
|
||||||
|
|
||||||
|
let coin = Coin::new(args.amount, denom);
|
||||||
|
|
||||||
|
let nonce = match client.get_signing_nonce(client.address()).await {
|
||||||
|
Ok(nonce) => nonce,
|
||||||
|
Err(err) => {
|
||||||
|
eprint!(
|
||||||
|
"failed to query for the signing nonce of {}: {err}",
|
||||||
|
client.address()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let address = account_id_to_cw_addr(client.address());
|
||||||
|
let proxy = if args.with_vesting_account {
|
||||||
|
Some(account_id_to_cw_addr(client.vesting_contract_address()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let payload = construct_gateway_bonding_sign_payload(nonce, address, proxy, coin, gateway);
|
||||||
|
println!("{}", payload.to_base58_string().unwrap())
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
use clap::{Args, Subcommand};
|
use clap::{Args, Subcommand};
|
||||||
|
|
||||||
pub mod bond_gateway;
|
pub mod bond_gateway;
|
||||||
|
pub mod gateway_bonding_sign_payload;
|
||||||
pub mod unbond_gateway;
|
pub mod unbond_gateway;
|
||||||
pub mod vesting_bond_gateway;
|
pub mod vesting_bond_gateway;
|
||||||
pub mod vesting_unbond_gateway;
|
pub mod vesting_unbond_gateway;
|
||||||
@@ -25,4 +26,6 @@ pub enum MixnetOperatorsGatewayCommands {
|
|||||||
VestingBond(vesting_bond_gateway::Args),
|
VestingBond(vesting_bond_gateway::Args),
|
||||||
/// Unbound from a gateway (when originally using locked tokens)
|
/// Unbound from a gateway (when originally using locked tokens)
|
||||||
VestingUnbound(vesting_unbond_gateway::Args),
|
VestingUnbound(vesting_unbond_gateway::Args),
|
||||||
|
/// Create base58-encoded payload required for producing valid bonding signature.
|
||||||
|
CreateGatewayBondingSignPayload(gateway_bonding_sign_payload::Args),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_mixnet_contract_common::{Coin, Gateway};
|
use nym_mixnet_contract_common::{Coin, Gateway};
|
||||||
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
||||||
use validator_client::nyxd::VestingSigningClient;
|
use validator_client::nyxd::VestingSigningClient;
|
||||||
@@ -14,7 +15,7 @@ pub struct Args {
|
|||||||
pub host: String,
|
pub host: String,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub signature: String,
|
pub signature: MessageSignature,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub mix_port: Option<u16>,
|
pub mix_port: Option<u16>,
|
||||||
@@ -68,7 +69,7 @@ pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str
|
|||||||
let coin = Coin::new(args.amount, denom);
|
let coin = Coin::new(args.amount, denom);
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.vesting_bond_gateway(gateway, &args.signature, coin.into(), None)
|
.vesting_bond_gateway(gateway, args.signature, coin.into(), None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to bond gateway!");
|
.expect("failed to bond gateway!");
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use clap::Parser;
|
|||||||
use cosmwasm_std::Uint128;
|
use cosmwasm_std::Uint128;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_mixnet_contract_common::{Coin, MixNodeCostParams, Percent};
|
use nym_mixnet_contract_common::{Coin, MixNodeCostParams, Percent};
|
||||||
use nym_network_defaults::{
|
use nym_network_defaults::{
|
||||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||||
@@ -20,7 +21,7 @@ pub struct Args {
|
|||||||
pub host: String,
|
pub host: String,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub signature: String,
|
pub signature: MessageSignature,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub mix_port: Option<u16>,
|
pub mix_port: Option<u16>,
|
||||||
|
|||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::context::SigningClient;
|
||||||
|
use crate::utils::account_id_to_cw_addr;
|
||||||
|
use clap::Parser;
|
||||||
|
use cosmwasm_std::{Coin, Uint128};
|
||||||
|
use nym_contracts_common::Percent;
|
||||||
|
use nym_mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNodeCostParams};
|
||||||
|
use nym_network_defaults::{
|
||||||
|
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||||
|
};
|
||||||
|
use validator_client::nyxd::traits::MixnetQueryClient;
|
||||||
|
use validator_client::nyxd::CosmWasmCoin;
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct Args {
|
||||||
|
#[clap(long)]
|
||||||
|
pub host: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub mix_port: Option<u16>,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub verloc_port: Option<u16>,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub http_api_port: Option<u16>,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub sphinx_key: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub identity_key: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub version: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub profit_margin_percent: Option<u8>,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||||
|
)]
|
||||||
|
pub interval_operating_cost: Option<u128>,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||||
|
)]
|
||||||
|
pub amount: u128,
|
||||||
|
|
||||||
|
/// Indicates whether the mixnode is going to get bonded via a vesting account
|
||||||
|
#[arg(long)]
|
||||||
|
pub with_vesting_account: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_payload(args: Args, client: SigningClient) {
|
||||||
|
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||||
|
|
||||||
|
let mixnode = nym_mixnet_contract_common::MixNode {
|
||||||
|
host: args.host,
|
||||||
|
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
|
||||||
|
verloc_port: args.verloc_port.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT),
|
||||||
|
http_api_port: args
|
||||||
|
.http_api_port
|
||||||
|
.unwrap_or(DEFAULT_HTTP_API_LISTENING_PORT),
|
||||||
|
sphinx_key: args.sphinx_key,
|
||||||
|
identity_key: args.identity_key,
|
||||||
|
version: args.version,
|
||||||
|
};
|
||||||
|
|
||||||
|
let coin = Coin::new(args.amount, denom);
|
||||||
|
|
||||||
|
let cost_params = MixNodeCostParams {
|
||||||
|
profit_margin_percent: Percent::from_percentage_value(
|
||||||
|
args.profit_margin_percent.unwrap_or(10) as u64,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
interval_operating_cost: CosmWasmCoin {
|
||||||
|
denom: denom.into(),
|
||||||
|
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let nonce = match client.get_signing_nonce(client.address()).await {
|
||||||
|
Ok(nonce) => nonce,
|
||||||
|
Err(err) => {
|
||||||
|
eprint!(
|
||||||
|
"failed to query for the signing nonce of {}: {err}",
|
||||||
|
client.address()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let address = account_id_to_cw_addr(client.address());
|
||||||
|
let proxy = if args.with_vesting_account {
|
||||||
|
Some(account_id_to_cw_addr(client.vesting_contract_address()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let payload =
|
||||||
|
construct_mixnode_bonding_sign_payload(nonce, address, proxy, coin, mixnode, cost_params);
|
||||||
|
println!("{}", payload.to_base58_string().unwrap())
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ use clap::{Args, Subcommand};
|
|||||||
|
|
||||||
pub mod bond_mixnode;
|
pub mod bond_mixnode;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
|
pub mod mixnode_bonding_sign_payload;
|
||||||
pub mod rewards;
|
pub mod rewards;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod unbond_mixnode;
|
pub mod unbond_mixnode;
|
||||||
@@ -34,4 +35,6 @@ pub enum MixnetOperatorsMixnodeCommands {
|
|||||||
BondVesting(vesting_bond_mixnode::Args),
|
BondVesting(vesting_bond_mixnode::Args),
|
||||||
/// Unbound from a mixnode (when originally using locked tokens)
|
/// Unbound from a mixnode (when originally using locked tokens)
|
||||||
UnboundVesting(vesting_unbond_mixnode::Args),
|
UnboundVesting(vesting_unbond_mixnode::Args),
|
||||||
|
/// Create base58-encoded payload required for producing valid bonding signature.
|
||||||
|
CreateMixnodeBondingSignPayload(mixnode_bonding_sign_payload::Args),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::context::SigningClient;
|
use crate::context::SigningClient;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cosmwasm_std::Uint128;
|
use cosmwasm_std::Uint128;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_mixnet_contract_common::{Coin, MixNodeCostParams};
|
use nym_mixnet_contract_common::{Coin, MixNodeCostParams};
|
||||||
use nym_mixnet_contract_common::{MixNode, Percent};
|
use nym_mixnet_contract_common::{MixNode, Percent};
|
||||||
use nym_network_defaults::{
|
use nym_network_defaults::{
|
||||||
@@ -18,7 +19,7 @@ pub struct Args {
|
|||||||
pub host: String,
|
pub host: String,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub signature: String,
|
pub signature: MessageSignature,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub mix_port: Option<u16>,
|
pub mix_port: Option<u16>,
|
||||||
@@ -95,7 +96,7 @@ pub async fn vesting_bond_mixnode(client: SigningClient, args: Args, denom: &str
|
|||||||
};
|
};
|
||||||
|
|
||||||
let res = client
|
let res = client
|
||||||
.vesting_bond_mixnode(mixnode, cost_params, &args.signature, coin.into(), None)
|
.vesting_bond_mixnode(mixnode, cost_params, args.signature, coin.into(), None)
|
||||||
.await
|
.await
|
||||||
.expect("failed to bond vesting mixnode!");
|
.expect("failed to bond vesting mixnode!");
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
pub mod dealings;
|
pub mod dealings;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
pub mod signing;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
|||||||
@@ -0,0 +1,259 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use cosmwasm_std::{from_slice, to_vec, Addr, Coin, MessageInfo, StdResult};
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::str::FromStr;
|
||||||
|
pub use verifier::Verifier;
|
||||||
|
|
||||||
|
pub mod verifier;
|
||||||
|
|
||||||
|
pub type Nonce = u32;
|
||||||
|
|
||||||
|
// define this type explicitly for [hopefully] better usability
|
||||||
|
// (so you wouldn't need to worry about whether you should use bytes, bs58, etc.)
|
||||||
|
#[derive(Clone, Debug, PartialEq, JsonSchema)]
|
||||||
|
pub struct MessageSignature(Vec<u8>);
|
||||||
|
|
||||||
|
impl MessageSignature {
|
||||||
|
pub fn as_bs58_string(&self) -> String {
|
||||||
|
bs58::encode(&self.0).into_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a [u8]> for MessageSignature {
|
||||||
|
fn from(value: &'a [u8]) -> Self {
|
||||||
|
MessageSignature(value.to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for MessageSignature {
|
||||||
|
fn from(value: Vec<u8>) -> Self {
|
||||||
|
MessageSignature(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a str> for MessageSignature {
|
||||||
|
type Error = bs58::decode::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||||
|
Ok(MessageSignature(bs58::decode(value).into_vec()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for MessageSignature {
|
||||||
|
type Error = bs58::decode::Error;
|
||||||
|
|
||||||
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
Self::try_from(value.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for MessageSignature {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for MessageSignature {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let inner = String::deserialize(deserializer)?;
|
||||||
|
let bytes = bs58::decode(inner).into_vec().map_err(de::Error::custom)?;
|
||||||
|
Ok(MessageSignature(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for MessageSignature {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let bs58_encoded = self.as_bs58_string();
|
||||||
|
bs58_encoded.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for MessageSignature {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.as_bs58_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for MessageSignature {
|
||||||
|
type Err = bs58::decode::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Self::try_from(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SigningPurpose {
|
||||||
|
fn message_type() -> MessageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct MessageType(String);
|
||||||
|
|
||||||
|
impl MessageType {
|
||||||
|
pub fn new<S: Into<String>>(typ: S) -> Self {
|
||||||
|
MessageType(typ.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for MessageType
|
||||||
|
where
|
||||||
|
T: ToString,
|
||||||
|
{
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
MessageType(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum SigningAlgorithm {
|
||||||
|
#[default]
|
||||||
|
Ed25519,
|
||||||
|
Secp256k1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigningAlgorithm {
|
||||||
|
pub fn is_ed25519(&self) -> bool {
|
||||||
|
matches!(self, SigningAlgorithm::Ed25519)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: maybe move this one to repo-wide common?
|
||||||
|
// TODO: should it perhaps also include the public key itself?
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SignableMessage<T> {
|
||||||
|
pub nonce: u32,
|
||||||
|
pub algorithm: SigningAlgorithm,
|
||||||
|
pub message_type: MessageType,
|
||||||
|
|
||||||
|
pub content: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SignableMessage<T>
|
||||||
|
where
|
||||||
|
T: SigningPurpose,
|
||||||
|
{
|
||||||
|
pub fn new(nonce: u32, content: T) -> Self {
|
||||||
|
SignableMessage {
|
||||||
|
nonce,
|
||||||
|
algorithm: SigningAlgorithm::Ed25519,
|
||||||
|
message_type: T::message_type(),
|
||||||
|
content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_signing_algorithm(mut self, algorithm: SigningAlgorithm) -> Self {
|
||||||
|
self.algorithm = algorithm;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_plaintext(&self) -> StdResult<Vec<u8>>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
to_vec(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_sha256_plaintext_digest(&self) -> StdResult<Vec<u8>>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_json_string(&self) -> StdResult<String>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
// if you look into implementation of `serde_json_wasm::to_string` this [i.e. the String conversion]
|
||||||
|
// CAN'T fail, but let's avoid this unnecessary unwrap either way
|
||||||
|
self.to_plaintext()
|
||||||
|
.map(|s| String::from_utf8(s).unwrap_or(String::from("SERIALIZATION FAILURE")))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_base58_string(&self) -> StdResult<String>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
self.to_plaintext().map(|s| bs58::encode(s).into_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_from_bytes(bytes: &[u8]) -> StdResult<SignableMessage<T>>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
{
|
||||||
|
from_slice(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_from_string(raw: &str) -> StdResult<SignableMessage<T>>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
{
|
||||||
|
Self::try_from_bytes(raw.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_from_base58_string(raw: &str) -> bs58::decode::Result<StdResult<SignableMessage<T>>>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
{
|
||||||
|
bs58::decode(raw)
|
||||||
|
.into_vec()
|
||||||
|
.map(|d| Self::try_from_bytes(&d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ContractMessageContent<T> {
|
||||||
|
pub sender: Addr,
|
||||||
|
pub proxy: Option<Addr>,
|
||||||
|
pub funds: Vec<Coin>,
|
||||||
|
pub data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SigningPurpose for ContractMessageContent<T>
|
||||||
|
where
|
||||||
|
T: SigningPurpose,
|
||||||
|
{
|
||||||
|
fn message_type() -> MessageType {
|
||||||
|
T::message_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ContractMessageContent<T> {
|
||||||
|
pub fn new(sender: Addr, proxy: Option<Addr>, funds: Vec<Coin>, data: T) -> Self {
|
||||||
|
ContractMessageContent {
|
||||||
|
sender,
|
||||||
|
proxy,
|
||||||
|
funds,
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_info(info: MessageInfo, signer: Addr, data: T) -> Self {
|
||||||
|
let proxy = if info.sender == signer {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(info.sender)
|
||||||
|
};
|
||||||
|
|
||||||
|
ContractMessageContent {
|
||||||
|
sender: signer,
|
||||||
|
proxy,
|
||||||
|
funds: info.funds,
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::signing::{MessageSignature, SignableMessage, SigningAlgorithm, SigningPurpose};
|
||||||
|
use cosmwasm_std::{Api, StdError, VerificationError};
|
||||||
|
use serde::Serialize;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub trait Verifier {
|
||||||
|
type Error: From<StdError>;
|
||||||
|
|
||||||
|
fn verify_message<T: Serialize + SigningPurpose>(
|
||||||
|
&self,
|
||||||
|
message: SignableMessage<T>,
|
||||||
|
signature: MessageSignature,
|
||||||
|
public_key: &[u8],
|
||||||
|
) -> Result<bool, Self::Error> {
|
||||||
|
match message.algorithm {
|
||||||
|
SigningAlgorithm::Ed25519 => {
|
||||||
|
let plaintext = message.to_plaintext()?;
|
||||||
|
self.verify_ed25519(&plaintext, signature.as_ref(), public_key)
|
||||||
|
}
|
||||||
|
SigningAlgorithm::Secp256k1 => {
|
||||||
|
let plaintext = message.to_sha256_plaintext_digest()?;
|
||||||
|
self.verify_secp256k1(&plaintext, signature.as_ref(), public_key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_ed25519(
|
||||||
|
&self,
|
||||||
|
_message: &[u8],
|
||||||
|
_signature: &[u8],
|
||||||
|
_public_key: &[u8],
|
||||||
|
) -> Result<bool, Self::Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_secp256k1(
|
||||||
|
&self,
|
||||||
|
_message_hash: &[u8],
|
||||||
|
_signature: &[u8],
|
||||||
|
_public_key: &[u8],
|
||||||
|
) -> Result<bool, Self::Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error, PartialEq)]
|
||||||
|
pub enum ApiVerifierError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Verification(#[from] VerificationError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Std(#[from] StdError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Verifier for T
|
||||||
|
where
|
||||||
|
T: Api + ?Sized,
|
||||||
|
{
|
||||||
|
type Error = ApiVerifierError;
|
||||||
|
|
||||||
|
fn verify_ed25519(
|
||||||
|
&self,
|
||||||
|
message: &[u8],
|
||||||
|
signature: &[u8],
|
||||||
|
public_key: &[u8],
|
||||||
|
) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.ed25519_verify(message, signature, public_key)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_secp256k1(
|
||||||
|
&self,
|
||||||
|
message_hash: &[u8],
|
||||||
|
signature: &[u8],
|
||||||
|
public_key: &[u8],
|
||||||
|
) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.secp256k1_verify(message_hash, signature, public_key)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::{EpochState, IdentityKey, MixId};
|
use crate::{EpochState, IdentityKey, MixId};
|
||||||
|
use contracts_common::signing::verifier::ApiVerifierError;
|
||||||
use cosmwasm_std::{Addr, Coin, Decimal};
|
use cosmwasm_std::{Addr, Coin, Decimal};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@@ -218,4 +219,10 @@ pub enum MixnetContractError {
|
|||||||
value: String,
|
value: String,
|
||||||
error_message: String,
|
error_message: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("failed to verify message signature: {source}")]
|
||||||
|
SignatureVerificationFailure {
|
||||||
|
#[from]
|
||||||
|
source: ApiVerifierError,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ mod msg;
|
|||||||
pub mod pending_events;
|
pub mod pending_events;
|
||||||
pub mod reward_params;
|
pub mod reward_params;
|
||||||
pub mod rewarding;
|
pub mod rewarding;
|
||||||
|
pub mod signing_types;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use contracts_common::types::*;
|
pub use contracts_common::types::*;
|
||||||
@@ -43,4 +44,5 @@ pub use pending_events::{
|
|||||||
PendingIntervalEventData, PendingIntervalEventKind,
|
PendingIntervalEventData, PendingIntervalEventKind,
|
||||||
};
|
};
|
||||||
pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams};
|
pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams};
|
||||||
|
pub use signing_types::*;
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use crate::reward_params::{
|
|||||||
};
|
};
|
||||||
use crate::{delegation, ContractStateParams, Layer, LayerAssignment, MixId, Percent};
|
use crate::{delegation, ContractStateParams, Layer, LayerAssignment, MixId, Percent};
|
||||||
use crate::{Gateway, IdentityKey, MixNode};
|
use crate::{Gateway, IdentityKey, MixNode};
|
||||||
|
use contracts_common::signing::MessageSignature;
|
||||||
use cosmwasm_std::Decimal;
|
use cosmwasm_std::Decimal;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -153,12 +154,12 @@ pub enum ExecuteMsg {
|
|||||||
BondMixnode {
|
BondMixnode {
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
},
|
},
|
||||||
BondMixnodeOnBehalf {
|
BondMixnodeOnBehalf {
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
owner: String,
|
owner: String,
|
||||||
},
|
},
|
||||||
PledgeMore {},
|
PledgeMore {},
|
||||||
@@ -187,12 +188,12 @@ pub enum ExecuteMsg {
|
|||||||
// gateway-related:
|
// gateway-related:
|
||||||
BondGateway {
|
BondGateway {
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
},
|
},
|
||||||
BondGatewayOnBehalf {
|
BondGatewayOnBehalf {
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner: String,
|
owner: String,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
},
|
},
|
||||||
UnbondGateway {},
|
UnbondGateway {},
|
||||||
UnbondGatewayOnBehalf {
|
UnbondGatewayOnBehalf {
|
||||||
@@ -500,6 +501,11 @@ pub enum QueryMsg {
|
|||||||
start_after: Option<u32>,
|
start_after: Option<u32>,
|
||||||
},
|
},
|
||||||
GetNumberOfPendingEvents {},
|
GetNumberOfPendingEvents {},
|
||||||
|
|
||||||
|
// signing-related
|
||||||
|
GetSigningNonce {
|
||||||
|
address: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::{Gateway, MixNode, MixNodeCostParams};
|
||||||
|
use contracts_common::signing::{
|
||||||
|
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
|
||||||
|
};
|
||||||
|
use cosmwasm_std::{Addr, Coin};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
pub type SignableMixNodeBondingMsg = SignableMessage<ContractMessageContent<MixnodeBondingPayload>>;
|
||||||
|
pub type SignableGatewayBondingMsg = SignableMessage<ContractMessageContent<GatewayBondingPayload>>;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct MixnodeBondingPayload {
|
||||||
|
mix_node: MixNode,
|
||||||
|
cost_params: MixNodeCostParams,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MixnodeBondingPayload {
|
||||||
|
pub fn new(mix_node: MixNode, cost_params: MixNodeCostParams) -> Self {
|
||||||
|
Self {
|
||||||
|
mix_node,
|
||||||
|
cost_params,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigningPurpose for MixnodeBondingPayload {
|
||||||
|
fn message_type() -> MessageType {
|
||||||
|
MessageType::new("mixnode-bonding")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn construct_mixnode_bonding_sign_payload(
|
||||||
|
nonce: Nonce,
|
||||||
|
sender: Addr,
|
||||||
|
proxy: Option<Addr>,
|
||||||
|
pledge: Coin,
|
||||||
|
mix_node: MixNode,
|
||||||
|
cost_params: MixNodeCostParams,
|
||||||
|
) -> SignableMixNodeBondingMsg {
|
||||||
|
let payload = MixnodeBondingPayload::new(mix_node, cost_params);
|
||||||
|
let content = ContractMessageContent::new(sender, proxy, vec![pledge], payload);
|
||||||
|
|
||||||
|
SignableMessage::new(nonce, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct GatewayBondingPayload {
|
||||||
|
gateway: Gateway,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GatewayBondingPayload {
|
||||||
|
pub fn new(gateway: Gateway) -> Self {
|
||||||
|
Self { gateway }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigningPurpose for GatewayBondingPayload {
|
||||||
|
fn message_type() -> MessageType {
|
||||||
|
MessageType::new("gateway-bonding")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn construct_gateway_bonding_sign_payload(
|
||||||
|
nonce: Nonce,
|
||||||
|
sender: Addr,
|
||||||
|
proxy: Option<Addr>,
|
||||||
|
pledge: Coin,
|
||||||
|
gateway: Gateway,
|
||||||
|
) -> SignableGatewayBondingMsg {
|
||||||
|
let payload = GatewayBondingPayload::new(gateway);
|
||||||
|
let content = ContractMessageContent::new(sender, proxy, vec![pledge], payload);
|
||||||
|
|
||||||
|
SignableMessage::new(nonce, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct FamilyCreationSignature {
|
||||||
|
label: String,
|
||||||
|
// TODO: add any extra fields?
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FamilyCreationSignature {
|
||||||
|
pub fn new(label: String) -> Self {
|
||||||
|
Self { label }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigningPurpose for FamilyCreationSignature {
|
||||||
|
fn message_type() -> MessageType {
|
||||||
|
MessageType::new("family-creation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct FamilyJoinSignature {
|
||||||
|
family_head: String,
|
||||||
|
// TODO: add any extra fields?
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FamilyJoinSignature {
|
||||||
|
pub fn new(family_head: String) -> Self {
|
||||||
|
Self { family_head }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigningPurpose for FamilyJoinSignature {
|
||||||
|
fn message_type() -> MessageType {
|
||||||
|
MessageType::new("family-join")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct FamilyLeaveSignature {
|
||||||
|
family_head: String,
|
||||||
|
// TODO: add any extra fields?
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FamilyLeaveSignature {
|
||||||
|
pub fn new(family_head: String) -> Self {
|
||||||
|
Self { family_head }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigningPurpose for FamilyLeaveSignature {
|
||||||
|
fn message_type() -> MessageType {
|
||||||
|
MessageType::new("family-leave")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct FamilyKickSignature {
|
||||||
|
member: String,
|
||||||
|
// TODO: add any extra fields?
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FamilyKickSignature {
|
||||||
|
pub fn new(member: String) -> Self {
|
||||||
|
Self { member }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigningPurpose for FamilyKickSignature {
|
||||||
|
fn message_type() -> MessageType {
|
||||||
|
MessageType::new("family-member-removal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: depending on our threat model, we should perhaps extend it to include all _on_behalf methods
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use contracts_common::signing::MessageSignature;
|
||||||
use cosmwasm_std::{Coin, Timestamp};
|
use cosmwasm_std::{Coin, Timestamp};
|
||||||
use mixnet_contract_common::{
|
use mixnet_contract_common::{
|
||||||
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
|
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
|
||||||
@@ -118,7 +119,7 @@ pub enum ExecuteMsg {
|
|||||||
BondMixnode {
|
BondMixnode {
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
},
|
},
|
||||||
PledgeMore {
|
PledgeMore {
|
||||||
@@ -131,7 +132,7 @@ pub enum ExecuteMsg {
|
|||||||
},
|
},
|
||||||
BondGateway {
|
BondGateway {
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
},
|
},
|
||||||
UnbondGateway {},
|
UnbondGateway {},
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ impl GatewayBond {
|
|||||||
pub struct GatewayNodeDetailsResponse {
|
pub struct GatewayNodeDetailsResponse {
|
||||||
pub identity_key: String,
|
pub identity_key: String,
|
||||||
pub sphinx_key: String,
|
pub sphinx_key: String,
|
||||||
pub owner_signature: String,
|
|
||||||
pub announce_address: String,
|
pub announce_address: String,
|
||||||
pub bind_address: String,
|
pub bind_address: String,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
@@ -94,7 +93,6 @@ impl fmt::Display for GatewayNodeDetailsResponse {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
writeln!(f, "Identity Key: {}", self.identity_key)?;
|
writeln!(f, "Identity Key: {}", self.identity_key)?;
|
||||||
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
|
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
|
||||||
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
|
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"Host: {} (bind address: {})",
|
"Host: {} (bind address: {})",
|
||||||
|
|||||||
@@ -167,7 +167,6 @@ impl MixNodeCostParams {
|
|||||||
pub struct MixnodeNodeDetailsResponse {
|
pub struct MixnodeNodeDetailsResponse {
|
||||||
pub identity_key: String,
|
pub identity_key: String,
|
||||||
pub sphinx_key: String,
|
pub sphinx_key: String,
|
||||||
pub owner_signature: String,
|
|
||||||
pub announce_address: String,
|
pub announce_address: String,
|
||||||
pub bind_address: String,
|
pub bind_address: String,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
@@ -182,7 +181,6 @@ impl fmt::Display for MixnodeNodeDetailsResponse {
|
|||||||
let wallet_address = self.wallet_address.clone().unwrap_or_default();
|
let wallet_address = self.wallet_address.clone().unwrap_or_default();
|
||||||
writeln!(f, "Identity Key: {}", self.identity_key)?;
|
writeln!(f, "Identity Key: {}", self.identity_key)?;
|
||||||
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
|
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
|
||||||
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
|
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"Host: {} (bind address: {})",
|
"Host: {} (bind address: {})",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.2.0" }
|
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.2.0" }
|
||||||
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.2.0" }
|
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.2.0" }
|
||||||
#nym-config = { path = "../../common/config"}
|
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.2.0" }
|
||||||
|
|
||||||
cosmwasm-std = "1.0.0"
|
cosmwasm-std = "1.0.0"
|
||||||
cosmwasm-storage = "1.0.0"
|
cosmwasm-storage = "1.0.0"
|
||||||
@@ -41,7 +41,6 @@ semver = { version = "1.0.16", default-features = false }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
cosmwasm-schema = "1.0.0"
|
cosmwasm-schema = "1.0.0"
|
||||||
rand_chacha = "0.2"
|
rand_chacha = "0.2"
|
||||||
#rand = "0.7"
|
|
||||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
|
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
@@ -39,41 +39,43 @@ pub const FAMILIES_DEFAULT_RETRIEVAL_LIMIT: u32 = 10;
|
|||||||
pub const FAMILIES_MAX_RETRIEVAL_LIMIT: u32 = 20;
|
pub const FAMILIES_MAX_RETRIEVAL_LIMIT: u32 = 20;
|
||||||
|
|
||||||
// storage keys
|
// storage keys
|
||||||
pub(crate) const DELEGATION_PK_NAMESPACE: &str = "dl";
|
pub const DELEGATION_PK_NAMESPACE: &str = "dl";
|
||||||
pub(crate) const DELEGATION_OWNER_IDX_NAMESPACE: &str = "dlo";
|
pub const DELEGATION_OWNER_IDX_NAMESPACE: &str = "dlo";
|
||||||
pub(crate) const DELEGATION_MIXNODE_IDX_NAMESPACE: &str = "dlm";
|
pub const DELEGATION_MIXNODE_IDX_NAMESPACE: &str = "dlm";
|
||||||
|
|
||||||
pub(crate) const GATEWAYS_PK_NAMESPACE: &str = "gt";
|
pub const GATEWAYS_PK_NAMESPACE: &str = "gt";
|
||||||
pub(crate) const GATEWAYS_OWNER_IDX_NAMESPACE: &str = "gto";
|
pub const GATEWAYS_OWNER_IDX_NAMESPACE: &str = "gto";
|
||||||
|
|
||||||
pub(crate) const REWARDED_SET_KEY: &str = "rs";
|
pub const REWARDED_SET_KEY: &str = "rs";
|
||||||
pub(crate) const CURRENT_EPOCH_STATUS_KEY: &str = "ces";
|
pub const CURRENT_EPOCH_STATUS_KEY: &str = "ces";
|
||||||
pub(crate) const CURRENT_INTERVAL_KEY: &str = "ci";
|
pub const CURRENT_INTERVAL_KEY: &str = "ci";
|
||||||
pub(crate) const EPOCH_EVENT_ID_COUNTER_KEY: &str = "eic";
|
pub const EPOCH_EVENT_ID_COUNTER_KEY: &str = "eic";
|
||||||
pub(crate) const INTERVAL_EVENT_ID_COUNTER_KEY: &str = "iic";
|
pub const INTERVAL_EVENT_ID_COUNTER_KEY: &str = "iic";
|
||||||
pub(crate) const PENDING_EPOCH_EVENTS_NAMESPACE: &str = "pee";
|
pub const PENDING_EPOCH_EVENTS_NAMESPACE: &str = "pee";
|
||||||
pub(crate) const PENDING_INTERVAL_EVENTS_NAMESPACE: &str = "pie";
|
pub const PENDING_INTERVAL_EVENTS_NAMESPACE: &str = "pie";
|
||||||
|
|
||||||
pub(crate) const LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
|
pub const LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
|
||||||
pub(crate) const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
|
pub const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
|
||||||
|
|
||||||
pub(crate) const CONTRACT_STATE_KEY: &str = "state";
|
pub const CONTRACT_STATE_KEY: &str = "state";
|
||||||
|
|
||||||
pub(crate) const LAYER_DISTRIBUTION_KEY: &str = "layers";
|
pub const LAYER_DISTRIBUTION_KEY: &str = "layers";
|
||||||
pub(crate) const NODE_ID_COUNTER_KEY: &str = "nic";
|
pub const NODE_ID_COUNTER_KEY: &str = "nic";
|
||||||
pub(crate) const MIXNODES_PK_NAMESPACE: &str = "mnn";
|
pub const MIXNODES_PK_NAMESPACE: &str = "mnn";
|
||||||
pub(crate) const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno";
|
pub const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno";
|
||||||
pub(crate) const MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "mni";
|
pub const MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "mni";
|
||||||
pub(crate) const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns";
|
pub const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns";
|
||||||
|
|
||||||
pub(crate) const UNBONDED_MIXNODES_PK_NAMESPACE: &str = "ubm";
|
pub const UNBONDED_MIXNODES_PK_NAMESPACE: &str = "ubm";
|
||||||
pub(crate) const UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE: &str = "umo";
|
pub const UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE: &str = "umo";
|
||||||
pub(crate) const UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "umi";
|
pub const UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "umi";
|
||||||
|
|
||||||
pub(crate) const REWARDING_PARAMS_KEY: &str = "rparams";
|
pub const REWARDING_PARAMS_KEY: &str = "rparams";
|
||||||
pub(crate) const PENDING_REWARD_POOL_KEY: &str = "prp";
|
pub const PENDING_REWARD_POOL_KEY: &str = "prp";
|
||||||
pub(crate) const MIXNODES_REWARDING_PK_NAMESPACE: &str = "mnr";
|
pub const MIXNODES_REWARDING_PK_NAMESPACE: &str = "mnr";
|
||||||
|
|
||||||
pub(crate) const FAMILIES_INDEX_NAMESPACE: &str = "faml2";
|
pub const FAMILIES_INDEX_NAMESPACE: &str = "faml2";
|
||||||
pub(crate) const FAMILIES_MAP_NAMESPACE: &str = "fam2";
|
pub const FAMILIES_MAP_NAMESPACE: &str = "fam2";
|
||||||
pub(crate) const MEMBERS_MAP_NAMESPACE: &str = "memb2";
|
pub const MEMBERS_MAP_NAMESPACE: &str = "memb2";
|
||||||
|
|
||||||
|
pub const SIGNING_NONCES_NAMESPACE: &str = "sn";
|
||||||
|
|||||||
@@ -581,6 +581,9 @@ pub fn query(
|
|||||||
QueryMsg::GetNumberOfPendingEvents {} => to_binary(
|
QueryMsg::GetNumberOfPendingEvents {} => to_binary(
|
||||||
&crate::interval::queries::query_number_of_pending_events(deps)?,
|
&crate::interval::queries::query_number_of_pending_events(deps)?,
|
||||||
),
|
),
|
||||||
|
QueryMsg::GetSigningNonce { address } => to_binary(
|
||||||
|
&crate::signing::queries::query_current_signing_nonce(deps, address)?,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(query_res?)
|
Ok(query_res?)
|
||||||
|
|||||||
@@ -305,71 +305,77 @@ mod test {
|
|||||||
use crate::families::queries::{get_family_by_head, get_family_by_label};
|
use crate::families::queries::{get_family_by_head, get_family_by_label};
|
||||||
use crate::families::storage::is_family_member;
|
use crate::families::storage::is_family_member;
|
||||||
use crate::mixnet_contract_settings::storage::minimum_mixnode_pledge;
|
use crate::mixnet_contract_settings::storage::minimum_mixnode_pledge;
|
||||||
use crate::support::tests::{fixtures, test_helpers};
|
use crate::support::tests::fixtures;
|
||||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
use crate::support::tests::test_helpers::TestSetup;
|
||||||
|
use cosmwasm_std::testing::mock_info;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_family_crud() {
|
fn test_family_crud() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
let env = test.env();
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
let head = "alice";
|
let head = "alice";
|
||||||
let malicious_head = "timmy";
|
let malicious_head = "timmy";
|
||||||
|
let member = "bob";
|
||||||
|
|
||||||
let minimum_pledge = minimum_mixnode_pledge(deps.as_ref().storage).unwrap();
|
let minimum_pledge = minimum_mixnode_pledge(test.deps().storage).unwrap();
|
||||||
|
|
||||||
let (head_mixnode, head_sig, head_keypair) =
|
|
||||||
test_helpers::mixnode_with_signature(&mut rng, head);
|
|
||||||
|
|
||||||
let (malicious_mixnode, malicious_sig, _malicious_keypair) =
|
|
||||||
test_helpers::mixnode_with_signature(&mut rng, malicious_head);
|
|
||||||
|
|
||||||
let cost_params = fixtures::mix_node_cost_params_fixture();
|
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||||
|
|
||||||
let member = "bob";
|
let (head_mixnode, head_bond_sig, head_keypair) = test.mixnode_with_signature(head, None);
|
||||||
let (member_mixnode, member_sig, _) =
|
let (malicious_mixnode, malicious_bond_sig, malicious_keypair) =
|
||||||
test_helpers::mixnode_with_signature(&mut rng, member);
|
test.mixnode_with_signature(malicious_head, None);
|
||||||
|
let (member_mixnode, member_bond_sig, member_keypair) =
|
||||||
|
test.mixnode_with_signature(member, None);
|
||||||
|
|
||||||
// we are informed that we didn't send enough funds
|
|
||||||
crate::mixnodes::transactions::try_add_mixnode(
|
crate::mixnodes::transactions::try_add_mixnode(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
env.clone(),
|
env.clone(),
|
||||||
mock_info(head, &[minimum_pledge.clone()]),
|
mock_info(head, &[minimum_pledge.clone()]),
|
||||||
head_mixnode.clone(),
|
head_mixnode.clone(),
|
||||||
cost_params.clone(),
|
cost_params.clone(),
|
||||||
head_sig.clone(),
|
head_bond_sig,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
crate::mixnodes::transactions::try_add_mixnode(
|
crate::mixnodes::transactions::try_add_mixnode(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
env.clone(),
|
env.clone(),
|
||||||
mock_info(malicious_head, &[minimum_pledge.clone()]),
|
mock_info(malicious_head, &[minimum_pledge.clone()]),
|
||||||
malicious_mixnode,
|
malicious_mixnode,
|
||||||
cost_params.clone(),
|
cost_params.clone(),
|
||||||
malicious_sig.clone(),
|
malicious_bond_sig,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
crate::mixnodes::transactions::try_add_mixnode(
|
crate::mixnodes::transactions::try_add_mixnode(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
env,
|
env,
|
||||||
mock_info(member, &[minimum_pledge]),
|
mock_info(member, &[minimum_pledge]),
|
||||||
member_mixnode.clone(),
|
member_mixnode.clone(),
|
||||||
cost_params,
|
cost_params,
|
||||||
member_sig.clone(),
|
member_bond_sig,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
try_create_family(deps.as_mut(), mock_info(head, &[]), head_sig, "test").unwrap();
|
let old_style_head_sig = head_keypair.private_key().sign_text(head);
|
||||||
|
let old_style_malicious_head_sig =
|
||||||
|
malicious_keypair.private_key().sign_text(malicious_head);
|
||||||
|
let old_style_member_sig = member_keypair.private_key().sign_text(member);
|
||||||
|
|
||||||
|
try_create_family(
|
||||||
|
test.deps_mut(),
|
||||||
|
mock_info(head, &[]),
|
||||||
|
old_style_head_sig,
|
||||||
|
"test",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let family_head = FamilyHead::new(&head_mixnode.identity_key);
|
let family_head = FamilyHead::new(&head_mixnode.identity_key);
|
||||||
assert!(get_family(&family_head, &deps.storage).is_ok());
|
assert!(get_family(&family_head, test.deps().storage).is_ok());
|
||||||
|
|
||||||
let nope = try_create_family(
|
let nope = try_create_family(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
mock_info(malicious_head, &[]),
|
mock_info(malicious_head, &[]),
|
||||||
malicious_sig,
|
old_style_malicious_head_sig,
|
||||||
"test",
|
"test",
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -381,11 +387,11 @@ mod test {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
let family = get_family_by_label("test", &deps.storage).unwrap();
|
let family = get_family_by_label("test", test.deps().storage).unwrap();
|
||||||
assert!(family.is_some());
|
assert!(family.is_some());
|
||||||
assert_eq!(family.unwrap().head_identity(), family_head.identity());
|
assert_eq!(family.unwrap().head_identity(), family_head.identity());
|
||||||
|
|
||||||
let family = get_family_by_head(family_head.identity(), &deps.storage).unwrap();
|
let family = get_family_by_head(family_head.identity(), test.deps().storage).unwrap();
|
||||||
assert_eq!(family.head_identity(), family_head.identity());
|
assert_eq!(family.head_identity(), family_head.identity());
|
||||||
|
|
||||||
let join_signature = head_keypair
|
let join_signature = head_keypair
|
||||||
@@ -394,41 +400,47 @@ mod test {
|
|||||||
.to_base58_string();
|
.to_base58_string();
|
||||||
|
|
||||||
try_join_family(
|
try_join_family(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
mock_info(member, &[]),
|
mock_info(member, &[]),
|
||||||
Some(member_sig.clone()),
|
Some(old_style_member_sig.clone()),
|
||||||
join_signature.clone(),
|
join_signature.clone(),
|
||||||
head_mixnode.identity_key.clone(),
|
head_mixnode.identity_key.clone(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let family = get_family(&family_head, &deps.storage).unwrap();
|
let family = get_family(&family_head, test.deps().storage).unwrap();
|
||||||
|
|
||||||
assert!(is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
|
assert!(
|
||||||
|
is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
try_leave_family(
|
try_leave_family(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
mock_info(member, &[]),
|
mock_info(member, &[]),
|
||||||
member_sig.clone(),
|
old_style_member_sig.clone(),
|
||||||
head_mixnode.identity_key.clone(),
|
head_mixnode.identity_key.clone(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let family = get_family(&family_head, &deps.storage).unwrap();
|
let family = get_family(&family_head, test.deps().storage).unwrap();
|
||||||
assert!(!is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
|
assert!(
|
||||||
|
!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
try_join_family(
|
try_join_family(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
mock_info(member, &[]),
|
mock_info(member, &[]),
|
||||||
Some(member_sig.clone()),
|
Some(old_style_member_sig),
|
||||||
join_signature.clone(),
|
join_signature,
|
||||||
head_mixnode.identity_key.clone(),
|
head_mixnode.identity_key,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let family = get_family(&family_head, &deps.storage).unwrap();
|
let family = get_family(&family_head, test.deps().storage).unwrap();
|
||||||
|
|
||||||
assert!(is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
|
assert!(
|
||||||
|
is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
// try_head_kick_member(
|
// try_head_kick_member(
|
||||||
// deps.as_mut(),
|
// deps.as_mut(),
|
||||||
@@ -438,8 +450,8 @@ mod test {
|
|||||||
// )
|
// )
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// let family = get_family(&family_head, &deps.storage).unwrap();
|
// let family = get_family(&family_head, test.deps().storage).unwrap();
|
||||||
// assert!(!is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
|
// assert!(!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -456,7 +468,7 @@ mod test {
|
|||||||
|
|
||||||
let head = "alice";
|
let head = "alice";
|
||||||
|
|
||||||
let (_, keypair) = test.add_dummy_mixnode_with_keypair(head, None);
|
let (_, keypair) = test.add_dummy_mixnode_with_proxy_and_keypair(head, None);
|
||||||
let sig = keypair.private_key().sign_text(head);
|
let sig = keypair.private_key().sign_text(head);
|
||||||
|
|
||||||
let res = try_create_family_on_behalf(
|
let res = try_create_family_on_behalf(
|
||||||
@@ -495,7 +507,7 @@ mod test {
|
|||||||
let new_member = "vin-diesel";
|
let new_member = "vin-diesel";
|
||||||
|
|
||||||
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
|
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
|
||||||
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
|
let (_, member_keys) = test.add_dummy_mixnode_with_proxy_and_keypair(new_member, None);
|
||||||
|
|
||||||
// TODO: those signatures are WRONG and have to be c hanged
|
// TODO: those signatures are WRONG and have to be c hanged
|
||||||
let join_signature = head_keys
|
let join_signature = head_keys
|
||||||
@@ -542,7 +554,7 @@ mod test {
|
|||||||
let new_member = "vin-diesel";
|
let new_member = "vin-diesel";
|
||||||
|
|
||||||
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
|
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
|
||||||
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
|
let (_, member_keys) = test.add_dummy_mixnode_with_proxy_and_keypair(new_member, None);
|
||||||
|
|
||||||
// TODO: those signatures are WRONG and have to be changed
|
// TODO: those signatures are WRONG and have to be changed
|
||||||
let join_signature = head_keys
|
let join_signature = head_keys
|
||||||
@@ -599,7 +611,7 @@ mod test {
|
|||||||
let new_member = "vin-diesel";
|
let new_member = "vin-diesel";
|
||||||
|
|
||||||
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
|
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
|
||||||
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
|
let (_, member_keys) = test.add_dummy_mixnode_with_proxy_and_keypair(new_member, None);
|
||||||
|
|
||||||
// TODO: those signatures are WRONG and have to be c hanged
|
// TODO: those signatures are WRONG and have to be c hanged
|
||||||
let join_signature = head_keys
|
let join_signature = head_keys
|
||||||
@@ -613,7 +625,7 @@ mod test {
|
|||||||
test.deps_mut(),
|
test.deps_mut(),
|
||||||
mock_info(vesting_contract.as_ref(), &[]),
|
mock_info(vesting_contract.as_ref(), &[]),
|
||||||
new_member.to_string(),
|
new_member.to_string(),
|
||||||
Some(member_sig.clone()),
|
Some(member_sig),
|
||||||
join_signature,
|
join_signature,
|
||||||
head_identity,
|
head_identity,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,5 +2,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
pub mod queries;
|
pub mod queries;
|
||||||
|
pub mod signature_helpers;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub mod transactions;
|
pub mod transactions;
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ pub(crate) mod tests {
|
|||||||
use crate::contract::execute;
|
use crate::contract::execute;
|
||||||
use crate::support::tests;
|
use crate::support::tests;
|
||||||
use crate::support::tests::test_helpers;
|
use crate::support::tests::test_helpers;
|
||||||
|
use crate::support::tests::test_helpers::TestSetup;
|
||||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -73,27 +74,22 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gateways_paged_retrieval_obeys_limits() {
|
fn gateways_paged_retrieval_obeys_limits() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
test.add_dummy_gateways(1000);
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
let limit = 2;
|
let limit = 2;
|
||||||
test_helpers::add_dummy_gateways(&mut rng, deps.as_mut(), env, 1000);
|
|
||||||
|
|
||||||
let page1 = query_gateways_paged(deps.as_ref(), None, Option::from(limit)).unwrap();
|
let page1 = query_gateways_paged(test.deps(), None, Option::from(limit)).unwrap();
|
||||||
assert_eq!(limit, page1.nodes.len() as u32);
|
assert_eq!(limit, page1.nodes.len() as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gateways_paged_retrieval_has_default_limit() {
|
fn gateways_paged_retrieval_has_default_limit() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
test.add_dummy_gateways(1000);
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
test_helpers::add_dummy_gateways(&mut rng, deps.as_mut(), env, 1000);
|
|
||||||
|
|
||||||
// query without explicitly setting a limit
|
// query without explicitly setting a limit
|
||||||
let page1 = query_gateways_paged(deps.as_ref(), None, None).unwrap();
|
let page1 = query_gateways_paged(test.deps(), None, None).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
GATEWAY_BOND_DEFAULT_RETRIEVAL_LIMIT,
|
GATEWAY_BOND_DEFAULT_RETRIEVAL_LIMIT,
|
||||||
@@ -103,15 +99,12 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gateways_paged_retrieval_has_max_limit() {
|
fn gateways_paged_retrieval_has_max_limit() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
test.add_dummy_gateways(1000);
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
test_helpers::add_dummy_gateways(&mut rng, deps.as_mut(), env, 1000);
|
|
||||||
|
|
||||||
// query with a crazily high limit in an attempt to use too many resources
|
// query with a crazily high limit in an attempt to use too many resources
|
||||||
let crazy_limit = 1000 * GATEWAY_BOND_DEFAULT_RETRIEVAL_LIMIT;
|
let crazy_limit = 1000 * GATEWAY_BOND_DEFAULT_RETRIEVAL_LIMIT;
|
||||||
let page1 = query_gateways_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap();
|
let page1 = query_gateways_paged(test.deps(), None, Option::from(crazy_limit)).unwrap();
|
||||||
|
|
||||||
// we default to a decent sized upper bound instead
|
// we default to a decent sized upper bound instead
|
||||||
let expected_limit = GATEWAY_BOND_MAX_RETRIEVAL_LIMIT;
|
let expected_limit = GATEWAY_BOND_MAX_RETRIEVAL_LIMIT;
|
||||||
@@ -121,25 +114,27 @@ pub(crate) mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn gateway_pagination_works() {
|
fn gateway_pagination_works() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut deps = test_helpers::init_contract();
|
||||||
let _env = mock_env();
|
|
||||||
let mut rng = test_helpers::test_rng();
|
let mut rng = test_helpers::test_rng();
|
||||||
|
let stake = tests::fixtures::good_gateway_pledge();
|
||||||
|
|
||||||
// prepare 4 messages and identities that are sorted by the generated identities
|
// prepare 4 messages and identities that are sorted by the generated identities
|
||||||
// (because we query them in an ascended manner)
|
// (because we query them in an ascended manner)
|
||||||
let mut exec_data = (0..4)
|
let mut exec_data = (0..4)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let sender = format!("nym-addr{}", i);
|
let sender = format!("nym-addr{}", i);
|
||||||
let (msg, identity) = tests::messages::valid_bond_gateway_msg(&mut rng, &sender);
|
let (msg, identity) = tests::messages::valid_bond_gateway_msg(
|
||||||
|
&mut rng,
|
||||||
|
deps.as_ref(),
|
||||||
|
stake.clone(),
|
||||||
|
&sender,
|
||||||
|
);
|
||||||
(msg, (sender, identity))
|
(msg, (sender, identity))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
exec_data.sort_by(|(_, (_, id1)), (_, (_, id2))| id1.cmp(id2));
|
exec_data.sort_by(|(_, (_, id1)), (_, (_, id2))| id1.cmp(id2));
|
||||||
let (messages, sender_identities): (Vec<_>, Vec<_>) = exec_data.into_iter().unzip();
|
let (messages, sender_identities): (Vec<_>, Vec<_>) = exec_data.into_iter().unzip();
|
||||||
|
|
||||||
let info = mock_info(
|
let info = mock_info(&sender_identities[0].0.clone(), &stake);
|
||||||
&sender_identities[0].0.clone(),
|
|
||||||
&tests::fixtures::good_gateway_pledge(),
|
|
||||||
);
|
|
||||||
execute(deps.as_mut(), mock_env(), info, messages[0].clone()).unwrap();
|
execute(deps.as_mut(), mock_env(), info, messages[0].clone()).unwrap();
|
||||||
|
|
||||||
let per_page = 2;
|
let per_page = 2;
|
||||||
@@ -200,43 +195,29 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn query_for_gateway_owner_works() {
|
fn query_for_gateway_owner_works() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
// "fred" does not own a mixnode if there are no mixnodes
|
// "fred" does not own a mixnode if there are no mixnodes
|
||||||
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
|
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
|
||||||
assert!(res.gateway.is_none());
|
assert!(res.gateway.is_none());
|
||||||
|
|
||||||
// mixnode was added to "bob", "fred" still does not own one
|
// gateway was added to "bob", "fred" still does not own one
|
||||||
test_helpers::add_gateway(
|
test.add_dummy_gateway("bob", None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
"bob",
|
|
||||||
tests::fixtures::good_gateway_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
|
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
|
||||||
assert!(res.gateway.is_none());
|
assert!(res.gateway.is_none());
|
||||||
|
|
||||||
// "fred" now owns a gateway!
|
// "fred" now owns a gateway!
|
||||||
test_helpers::add_gateway(
|
test.add_dummy_gateway("fred", None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env,
|
|
||||||
"fred",
|
|
||||||
tests::fixtures::good_gateway_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
|
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
|
||||||
assert!(res.gateway.is_some());
|
assert!(res.gateway.is_some());
|
||||||
|
|
||||||
// but after unbonding it, he doesn't own one anymore
|
// but after unbonding it, he doesn't own one anymore
|
||||||
crate::gateways::transactions::try_remove_gateway(deps.as_mut(), mock_info("fred", &[]))
|
crate::gateways::transactions::try_remove_gateway(test.deps_mut(), mock_info("fred", &[]))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
|
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
|
||||||
assert!(res.gateway.is_none());
|
assert!(res.gateway.is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::signing::storage as signing_storage;
|
||||||
|
use cosmwasm_std::{Addr, Coin, Deps};
|
||||||
|
use mixnet_contract_common::error::MixnetContractError;
|
||||||
|
use mixnet_contract_common::{construct_gateway_bonding_sign_payload, Gateway};
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
|
use nym_contracts_common::signing::Verifier;
|
||||||
|
|
||||||
|
pub(crate) fn verify_gateway_bonding_signature(
|
||||||
|
deps: Deps<'_>,
|
||||||
|
sender: Addr,
|
||||||
|
proxy: Option<Addr>,
|
||||||
|
pledge: Coin,
|
||||||
|
gateway: Gateway,
|
||||||
|
signature: MessageSignature,
|
||||||
|
) -> Result<(), MixnetContractError> {
|
||||||
|
// recover the public key
|
||||||
|
let mut public_key = [0u8; 32];
|
||||||
|
bs58::decode(&gateway.identity_key)
|
||||||
|
.into(&mut public_key)
|
||||||
|
.map_err(|err| MixnetContractError::MalformedEd25519IdentityKey(err.to_string()))?;
|
||||||
|
|
||||||
|
// reconstruct the payload
|
||||||
|
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
|
||||||
|
let msg = construct_gateway_bonding_sign_payload(nonce, sender, proxy, pledge, gateway);
|
||||||
|
|
||||||
|
if deps.api.verify_message(msg, signature, &public_key)? {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(MixnetContractError::InvalidEd25519Signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,17 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use super::storage;
|
use super::storage;
|
||||||
|
use crate::gateways::signature_helpers::verify_gateway_bonding_signature;
|
||||||
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||||
|
use crate::signing::storage as signing_storage;
|
||||||
use crate::support::helpers::{
|
use crate::support::helpers::{
|
||||||
ensure_no_existing_bond, ensure_sent_by_vesting_contract, validate_node_identity_signature,
|
ensure_no_existing_bond, ensure_sent_by_vesting_contract, validate_pledge,
|
||||||
validate_pledge,
|
|
||||||
};
|
};
|
||||||
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
|
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
|
||||||
use mixnet_contract_common::error::MixnetContractError;
|
use mixnet_contract_common::error::MixnetContractError;
|
||||||
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
|
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
|
||||||
use mixnet_contract_common::{Gateway, GatewayBond};
|
use mixnet_contract_common::{Gateway, GatewayBond};
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
|
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
|
||||||
|
|
||||||
pub fn try_add_gateway(
|
pub fn try_add_gateway(
|
||||||
@@ -18,7 +20,7 @@ pub fn try_add_gateway(
|
|||||||
env: Env,
|
env: Env,
|
||||||
info: MessageInfo,
|
info: MessageInfo,
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
) -> Result<Response, MixnetContractError> {
|
) -> Result<Response, MixnetContractError> {
|
||||||
_try_add_gateway(
|
_try_add_gateway(
|
||||||
deps,
|
deps,
|
||||||
@@ -37,7 +39,7 @@ pub fn try_add_gateway_on_behalf(
|
|||||||
info: MessageInfo,
|
info: MessageInfo,
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner: String,
|
owner: String,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
) -> Result<Response, MixnetContractError> {
|
) -> Result<Response, MixnetContractError> {
|
||||||
ensure_sent_by_vesting_contract(&info, deps.storage)?;
|
ensure_sent_by_vesting_contract(&info, deps.storage)?;
|
||||||
|
|
||||||
@@ -54,13 +56,15 @@ pub fn try_add_gateway_on_behalf(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: perhaps also require the user to explicitly provide what it thinks is the current nonce
|
||||||
|
// so that we could return a better error message if it doesn't match?
|
||||||
pub(crate) fn _try_add_gateway(
|
pub(crate) fn _try_add_gateway(
|
||||||
deps: DepsMut<'_>,
|
deps: DepsMut<'_>,
|
||||||
env: Env,
|
env: Env,
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
pledge: Vec<Coin>,
|
pledge: Vec<Coin>,
|
||||||
owner: Addr,
|
owner: Addr,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
proxy: Option<Addr>,
|
proxy: Option<Addr>,
|
||||||
) -> Result<Response, MixnetContractError> {
|
) -> Result<Response, MixnetContractError> {
|
||||||
// check if the pledge contains any funds of the appropriate denomination
|
// check if the pledge contains any funds of the appropriate denomination
|
||||||
@@ -82,13 +86,18 @@ pub(crate) fn _try_add_gateway(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if this sender actually owns the gateway by checking the signature
|
// check if this sender actually owns the gateway by checking the signature
|
||||||
validate_node_identity_signature(
|
verify_gateway_bonding_signature(
|
||||||
deps.as_ref(),
|
deps.as_ref(),
|
||||||
&owner,
|
owner.clone(),
|
||||||
&owner_signature,
|
proxy.clone(),
|
||||||
&gateway.identity_key,
|
pledge.clone(),
|
||||||
|
gateway.clone(),
|
||||||
|
owner_signature,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// update the signing nonce associated with this sender so that the future signature would be made on the new value
|
||||||
|
signing_storage::increment_signing_nonce(deps.storage, owner.clone())?;
|
||||||
|
|
||||||
let gateway_identity = gateway.identity_key.clone();
|
let gateway_identity = gateway.identity_key.clone();
|
||||||
let bond = GatewayBond::new(
|
let bond = GatewayBond::new(
|
||||||
pledge.clone(),
|
pledge.clone(),
|
||||||
@@ -182,39 +191,41 @@ pub(crate) fn _try_remove_gateway(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
use crate::contract::execute;
|
use crate::contract::execute;
|
||||||
|
use crate::gateways::queries;
|
||||||
use crate::gateways::transactions::{
|
use crate::gateways::transactions::{
|
||||||
try_add_gateway, try_add_gateway_on_behalf, try_remove_gateway_on_behalf,
|
try_add_gateway, try_add_gateway_on_behalf, try_remove_gateway_on_behalf,
|
||||||
};
|
};
|
||||||
use crate::interval::pending_events;
|
use crate::interval::pending_events;
|
||||||
use crate::mixnet_contract_settings::storage::minimum_gateway_pledge;
|
use crate::mixnet_contract_settings::storage::minimum_gateway_pledge;
|
||||||
use crate::support::tests;
|
use crate::support::tests;
|
||||||
use crate::support::tests::fixtures::{good_gateway_pledge, TEST_COIN_DENOM};
|
use crate::support::tests::fixtures;
|
||||||
|
use crate::support::tests::fixtures::{good_gateway_pledge, good_mixnode_pledge};
|
||||||
use crate::support::tests::test_helpers::TestSetup;
|
use crate::support::tests::test_helpers::TestSetup;
|
||||||
use crate::support::tests::{fixtures, test_helpers};
|
use cosmwasm_std::testing::mock_info;
|
||||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
use cosmwasm_std::{Addr, BankMsg, Response, Uint128};
|
||||||
use cosmwasm_std::{coin, Addr, BankMsg, Response, Uint128};
|
|
||||||
use mixnet_contract_common::error::MixnetContractError;
|
use mixnet_contract_common::error::MixnetContractError;
|
||||||
use mixnet_contract_common::events::new_gateway_unbonding_event;
|
use mixnet_contract_common::events::new_gateway_unbonding_event;
|
||||||
use mixnet_contract_common::ExecuteMsg;
|
use mixnet_contract_common::ExecuteMsg;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gateway_add() {
|
fn gateway_add() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
// if we fail validation (by say not sending enough funds
|
// if we fail validation (by say not sending enough funds
|
||||||
let sender = "alice";
|
let sender = "alice";
|
||||||
let minimum_pledge = minimum_gateway_pledge(deps.as_ref().storage).unwrap();
|
let minimum_pledge = minimum_gateway_pledge(test.deps().storage).unwrap();
|
||||||
let mut insufficient_pledge = minimum_pledge.clone();
|
let mut insufficient_pledge = minimum_pledge.clone();
|
||||||
insufficient_pledge.amount -= Uint128::new(1000);
|
insufficient_pledge.amount -= Uint128::new(1000);
|
||||||
|
|
||||||
let info = mock_info(sender, &[insufficient_pledge.clone()]);
|
let info = mock_info(sender, &[insufficient_pledge.clone()]);
|
||||||
let (gateway, sig) = test_helpers::gateway_with_signature(&mut rng, sender);
|
let (gateway, sig) =
|
||||||
|
test.gateway_with_signature(sender, Some(vec![insufficient_pledge.clone()]));
|
||||||
|
|
||||||
|
let env = test.env();
|
||||||
let result = try_add_gateway(
|
let result = try_add_gateway(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
env.clone(),
|
env.clone(),
|
||||||
info,
|
info,
|
||||||
gateway.clone(),
|
gateway.clone(),
|
||||||
@@ -233,47 +244,23 @@ pub mod tests {
|
|||||||
// if the signature provided is invalid, the bonding also fails
|
// if the signature provided is invalid, the bonding also fails
|
||||||
let info = mock_info(sender, &[minimum_pledge]);
|
let info = mock_info(sender, &[minimum_pledge]);
|
||||||
|
|
||||||
let result = try_add_gateway(
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
info.clone(),
|
|
||||||
gateway.clone(),
|
|
||||||
"bad-signature".into(),
|
|
||||||
);
|
|
||||||
assert!(matches!(
|
|
||||||
result,
|
|
||||||
Err(MixnetContractError::MalformedEd25519Signature(..))
|
|
||||||
));
|
|
||||||
|
|
||||||
// if there was already a gateway bonded by particular user
|
// if there was already a gateway bonded by particular user
|
||||||
test_helpers::add_gateway(
|
test.add_dummy_gateway(sender, None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
sender,
|
|
||||||
fixtures::good_gateway_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// it fails
|
// it fails
|
||||||
let result = try_add_gateway(deps.as_mut(), env.clone(), info, gateway, sig);
|
let result = try_add_gateway(test.deps_mut(), env.clone(), info, gateway, sig);
|
||||||
assert_eq!(Err(MixnetContractError::AlreadyOwnsGateway), result);
|
assert_eq!(Err(MixnetContractError::AlreadyOwnsGateway), result);
|
||||||
|
|
||||||
// the same holds if the user already owns a mixnode
|
// the same holds if the user already owns a mixnode
|
||||||
let sender2 = "mixnode-owner";
|
let sender2 = "mixnode-owner";
|
||||||
|
|
||||||
let mix_id = test_helpers::add_mixnode(
|
let mix_id = test.add_dummy_mixnode(sender2, None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
sender2,
|
|
||||||
vec![coin(100_000_000, TEST_COIN_DENOM)],
|
|
||||||
);
|
|
||||||
|
|
||||||
let info = mock_info(sender2, &fixtures::good_gateway_pledge());
|
let info = mock_info(sender2, &fixtures::good_gateway_pledge());
|
||||||
let (gateway, sig) = test_helpers::gateway_with_signature(&mut rng, sender2);
|
let (gateway, sig) = test.gateway_with_signature(sender2, None);
|
||||||
|
|
||||||
let result = try_add_gateway(
|
let result = try_add_gateway(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
env.clone(),
|
env.clone(),
|
||||||
info.clone(),
|
info.clone(),
|
||||||
gateway.clone(),
|
gateway.clone(),
|
||||||
@@ -282,12 +269,84 @@ pub mod tests {
|
|||||||
assert_eq!(Err(MixnetContractError::AlreadyOwnsMixnode), result);
|
assert_eq!(Err(MixnetContractError::AlreadyOwnsMixnode), result);
|
||||||
|
|
||||||
// but after he unbonds it, it's all fine again
|
// but after he unbonds it, it's all fine again
|
||||||
pending_events::unbond_mixnode(deps.as_mut(), &env, 123, mix_id).unwrap();
|
pending_events::unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
|
||||||
|
|
||||||
let result = try_add_gateway(deps.as_mut(), env, info, gateway, sig);
|
let result = try_add_gateway(test.deps_mut(), env, info, gateway, sig);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adding_gateway_with_invalid_signatures() {
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
let env = test.env();
|
||||||
|
|
||||||
|
let sender = "alice";
|
||||||
|
let pledge = good_mixnode_pledge();
|
||||||
|
let info = mock_info(sender, pledge.as_ref());
|
||||||
|
|
||||||
|
let (gateway, signature) = test.gateway_with_signature(sender, Some(pledge.clone()));
|
||||||
|
|
||||||
|
// using different parameters than what the signature was made on
|
||||||
|
let mut modified_gateway = gateway.clone();
|
||||||
|
modified_gateway.mix_port += 1;
|
||||||
|
let res = try_add_gateway(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
info,
|
||||||
|
modified_gateway,
|
||||||
|
signature.clone(),
|
||||||
|
);
|
||||||
|
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||||
|
|
||||||
|
// even stake amount is protected
|
||||||
|
let mut different_pledge = pledge.clone();
|
||||||
|
different_pledge[0].amount += Uint128::new(12345);
|
||||||
|
|
||||||
|
let info = mock_info(sender, different_pledge.as_ref());
|
||||||
|
let res = try_add_gateway(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
info.clone(),
|
||||||
|
gateway.clone(),
|
||||||
|
signature.clone(),
|
||||||
|
);
|
||||||
|
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||||
|
|
||||||
|
let other_sender = mock_info("another-sender", pledge.as_ref());
|
||||||
|
let res = try_add_gateway(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
other_sender,
|
||||||
|
gateway.clone(),
|
||||||
|
signature.clone(),
|
||||||
|
);
|
||||||
|
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||||
|
|
||||||
|
// trying to reuse the same signature for another bonding fails (because nonce doesn't match!)
|
||||||
|
let info = mock_info(sender, pledge.as_ref());
|
||||||
|
let current_nonce =
|
||||||
|
signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(0, current_nonce);
|
||||||
|
let res = try_add_gateway(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
info.clone(),
|
||||||
|
gateway.clone(),
|
||||||
|
signature.clone(),
|
||||||
|
);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let updated_nonce =
|
||||||
|
signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(1, updated_nonce);
|
||||||
|
|
||||||
|
_try_remove_gateway(test.deps_mut(), Addr::unchecked(sender), None).unwrap();
|
||||||
|
|
||||||
|
let res = try_add_gateway(test.deps_mut(), env, info, gateway, signature);
|
||||||
|
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gateway_add_with_illegal_proxy() {
|
fn gateway_add_with_illegal_proxy() {
|
||||||
let mut test = TestSetup::new();
|
let mut test = TestSetup::new();
|
||||||
@@ -297,7 +356,7 @@ pub mod tests {
|
|||||||
let vesting_contract = test.vesting_contract();
|
let vesting_contract = test.vesting_contract();
|
||||||
|
|
||||||
let owner = "alice";
|
let owner = "alice";
|
||||||
let (gateway, sig) = test_helpers::gateway_with_signature(&mut test.rng, owner);
|
let (gateway, sig) = test.gateway_with_signature(owner, None);
|
||||||
|
|
||||||
let res = try_add_gateway_on_behalf(
|
let res = try_add_gateway_on_behalf(
|
||||||
test.deps_mut(),
|
test.deps_mut(),
|
||||||
@@ -320,14 +379,13 @@ pub mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gateway_remove() {
|
fn gateway_remove() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let mut rng = test_helpers::test_rng();
|
let env = test.env();
|
||||||
let env = mock_env();
|
|
||||||
|
|
||||||
// try unbond when no nodes exist yet
|
// try unbond when no nodes exist yet
|
||||||
let info = mock_info("anyone", &[]);
|
let info = mock_info("anyone", &[]);
|
||||||
let msg = ExecuteMsg::UnbondGateway {};
|
let msg = ExecuteMsg::UnbondGateway {};
|
||||||
let result = execute(deps.as_mut(), mock_env(), info, msg);
|
let result = execute(test.deps_mut(), env.clone(), info, msg);
|
||||||
|
|
||||||
// we're told that there is no node for our address
|
// we're told that there is no node for our address
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -338,18 +396,12 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// let's add a node owned by bob
|
// let's add a node owned by bob
|
||||||
test_helpers::add_gateway(
|
test.add_dummy_gateway("bob", None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
"bob",
|
|
||||||
fixtures::good_gateway_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// attempt to unbond fred's node, which doesn't exist
|
// attempt to unbond fred's node, which doesn't exist
|
||||||
let info = mock_info("fred", &[]);
|
let info = mock_info("fred", &[]);
|
||||||
let msg = ExecuteMsg::UnbondGateway {};
|
let msg = ExecuteMsg::UnbondGateway {};
|
||||||
let result = execute(deps.as_mut(), mock_env(), info, msg);
|
let result = execute(test.deps_mut(), env.clone(), info, msg);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Err(MixnetContractError::NoAssociatedGatewayBond {
|
Err(MixnetContractError::NoAssociatedGatewayBond {
|
||||||
@@ -358,28 +410,27 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// bob's node is still there
|
// bob's node is still there
|
||||||
let nodes = tests::queries::get_gateways(&mut deps);
|
let nodes = queries::query_gateways_paged(test.deps(), None, None)
|
||||||
|
.unwrap()
|
||||||
|
.nodes;
|
||||||
assert_eq!(1, nodes.len());
|
assert_eq!(1, nodes.len());
|
||||||
|
|
||||||
let first_node = &nodes[0];
|
let first_node = &nodes[0];
|
||||||
assert_eq!(&Addr::unchecked("bob"), first_node.owner());
|
assert_eq!(&Addr::unchecked("bob"), first_node.owner());
|
||||||
|
|
||||||
// add a node owned by fred
|
// add a node owned by fred
|
||||||
let fred_identity = test_helpers::add_gateway(
|
let fred_identity = test.add_dummy_gateway("fred", None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env,
|
|
||||||
"fred",
|
|
||||||
tests::fixtures::good_gateway_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// let's make sure we now have 2 nodes:
|
// let's make sure we now have 2 nodes:
|
||||||
assert_eq!(2, tests::queries::get_gateways(&mut deps).len());
|
let nodes = queries::query_gateways_paged(test.deps(), None, None)
|
||||||
|
.unwrap()
|
||||||
|
.nodes;
|
||||||
|
assert_eq!(2, nodes.len());
|
||||||
|
|
||||||
// unbond fred's node
|
// unbond fred's node
|
||||||
let info = mock_info("fred", &[]);
|
let info = mock_info("fred", &[]);
|
||||||
let msg = ExecuteMsg::UnbondGateway {};
|
let msg = ExecuteMsg::UnbondGateway {};
|
||||||
let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
|
let remove_fred = execute(test.deps_mut(), env, info.clone(), msg).unwrap();
|
||||||
|
|
||||||
// we should see a funds transfer from the contract back to fred
|
// we should see a funds transfer from the contract back to fred
|
||||||
let expected_message = BankMsg::Send {
|
let expected_message = BankMsg::Send {
|
||||||
@@ -401,9 +452,11 @@ pub mod tests {
|
|||||||
assert_eq!(expected_response, remove_fred);
|
assert_eq!(expected_response, remove_fred);
|
||||||
|
|
||||||
// only 1 node now exists, owned by bob:
|
// only 1 node now exists, owned by bob:
|
||||||
let gateway_bonds = tests::queries::get_gateways(&mut deps);
|
let nodes = queries::query_gateways_paged(test.deps(), None, None)
|
||||||
assert_eq!(1, gateway_bonds.len());
|
.unwrap()
|
||||||
assert_eq!(&Addr::unchecked("bob"), gateway_bonds[0].owner());
|
.nodes;
|
||||||
|
assert_eq!(1, nodes.len());
|
||||||
|
assert_eq!(&Addr::unchecked("bob"), nodes[0].owner());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ mod mixnet_contract_settings;
|
|||||||
mod mixnodes;
|
mod mixnodes;
|
||||||
mod queued_migrations;
|
mod queued_migrations;
|
||||||
mod rewards;
|
mod rewards;
|
||||||
|
mod signing;
|
||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
#[cfg(feature = "contract-testing")]
|
#[cfg(feature = "contract-testing")]
|
||||||
|
|||||||
@@ -3,5 +3,6 @@
|
|||||||
|
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
pub mod queries;
|
pub mod queries;
|
||||||
|
pub mod signature_helpers;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub mod transactions;
|
pub mod transactions;
|
||||||
|
|||||||
@@ -273,30 +273,24 @@ pub(crate) mod tests {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod mixnode_bonds {
|
mod mixnode_bonds {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::support::tests::fixtures::good_mixnode_pledge;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn obeys_limits() {
|
fn obeys_limits() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
test.add_dummy_mixnodes(1000);
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
let limit = 2;
|
let limit = 2;
|
||||||
|
|
||||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(limit)).unwrap();
|
||||||
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(limit)).unwrap();
|
|
||||||
assert_eq!(limit, page1.nodes.len() as u32);
|
assert_eq!(limit, page1.nodes.len() as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn has_default_limit() {
|
fn has_default_limit() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
test.add_dummy_mixnodes(1000);
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
|
||||||
|
|
||||||
// query without explicitly setting a limit
|
// query without explicitly setting a limit
|
||||||
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, None).unwrap();
|
let page1 = query_mixnode_bonds_paged(test.deps(), None, None).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MIXNODE_BOND_DEFAULT_RETRIEVAL_LIMIT,
|
MIXNODE_BOND_DEFAULT_RETRIEVAL_LIMIT,
|
||||||
@@ -306,14 +300,12 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn has_max_limit() {
|
fn has_max_limit() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
test.add_dummy_mixnodes(1000);
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
|
||||||
|
|
||||||
// query with a crazily high limit in an attempt to use too many resources
|
// query with a crazily high limit in an attempt to use too many resources
|
||||||
let crazy_limit = 1000;
|
let crazy_limit = 1000;
|
||||||
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(crazy_limit)).unwrap();
|
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(crazy_limit)).unwrap();
|
||||||
|
|
||||||
// we default to a decent sized upper bound instead
|
// we default to a decent sized upper bound instead
|
||||||
assert_eq!(MIXNODE_BOND_MAX_RETRIEVAL_LIMIT, page1.nodes.len() as u32);
|
assert_eq!(MIXNODE_BOND_MAX_RETRIEVAL_LIMIT, page1.nodes.len() as u32);
|
||||||
@@ -322,63 +314,43 @@ pub(crate) mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn pagination_works() {
|
fn pagination_works() {
|
||||||
// as we add mixnodes, we're always inserting them in ascending manner due to monotonically increasing id
|
// as we add mixnodes, we're always inserting them in ascending manner due to monotonically increasing id
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
test_helpers::add_mixnode(
|
test.add_dummy_mixnode("addr1", None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
"addr1",
|
|
||||||
good_mixnode_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let per_page = 2;
|
let per_page = 2;
|
||||||
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||||
|
|
||||||
// page should have 1 result on it
|
// page should have 1 result on it
|
||||||
assert_eq!(1, page1.nodes.len());
|
assert_eq!(1, page1.nodes.len());
|
||||||
|
|
||||||
// save another
|
// save another
|
||||||
test_helpers::add_mixnode(
|
test.add_dummy_mixnode("addr2", None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
"addr2",
|
|
||||||
good_mixnode_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// page1 should have 2 results on it
|
// page1 should have 2 results on it
|
||||||
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||||
assert_eq!(2, page1.nodes.len());
|
assert_eq!(2, page1.nodes.len());
|
||||||
|
|
||||||
test_helpers::add_mixnode(
|
test.add_dummy_mixnode("addr3", None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
"addr3",
|
|
||||||
good_mixnode_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// page1 still has the same 2 results
|
// page1 still has the same 2 results
|
||||||
let another_page1 =
|
let another_page1 =
|
||||||
query_mixnode_bonds_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||||
assert_eq!(2, another_page1.nodes.len());
|
assert_eq!(2, another_page1.nodes.len());
|
||||||
assert_eq!(page1, another_page1);
|
assert_eq!(page1, another_page1);
|
||||||
|
|
||||||
// retrieving the next page should start after the last key on this page
|
// retrieving the next page should start after the last key on this page
|
||||||
let start_after = page1.start_next_after.unwrap();
|
let start_after = page1.start_next_after.unwrap();
|
||||||
let page2 = query_mixnode_bonds_paged(deps.as_ref(), Some(start_after), Some(per_page))
|
let page2 =
|
||||||
.unwrap();
|
query_mixnode_bonds_paged(test.deps(), Some(start_after), Some(per_page)).unwrap();
|
||||||
|
|
||||||
assert_eq!(1, page2.nodes.len());
|
assert_eq!(1, page2.nodes.len());
|
||||||
|
|
||||||
// save another one
|
// save another one
|
||||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "addr4", good_mixnode_pledge());
|
test.add_dummy_mixnode("addr4", None);
|
||||||
|
|
||||||
let page2 = query_mixnode_bonds_paged(deps.as_ref(), Some(start_after), Some(per_page))
|
let page2 =
|
||||||
.unwrap();
|
query_mixnode_bonds_paged(test.deps(), Some(start_after), Some(per_page)).unwrap();
|
||||||
|
|
||||||
// now we have 2 pages, with 2 results on the second page
|
// now we have 2 pages, with 2 results on the second page
|
||||||
assert_eq!(2, page2.nodes.len());
|
assert_eq!(2, page2.nodes.len());
|
||||||
@@ -388,29 +360,24 @@ pub(crate) mod tests {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod mixnode_details {
|
mod mixnode_details {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::support::tests::fixtures::good_mixnode_pledge;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn obeys_limits() {
|
fn obeys_limits() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
test.add_dummy_mixnodes(1000);
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
let limit = 2;
|
let limit = 2;
|
||||||
|
|
||||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(limit)).unwrap();
|
||||||
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, Some(limit)).unwrap();
|
|
||||||
assert_eq!(limit, page1.nodes.len() as u32);
|
assert_eq!(limit, page1.nodes.len() as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn has_default_limit() {
|
fn has_default_limit() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
test.add_dummy_mixnodes(1000);
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
|
||||||
|
|
||||||
// query without explicitly setting a limit
|
// query without explicitly setting a limit
|
||||||
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, None).unwrap();
|
let page1 = query_mixnodes_details_paged(test.deps(), None, None).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MIXNODE_DETAILS_DEFAULT_RETRIEVAL_LIMIT,
|
MIXNODE_DETAILS_DEFAULT_RETRIEVAL_LIMIT,
|
||||||
@@ -420,15 +387,12 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn has_max_limit() {
|
fn has_max_limit() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
test.add_dummy_mixnodes(1000);
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
|
||||||
|
|
||||||
// query with a crazily high limit in an attempt to use too many resources
|
// query with a crazily high limit in an attempt to use too many resources
|
||||||
let crazy_limit = 1000;
|
let crazy_limit = 1000;
|
||||||
let page1 =
|
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(crazy_limit)).unwrap();
|
||||||
query_mixnodes_details_paged(deps.as_ref(), None, Some(crazy_limit)).unwrap();
|
|
||||||
|
|
||||||
// we default to a decent sized upper bound instead
|
// we default to a decent sized upper bound instead
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -440,64 +404,44 @@ pub(crate) mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn pagination_works() {
|
fn pagination_works() {
|
||||||
// as we add mixnodes, we're always inserting them in ascending manner due to monotonically increasing id
|
// as we add mixnodes, we're always inserting them in ascending manner due to monotonically increasing id
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
test_helpers::add_mixnode(
|
test.add_dummy_mixnode("addr1", None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
"addr1",
|
|
||||||
good_mixnode_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let per_page = 2;
|
let per_page = 2;
|
||||||
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||||
|
|
||||||
// page should have 1 result on it
|
// page should have 1 result on it
|
||||||
assert_eq!(1, page1.nodes.len());
|
assert_eq!(1, page1.nodes.len());
|
||||||
|
|
||||||
// save another
|
// save another
|
||||||
test_helpers::add_mixnode(
|
test.add_dummy_mixnode("addr2", None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
"addr2",
|
|
||||||
good_mixnode_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// page1 should have 2 results on it
|
// page1 should have 2 results on it
|
||||||
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||||
assert_eq!(2, page1.nodes.len());
|
assert_eq!(2, page1.nodes.len());
|
||||||
|
|
||||||
test_helpers::add_mixnode(
|
test.add_dummy_mixnode("addr3", None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
"addr3",
|
|
||||||
good_mixnode_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// page1 still has the same 2 results
|
// page1 still has the same 2 results
|
||||||
let another_page1 =
|
let another_page1 =
|
||||||
query_mixnodes_details_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||||
assert_eq!(2, another_page1.nodes.len());
|
assert_eq!(2, another_page1.nodes.len());
|
||||||
assert_eq!(page1, another_page1);
|
assert_eq!(page1, another_page1);
|
||||||
|
|
||||||
// retrieving the next page should start after the last key on this page
|
// retrieving the next page should start after the last key on this page
|
||||||
let start_after = page1.start_next_after.unwrap();
|
let start_after = page1.start_next_after.unwrap();
|
||||||
let page2 =
|
let page2 =
|
||||||
query_mixnodes_details_paged(deps.as_ref(), Some(start_after), Some(per_page))
|
query_mixnodes_details_paged(test.deps(), Some(start_after), Some(per_page))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(1, page2.nodes.len());
|
assert_eq!(1, page2.nodes.len());
|
||||||
|
|
||||||
// save another one
|
// save another one
|
||||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "addr4", good_mixnode_pledge());
|
test.add_dummy_mixnode("addr4", None);
|
||||||
|
|
||||||
let page2 =
|
let page2 =
|
||||||
query_mixnodes_details_paged(deps.as_ref(), Some(start_after), Some(per_page))
|
query_mixnodes_details_paged(test.deps(), Some(start_after), Some(per_page))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// now we have 2 pages, with 2 results on the second page
|
// now we have 2 pages, with 2 results on the second page
|
||||||
@@ -1135,26 +1079,18 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn query_for_owned_mixnode() {
|
fn query_for_owned_mixnode() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
let address = "mix-owner".to_string();
|
let address = "mix-owner".to_string();
|
||||||
|
|
||||||
// when it doesnt exist
|
// when it doesnt exist
|
||||||
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
|
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
|
||||||
assert!(res.mixnode_details.is_none());
|
assert!(res.mixnode_details.is_none());
|
||||||
assert_eq!(address, res.address);
|
assert_eq!(address, res.address);
|
||||||
|
|
||||||
// when it [fully] exists
|
// when it [fully] exists
|
||||||
let id = test_helpers::add_mixnode(
|
let id = test.add_dummy_mixnode(&address, None);
|
||||||
&mut rng,
|
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
|
||||||
deps.as_mut(),
|
|
||||||
env,
|
|
||||||
&address,
|
|
||||||
good_mixnode_pledge(),
|
|
||||||
);
|
|
||||||
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
|
|
||||||
let details = res.mixnode_details.unwrap();
|
let details = res.mixnode_details.unwrap();
|
||||||
assert_eq!(address, details.bond_information.owner);
|
assert_eq!(address, details.bond_information.owner);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1171,30 +1107,27 @@ pub(crate) mod tests {
|
|||||||
rewarding_details.delegates = Decimal::raw(12345);
|
rewarding_details.delegates = Decimal::raw(12345);
|
||||||
rewarding_details.unique_delegations = 1;
|
rewarding_details.unique_delegations = 1;
|
||||||
rewards_storage::MIXNODE_REWARDING
|
rewards_storage::MIXNODE_REWARDING
|
||||||
.save(deps.as_mut().storage, id, &rewarding_details)
|
.save(test.deps_mut().storage, id, &rewarding_details)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), 123, id).unwrap();
|
pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, id).unwrap();
|
||||||
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
|
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
|
||||||
assert!(res.mixnode_details.is_none());
|
assert!(res.mixnode_details.is_none());
|
||||||
assert_eq!(address, res.address);
|
assert_eq!(address, res.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn query_for_mixnode_details() {
|
fn query_for_mixnode_details() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
// no node under this id
|
// no node under this id
|
||||||
let res = query_mixnode_details(deps.as_ref(), 42).unwrap();
|
let res = query_mixnode_details(test.deps(), 42).unwrap();
|
||||||
assert!(res.mixnode_details.is_none());
|
assert!(res.mixnode_details.is_none());
|
||||||
assert_eq!(42, res.mix_id);
|
assert_eq!(42, res.mix_id);
|
||||||
|
|
||||||
// it exists
|
// it exists
|
||||||
let mix_id =
|
let mix_id = test.add_dummy_mixnode("foomp", None);
|
||||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "foomp", good_mixnode_pledge());
|
let res = query_mixnode_details(test.deps(), mix_id).unwrap();
|
||||||
let res = query_mixnode_details(deps.as_ref(), mix_id).unwrap();
|
|
||||||
let details = res.mixnode_details.unwrap();
|
let details = res.mixnode_details.unwrap();
|
||||||
assert_eq!(mix_id, details.bond_information.mix_id);
|
assert_eq!(mix_id, details.bond_information.mix_id);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1227,18 +1160,15 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn query_for_mixnode_rewarding_details() {
|
fn query_for_mixnode_rewarding_details() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
// no node under this id
|
// no node under this id
|
||||||
let res = query_mixnode_rewarding_details(deps.as_ref(), 42).unwrap();
|
let res = query_mixnode_rewarding_details(test.deps(), 42).unwrap();
|
||||||
assert!(res.rewarding_details.is_none());
|
assert!(res.rewarding_details.is_none());
|
||||||
assert_eq!(42, res.mix_id);
|
assert_eq!(42, res.mix_id);
|
||||||
|
|
||||||
let mix_id =
|
let mix_id = test.add_dummy_mixnode("foomp", None);
|
||||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "foomp", good_mixnode_pledge());
|
let res = query_mixnode_rewarding_details(test.deps(), mix_id).unwrap();
|
||||||
let res = query_mixnode_rewarding_details(deps.as_ref(), mix_id).unwrap();
|
|
||||||
let details = res.rewarding_details.unwrap();
|
let details = res.rewarding_details.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fixtures::mix_node_cost_params_fixture(),
|
fixtures::mix_node_cost_params_fixture(),
|
||||||
@@ -1249,80 +1179,74 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn query_for_unbonded_mixnode() {
|
fn query_for_unbonded_mixnode() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
let sender = "mix-owner";
|
let sender = "mix-owner";
|
||||||
|
|
||||||
// no node under this id
|
// no node under this id
|
||||||
let res = query_unbonded_mixnode(deps.as_ref(), 42).unwrap();
|
let res = query_unbonded_mixnode(test.deps(), 42).unwrap();
|
||||||
assert!(res.unbonded_info.is_none());
|
assert!(res.unbonded_info.is_none());
|
||||||
assert_eq!(42, res.mix_id);
|
assert_eq!(42, res.mix_id);
|
||||||
|
|
||||||
// add and unbond the mixnode
|
// add and unbond the mixnode
|
||||||
let mix_id =
|
let mix_id = test.add_dummy_mixnode(sender, None);
|
||||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, sender, good_mixnode_pledge());
|
pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, mix_id).unwrap();
|
||||||
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), 123, mix_id).unwrap();
|
|
||||||
|
|
||||||
let res = query_unbonded_mixnode(deps.as_ref(), mix_id).unwrap();
|
let res = query_unbonded_mixnode(test.deps(), mix_id).unwrap();
|
||||||
assert_eq!(res.unbonded_info.unwrap().owner, sender);
|
assert_eq!(res.unbonded_info.unwrap().owner, sender);
|
||||||
assert_eq!(mix_id, res.mix_id);
|
assert_eq!(mix_id, res.mix_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn query_for_stake_saturation() {
|
fn query_for_stake_saturation() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
// no node under this id
|
// no node under this id
|
||||||
let res = query_stake_saturation(deps.as_ref(), 42).unwrap();
|
let res = query_stake_saturation(test.deps(), 42).unwrap();
|
||||||
assert!(res.current_saturation.is_none());
|
assert!(res.current_saturation.is_none());
|
||||||
assert!(res.uncapped_saturation.is_none());
|
assert!(res.uncapped_saturation.is_none());
|
||||||
assert_eq!(42, res.mix_id);
|
assert_eq!(42, res.mix_id);
|
||||||
|
|
||||||
let rewarding_params = rewards_storage::REWARDING_PARAMS
|
let rewarding_params = rewards_storage::REWARDING_PARAMS
|
||||||
.load(deps.as_ref().storage)
|
.load(test.deps().storage)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let saturation_point = rewarding_params.interval.stake_saturation_point;
|
let saturation_point = rewarding_params.interval.stake_saturation_point;
|
||||||
|
|
||||||
let mix_id =
|
let mix_id = test.add_dummy_mixnode("foomp", None);
|
||||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "foomp", good_mixnode_pledge());
|
|
||||||
|
|
||||||
// below saturation point
|
// below saturation point
|
||||||
// there's only the base pledge without any delegation
|
// there's only the base pledge without any delegation
|
||||||
let expected =
|
let expected =
|
||||||
Decimal::from_atomics(good_mixnode_pledge()[0].amount, 0).unwrap() / saturation_point;
|
Decimal::from_atomics(good_mixnode_pledge()[0].amount, 0).unwrap() / saturation_point;
|
||||||
let res = query_stake_saturation(deps.as_ref(), mix_id).unwrap();
|
let res = query_stake_saturation(test.deps(), mix_id).unwrap();
|
||||||
assert_eq!(expected, res.current_saturation.unwrap());
|
assert_eq!(expected, res.current_saturation.unwrap());
|
||||||
assert_eq!(expected, res.uncapped_saturation.unwrap());
|
assert_eq!(expected, res.uncapped_saturation.unwrap());
|
||||||
assert_eq!(mix_id, res.mix_id);
|
assert_eq!(mix_id, res.mix_id);
|
||||||
|
|
||||||
// exactly at saturation point
|
// exactly at saturation point
|
||||||
let mut mix_rewarding = rewards_storage::MIXNODE_REWARDING
|
let mut mix_rewarding = rewards_storage::MIXNODE_REWARDING
|
||||||
.load(deps.as_ref().storage, mix_id)
|
.load(test.deps().storage, mix_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
mix_rewarding.operator = saturation_point;
|
mix_rewarding.operator = saturation_point;
|
||||||
rewards_storage::MIXNODE_REWARDING
|
rewards_storage::MIXNODE_REWARDING
|
||||||
.save(deps.as_mut().storage, mix_id, &mix_rewarding)
|
.save(test.deps_mut().storage, mix_id, &mix_rewarding)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let res = query_stake_saturation(deps.as_ref(), mix_id).unwrap();
|
let res = query_stake_saturation(test.deps(), mix_id).unwrap();
|
||||||
assert_eq!(Decimal::one(), res.current_saturation.unwrap());
|
assert_eq!(Decimal::one(), res.current_saturation.unwrap());
|
||||||
assert_eq!(Decimal::one(), res.uncapped_saturation.unwrap());
|
assert_eq!(Decimal::one(), res.uncapped_saturation.unwrap());
|
||||||
assert_eq!(mix_id, res.mix_id);
|
assert_eq!(mix_id, res.mix_id);
|
||||||
|
|
||||||
// above the saturation point
|
// above the saturation point
|
||||||
let mut mix_rewarding = rewards_storage::MIXNODE_REWARDING
|
let mut mix_rewarding = rewards_storage::MIXNODE_REWARDING
|
||||||
.load(deps.as_ref().storage, mix_id)
|
.load(test.deps().storage, mix_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
mix_rewarding.delegates = mix_rewarding.operator * Decimal::percent(150);
|
mix_rewarding.delegates = mix_rewarding.operator * Decimal::percent(150);
|
||||||
rewards_storage::MIXNODE_REWARDING
|
rewards_storage::MIXNODE_REWARDING
|
||||||
.save(deps.as_mut().storage, mix_id, &mix_rewarding)
|
.save(test.deps_mut().storage, mix_id, &mix_rewarding)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let res = query_stake_saturation(deps.as_ref(), mix_id).unwrap();
|
let res = query_stake_saturation(test.deps(), mix_id).unwrap();
|
||||||
assert_eq!(Decimal::one(), res.current_saturation.unwrap());
|
assert_eq!(Decimal::one(), res.current_saturation.unwrap());
|
||||||
assert_eq!(Decimal::percent(250), res.uncapped_saturation.unwrap());
|
assert_eq!(Decimal::percent(250), res.uncapped_saturation.unwrap());
|
||||||
assert_eq!(mix_id, res.mix_id);
|
assert_eq!(mix_id, res.mix_id);
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::signing::storage as signing_storage;
|
||||||
|
use cosmwasm_std::{Addr, Coin, Deps};
|
||||||
|
use mixnet_contract_common::error::MixnetContractError;
|
||||||
|
use mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNode, MixNodeCostParams};
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
|
use nym_contracts_common::signing::Verifier;
|
||||||
|
|
||||||
|
pub(crate) fn verify_mixnode_bonding_signature(
|
||||||
|
deps: Deps<'_>,
|
||||||
|
sender: Addr,
|
||||||
|
proxy: Option<Addr>,
|
||||||
|
pledge: Coin,
|
||||||
|
mixnode: MixNode,
|
||||||
|
cost_params: MixNodeCostParams,
|
||||||
|
signature: MessageSignature,
|
||||||
|
) -> Result<(), MixnetContractError> {
|
||||||
|
// recover the public key
|
||||||
|
let mut public_key = [0u8; 32];
|
||||||
|
bs58::decode(&mixnode.identity_key)
|
||||||
|
.into(&mut public_key)
|
||||||
|
.map_err(|err| MixnetContractError::MalformedEd25519IdentityKey(err.to_string()))?;
|
||||||
|
|
||||||
|
// reconstruct the payload
|
||||||
|
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
|
||||||
|
let msg =
|
||||||
|
construct_mixnode_bonding_sign_payload(nonce, sender, proxy, pledge, mixnode, cost_params);
|
||||||
|
|
||||||
|
if deps.api.verify_message(msg, signature, &public_key)? {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(MixnetContractError::InvalidEd25519Signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,10 +9,11 @@ use crate::mixnet_contract_settings::storage::rewarding_denom;
|
|||||||
use crate::mixnodes::helpers::{
|
use crate::mixnodes::helpers::{
|
||||||
get_mixnode_details_by_owner, must_get_mixnode_bond_by_owner, save_new_mixnode,
|
get_mixnode_details_by_owner, must_get_mixnode_bond_by_owner, save_new_mixnode,
|
||||||
};
|
};
|
||||||
|
use crate::mixnodes::signature_helpers::verify_mixnode_bonding_signature;
|
||||||
|
use crate::signing::storage as signing_storage;
|
||||||
use crate::support::helpers::{
|
use crate::support::helpers::{
|
||||||
ensure_bonded, ensure_epoch_in_progress_state, ensure_is_authorized, ensure_no_existing_bond,
|
ensure_bonded, ensure_epoch_in_progress_state, ensure_is_authorized, ensure_no_existing_bond,
|
||||||
ensure_proxy_match, ensure_sent_by_vesting_contract, validate_node_identity_signature,
|
ensure_proxy_match, ensure_sent_by_vesting_contract, validate_pledge,
|
||||||
validate_pledge,
|
|
||||||
};
|
};
|
||||||
use cosmwasm_std::{coin, Addr, Coin, DepsMut, Env, MessageInfo, Response, Storage};
|
use cosmwasm_std::{coin, Addr, Coin, DepsMut, Env, MessageInfo, Response, Storage};
|
||||||
use mixnet_contract_common::error::MixnetContractError;
|
use mixnet_contract_common::error::MixnetContractError;
|
||||||
@@ -24,6 +25,7 @@ use mixnet_contract_common::events::{
|
|||||||
use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||||
use mixnet_contract_common::pending_events::{PendingEpochEventKind, PendingIntervalEventKind};
|
use mixnet_contract_common::pending_events::{PendingEpochEventKind, PendingIntervalEventKind};
|
||||||
use mixnet_contract_common::{Layer, MixId, MixNode};
|
use mixnet_contract_common::{Layer, MixId, MixNode};
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
|
|
||||||
pub(crate) fn update_mixnode_layer(
|
pub(crate) fn update_mixnode_layer(
|
||||||
mix_id: MixId,
|
mix_id: MixId,
|
||||||
@@ -61,7 +63,7 @@ pub fn try_add_mixnode(
|
|||||||
info: MessageInfo,
|
info: MessageInfo,
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
) -> Result<Response, MixnetContractError> {
|
) -> Result<Response, MixnetContractError> {
|
||||||
_try_add_mixnode(
|
_try_add_mixnode(
|
||||||
deps,
|
deps,
|
||||||
@@ -82,7 +84,7 @@ pub fn try_add_mixnode_on_behalf(
|
|||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner: String,
|
owner: String,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
) -> Result<Response, MixnetContractError> {
|
) -> Result<Response, MixnetContractError> {
|
||||||
ensure_sent_by_vesting_contract(&info, deps.storage)?;
|
ensure_sent_by_vesting_contract(&info, deps.storage)?;
|
||||||
|
|
||||||
@@ -101,6 +103,9 @@ pub fn try_add_mixnode_on_behalf(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// I'm not entirely sure how to deal with this warning at the current moment
|
// I'm not entirely sure how to deal with this warning at the current moment
|
||||||
|
//
|
||||||
|
// TODO: perhaps also require the user to explicitly provide what it thinks is the current nonce
|
||||||
|
// so that we could return a better error message if it doesn't match?
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn _try_add_mixnode(
|
fn _try_add_mixnode(
|
||||||
deps: DepsMut<'_>,
|
deps: DepsMut<'_>,
|
||||||
@@ -109,7 +114,7 @@ fn _try_add_mixnode(
|
|||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
pledge: Vec<Coin>,
|
pledge: Vec<Coin>,
|
||||||
owner: Addr,
|
owner: Addr,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
proxy: Option<Addr>,
|
proxy: Option<Addr>,
|
||||||
) -> Result<Response, MixnetContractError> {
|
) -> Result<Response, MixnetContractError> {
|
||||||
// check if the pledge contains any funds of the appropriate denomination
|
// check if the pledge contains any funds of the appropriate denomination
|
||||||
@@ -126,13 +131,19 @@ fn _try_add_mixnode(
|
|||||||
// the bond information due to `UniqueIndex` constraint defined on those fields.
|
// the bond information due to `UniqueIndex` constraint defined on those fields.
|
||||||
|
|
||||||
// check if this sender actually owns the mixnode by checking the signature
|
// check if this sender actually owns the mixnode by checking the signature
|
||||||
validate_node_identity_signature(
|
verify_mixnode_bonding_signature(
|
||||||
deps.as_ref(),
|
deps.as_ref(),
|
||||||
&owner,
|
owner.clone(),
|
||||||
&owner_signature,
|
proxy.clone(),
|
||||||
&mixnode.identity_key,
|
pledge.clone(),
|
||||||
|
mixnode.clone(),
|
||||||
|
cost_params.clone(),
|
||||||
|
owner_signature,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// update the signing nonce associated with this sender so that the future signature would be made on the new value
|
||||||
|
signing_storage::increment_signing_nonce(deps.storage, owner.clone())?;
|
||||||
|
|
||||||
let node_identity = mixnode.identity_key.clone();
|
let node_identity = mixnode.identity_key.clone();
|
||||||
let (node_id, layer) = save_new_mixnode(
|
let (node_id, layer) = save_new_mixnode(
|
||||||
deps.storage,
|
deps.storage,
|
||||||
@@ -393,7 +404,7 @@ pub mod tests {
|
|||||||
use crate::support::tests::fixtures::{good_mixnode_pledge, TEST_COIN_DENOM};
|
use crate::support::tests::fixtures::{good_mixnode_pledge, TEST_COIN_DENOM};
|
||||||
use crate::support::tests::test_helpers::TestSetup;
|
use crate::support::tests::test_helpers::TestSetup;
|
||||||
use crate::support::tests::{fixtures, test_helpers};
|
use crate::support::tests::{fixtures, test_helpers};
|
||||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
use cosmwasm_std::testing::mock_info;
|
||||||
use cosmwasm_std::{Order, StdResult, Uint128};
|
use cosmwasm_std::{Order, StdResult, Uint128};
|
||||||
use mixnet_contract_common::{
|
use mixnet_contract_common::{
|
||||||
EpochState, EpochStatus, ExecuteMsg, Layer, LayerDistribution, Percent,
|
EpochState, EpochStatus, ExecuteMsg, Layer, LayerDistribution, Percent,
|
||||||
@@ -401,23 +412,23 @@ pub mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mixnode_add() {
|
fn mixnode_add() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let env = mock_env();
|
let env = test.env();
|
||||||
let mut rng = test_helpers::test_rng();
|
|
||||||
|
|
||||||
let sender = "alice";
|
let sender = "alice";
|
||||||
let minimum_pledge = minimum_mixnode_pledge(deps.as_ref().storage).unwrap();
|
let minimum_pledge = minimum_mixnode_pledge(test.deps().storage).unwrap();
|
||||||
let mut insufficient_pledge = minimum_pledge.clone();
|
let mut insufficient_pledge = minimum_pledge.clone();
|
||||||
insufficient_pledge.amount -= Uint128::new(1000);
|
insufficient_pledge.amount -= Uint128::new(1000);
|
||||||
|
|
||||||
// if we don't send enough funds
|
// if we don't send enough funds
|
||||||
let info = mock_info(sender, &[insufficient_pledge.clone()]);
|
let info = mock_info(sender, &[insufficient_pledge.clone()]);
|
||||||
let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut rng, sender);
|
let (mixnode, sig, _) =
|
||||||
|
test.mixnode_with_signature(sender, Some(vec![insufficient_pledge.clone()]));
|
||||||
let cost_params = fixtures::mix_node_cost_params_fixture();
|
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||||
|
|
||||||
// we are informed that we didn't send enough funds
|
// we are informed that we didn't send enough funds
|
||||||
let result = try_add_mixnode(
|
let result = try_add_mixnode(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
env.clone(),
|
env.clone(),
|
||||||
info,
|
info,
|
||||||
mixnode.clone(),
|
mixnode.clone(),
|
||||||
@@ -435,31 +446,12 @@ pub mod tests {
|
|||||||
// if the signature provided is invalid, the bonding also fails
|
// if the signature provided is invalid, the bonding also fails
|
||||||
let info = mock_info(sender, &[minimum_pledge]);
|
let info = mock_info(sender, &[minimum_pledge]);
|
||||||
|
|
||||||
let result = try_add_mixnode(
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
info.clone(),
|
|
||||||
mixnode.clone(),
|
|
||||||
cost_params.clone(),
|
|
||||||
"bad-signature".into(),
|
|
||||||
);
|
|
||||||
assert!(matches!(
|
|
||||||
result,
|
|
||||||
Err(MixnetContractError::MalformedEd25519Signature(..))
|
|
||||||
));
|
|
||||||
|
|
||||||
// if there was already a mixnode bonded by particular user
|
// if there was already a mixnode bonded by particular user
|
||||||
test_helpers::add_mixnode(
|
test.add_dummy_mixnode(sender, None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
sender,
|
|
||||||
fixtures::good_mixnode_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// it fails
|
// it fails
|
||||||
let result = try_add_mixnode(
|
let result = try_add_mixnode(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
env.clone(),
|
env.clone(),
|
||||||
info,
|
info,
|
||||||
mixnode,
|
mixnode,
|
||||||
@@ -471,19 +463,13 @@ pub mod tests {
|
|||||||
// the same holds if the user already owns a gateway
|
// the same holds if the user already owns a gateway
|
||||||
let sender2 = "gateway-owner";
|
let sender2 = "gateway-owner";
|
||||||
|
|
||||||
test_helpers::add_gateway(
|
test.add_dummy_gateway(sender2, None);
|
||||||
&mut rng,
|
|
||||||
deps.as_mut(),
|
|
||||||
env.clone(),
|
|
||||||
sender2,
|
|
||||||
tests::fixtures::good_gateway_pledge(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let info = mock_info(sender2, &tests::fixtures::good_mixnode_pledge());
|
let info = mock_info(sender2, &tests::fixtures::good_mixnode_pledge());
|
||||||
let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut rng, sender2);
|
let (mixnode, sig, _) = test.mixnode_with_signature(sender2, None);
|
||||||
|
|
||||||
let result = try_add_mixnode(
|
let result = try_add_mixnode(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
env.clone(),
|
env.clone(),
|
||||||
info.clone(),
|
info.clone(),
|
||||||
mixnode.clone(),
|
mixnode.clone(),
|
||||||
@@ -494,14 +480,14 @@ pub mod tests {
|
|||||||
|
|
||||||
// but after he unbonds it, it's all fine again
|
// but after he unbonds it, it's all fine again
|
||||||
let msg = ExecuteMsg::UnbondGateway {};
|
let msg = ExecuteMsg::UnbondGateway {};
|
||||||
execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
|
execute(test.deps_mut(), env.clone(), info.clone(), msg).unwrap();
|
||||||
|
|
||||||
let result = try_add_mixnode(deps.as_mut(), env, info, mixnode, cost_params, sig);
|
let result = try_add_mixnode(test.deps_mut(), env, info, mixnode, cost_params, sig);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
// make sure we got assigned the next id (note: we have already bonded a mixnode before in this test)
|
// make sure we got assigned the next id (note: we have already bonded a mixnode before in this test)
|
||||||
let bond = must_get_mixnode_bond_by_owner(deps.as_ref().storage, &Addr::unchecked(sender2))
|
let bond =
|
||||||
.unwrap();
|
must_get_mixnode_bond_by_owner(test.deps().storage, &Addr::unchecked(sender2)).unwrap();
|
||||||
assert_eq!(2, bond.mix_id);
|
assert_eq!(2, bond.mix_id);
|
||||||
|
|
||||||
// and make sure we're on layer 2 (because it was the next empty one)
|
// and make sure we're on layer 2 (because it was the next empty one)
|
||||||
@@ -513,10 +499,84 @@ pub mod tests {
|
|||||||
layer2: 1,
|
layer2: 1,
|
||||||
layer3: 0,
|
layer3: 0,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(expected, storage::LAYERS.load(test.deps().storage).unwrap())
|
||||||
expected,
|
}
|
||||||
storage::LAYERS.load(deps.as_ref().storage).unwrap()
|
|
||||||
)
|
#[test]
|
||||||
|
fn adding_mixnode_with_invalid_signatures() {
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
let env = test.env();
|
||||||
|
|
||||||
|
let sender = "alice";
|
||||||
|
let pledge = good_mixnode_pledge();
|
||||||
|
let info = mock_info(sender, pledge.as_ref());
|
||||||
|
|
||||||
|
let (mixnode, signature, _) = test.mixnode_with_signature(sender, Some(pledge.clone()));
|
||||||
|
// the above using cost params fixture
|
||||||
|
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||||
|
|
||||||
|
// using different parameters than what the signature was made on
|
||||||
|
let mut modified_mixnode = mixnode.clone();
|
||||||
|
modified_mixnode.mix_port += 1;
|
||||||
|
let res = try_add_mixnode(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
info,
|
||||||
|
modified_mixnode,
|
||||||
|
cost_params.clone(),
|
||||||
|
signature.clone(),
|
||||||
|
);
|
||||||
|
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||||
|
|
||||||
|
// even stake amount is protected
|
||||||
|
let mut different_pledge = pledge.clone();
|
||||||
|
different_pledge[0].amount += Uint128::new(12345);
|
||||||
|
|
||||||
|
let info = mock_info(sender, different_pledge.as_ref());
|
||||||
|
let res = try_add_mixnode(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
info.clone(),
|
||||||
|
mixnode.clone(),
|
||||||
|
cost_params.clone(),
|
||||||
|
signature.clone(),
|
||||||
|
);
|
||||||
|
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||||
|
|
||||||
|
let other_sender = mock_info("another-sender", pledge.as_ref());
|
||||||
|
let res = try_add_mixnode(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
other_sender,
|
||||||
|
mixnode.clone(),
|
||||||
|
cost_params.clone(),
|
||||||
|
signature.clone(),
|
||||||
|
);
|
||||||
|
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||||
|
|
||||||
|
// trying to reuse the same signature for another bonding fails (because nonce doesn't match!)
|
||||||
|
let info = mock_info(sender, pledge.as_ref());
|
||||||
|
let current_nonce =
|
||||||
|
signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(0, current_nonce);
|
||||||
|
let res = try_add_mixnode(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
info.clone(),
|
||||||
|
mixnode.clone(),
|
||||||
|
cost_params.clone(),
|
||||||
|
signature.clone(),
|
||||||
|
);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let updated_nonce =
|
||||||
|
signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(1, updated_nonce);
|
||||||
|
|
||||||
|
test.immediately_unbond_mixnode(1);
|
||||||
|
let res = try_add_mixnode(test.deps_mut(), env, info, mixnode, cost_params, signature);
|
||||||
|
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -528,10 +588,9 @@ pub mod tests {
|
|||||||
let vesting_contract = test.vesting_contract();
|
let vesting_contract = test.vesting_contract();
|
||||||
|
|
||||||
let owner = "alice";
|
let owner = "alice";
|
||||||
let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut test.rng, owner);
|
let (mixnode, sig, _) = test.mixnode_with_signature(owner, None);
|
||||||
let cost_params = fixtures::mix_node_cost_params_fixture();
|
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||||
|
|
||||||
// we are informed that we didn't send enough funds
|
|
||||||
let res = try_add_mixnode_on_behalf(
|
let res = try_add_mixnode_on_behalf(
|
||||||
test.deps_mut(),
|
test.deps_mut(),
|
||||||
env,
|
env,
|
||||||
@@ -920,52 +979,53 @@ pub mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn adding_mixnode_with_duplicate_sphinx_key_errors_out() {
|
fn adding_mixnode_with_duplicate_sphinx_key_errors_out() {
|
||||||
let mut deps = test_helpers::init_contract();
|
let mut test = TestSetup::new();
|
||||||
let mut rng = test_helpers::test_rng();
|
let env = test.env();
|
||||||
|
|
||||||
let keypair1 = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
let keypair1 = nym_crypto::asymmetric::identity::KeyPair::new(&mut test.rng);
|
||||||
let keypair2 = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
let keypair2 = nym_crypto::asymmetric::identity::KeyPair::new(&mut test.rng);
|
||||||
let sig1 = keypair1.private_key().sign_text("alice");
|
|
||||||
let sig2 = keypair1.private_key().sign_text("bob");
|
|
||||||
|
|
||||||
let info_alice = mock_info("alice", &tests::fixtures::good_mixnode_pledge());
|
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||||
let info_bob = mock_info("bob", &tests::fixtures::good_mixnode_pledge());
|
let mixnode1 = MixNode {
|
||||||
|
|
||||||
let mut mixnode = MixNode {
|
|
||||||
host: "1.2.3.4".to_string(),
|
host: "1.2.3.4".to_string(),
|
||||||
mix_port: 1234,
|
mix_port: 1234,
|
||||||
verloc_port: 1234,
|
verloc_port: 1234,
|
||||||
http_api_port: 1234,
|
http_api_port: 1234,
|
||||||
sphinx_key: nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng)
|
sphinx_key: nym_crypto::asymmetric::encryption::KeyPair::new(&mut test.rng)
|
||||||
.public_key()
|
.public_key()
|
||||||
.to_base58_string(),
|
.to_base58_string(),
|
||||||
identity_key: keypair1.public_key().to_base58_string(),
|
identity_key: keypair1.public_key().to_base58_string(),
|
||||||
version: "v0.1.2.3".to_string(),
|
version: "v0.1.2.3".to_string(),
|
||||||
};
|
};
|
||||||
let cost_params = fixtures::mix_node_cost_params_fixture();
|
|
||||||
|
// change identity but reuse sphinx key
|
||||||
|
let mut mixnode2 = mixnode1.clone();
|
||||||
|
mixnode2.sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut test.rng)
|
||||||
|
.public_key()
|
||||||
|
.to_base58_string();
|
||||||
|
|
||||||
|
let sig1 =
|
||||||
|
test.mixnode_bonding_signature(keypair1.private_key(), "alice", mixnode1.clone(), None);
|
||||||
|
let sig2 =
|
||||||
|
test.mixnode_bonding_signature(keypair2.private_key(), "bob", mixnode2.clone(), None);
|
||||||
|
|
||||||
|
let info_alice = mock_info("alice", &tests::fixtures::good_mixnode_pledge());
|
||||||
|
let info_bob = mock_info("bob", &tests::fixtures::good_mixnode_pledge());
|
||||||
|
|
||||||
assert!(try_add_mixnode(
|
assert!(try_add_mixnode(
|
||||||
deps.as_mut(),
|
test.deps_mut(),
|
||||||
mock_env(),
|
env.clone(),
|
||||||
info_alice,
|
info_alice,
|
||||||
mixnode.clone(),
|
mixnode1,
|
||||||
cost_params.clone(),
|
cost_params.clone(),
|
||||||
sig1
|
sig1
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
|
|
||||||
mixnode.identity_key = keypair2.public_key().to_base58_string();
|
|
||||||
|
|
||||||
// change identity but reuse sphinx key
|
// change identity but reuse sphinx key
|
||||||
assert!(try_add_mixnode(
|
assert!(
|
||||||
deps.as_mut(),
|
try_add_mixnode(test.deps_mut(), env, info_bob, mixnode2, cost_params, sig2).is_err()
|
||||||
mock_env(),
|
);
|
||||||
info_bob,
|
|
||||||
mixnode,
|
|
||||||
cost_params,
|
|
||||||
sig2
|
|
||||||
)
|
|
||||||
.is_err());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
pub mod queries;
|
||||||
|
pub mod storage;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::signing::storage::get_signing_nonce;
|
||||||
|
use cosmwasm_std::{Deps, StdResult};
|
||||||
|
use nym_contracts_common::signing::Nonce;
|
||||||
|
|
||||||
|
pub fn query_current_signing_nonce(deps: Deps<'_>, address: String) -> StdResult<Nonce> {
|
||||||
|
let address = deps.api.addr_validate(&address)?;
|
||||||
|
get_signing_nonce(deps.storage, address)
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::constants::SIGNING_NONCES_NAMESPACE;
|
||||||
|
use cosmwasm_std::{Addr, StdResult, Storage};
|
||||||
|
use cw_storage_plus::Map;
|
||||||
|
use nym_contracts_common::signing::Nonce;
|
||||||
|
|
||||||
|
pub const NONCES: Map<'_, Addr, Nonce> = Map::new(SIGNING_NONCES_NAMESPACE);
|
||||||
|
|
||||||
|
pub fn get_signing_nonce(storage: &dyn Storage, address: Addr) -> StdResult<Nonce> {
|
||||||
|
let nonce = NONCES.may_load(storage, address)?.unwrap_or(0);
|
||||||
|
Ok(nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_signing_nonce(
|
||||||
|
storage: &mut dyn Storage,
|
||||||
|
address: Addr,
|
||||||
|
value: Nonce,
|
||||||
|
) -> StdResult<()> {
|
||||||
|
NONCES.save(storage, address, &value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn increment_signing_nonce(storage: &mut dyn Storage, address: Addr) -> StdResult<()> {
|
||||||
|
// get the current nonce
|
||||||
|
let nonce = get_signing_nonce(storage, address.clone())?;
|
||||||
|
|
||||||
|
// increment it for the next use
|
||||||
|
update_signing_nonce(storage, address, nonce + 1)
|
||||||
|
}
|
||||||
@@ -377,7 +377,7 @@ pub fn validate_node_identity_signature(
|
|||||||
signature: &str,
|
signature: &str,
|
||||||
identity: IdentityKeyRef<'_>,
|
identity: IdentityKeyRef<'_>,
|
||||||
) -> Result<(), MixnetContractError> {
|
) -> Result<(), MixnetContractError> {
|
||||||
validate_signature(deps, owner.as_bytes(), signature, identity)
|
validate_ed25519_signature(deps, owner.as_bytes(), signature, identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_family_signature(
|
pub fn validate_family_signature(
|
||||||
@@ -386,10 +386,10 @@ pub fn validate_family_signature(
|
|||||||
signature: &str,
|
signature: &str,
|
||||||
family_head: IdentityKeyRef<'_>,
|
family_head: IdentityKeyRef<'_>,
|
||||||
) -> Result<(), MixnetContractError> {
|
) -> Result<(), MixnetContractError> {
|
||||||
validate_signature(deps, family_member.as_bytes(), signature, family_head)
|
validate_ed25519_signature(deps, family_member.as_bytes(), signature, family_head)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn validate_signature(
|
pub(crate) fn validate_ed25519_signature(
|
||||||
deps: Deps<'_>,
|
deps: Deps<'_>,
|
||||||
signed_bytes: &[u8],
|
signed_bytes: &[u8],
|
||||||
signature: &str,
|
signature: &str,
|
||||||
|
|||||||
@@ -1,25 +1,34 @@
|
|||||||
|
use cosmwasm_std::{Coin, Deps};
|
||||||
use mixnet_contract_common::{ExecuteMsg, Gateway, IdentityKey};
|
use mixnet_contract_common::{ExecuteMsg, Gateway, IdentityKey};
|
||||||
|
use nym_crypto::asymmetric::identity;
|
||||||
use rand_chacha::rand_core::{CryptoRng, RngCore};
|
use rand_chacha::rand_core::{CryptoRng, RngCore};
|
||||||
|
|
||||||
use crate::support::tests;
|
use crate::support::tests;
|
||||||
|
use crate::support::tests::test_helpers::{ed25519_sign_message, gateway_bonding_sign_payload};
|
||||||
|
|
||||||
pub(crate) fn valid_bond_gateway_msg(
|
pub(crate) fn valid_bond_gateway_msg(
|
||||||
mut rng: impl RngCore + CryptoRng,
|
mut rng: impl RngCore + CryptoRng,
|
||||||
|
deps: Deps<'_>,
|
||||||
|
stake: Vec<Coin>,
|
||||||
sender: &str,
|
sender: &str,
|
||||||
) -> (ExecuteMsg, IdentityKey) {
|
) -> (ExecuteMsg, IdentityKey) {
|
||||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
let keypair = identity::KeyPair::new(&mut rng);
|
||||||
let owner_signature = keypair
|
let identity_key = keypair.public_key().to_base58_string();
|
||||||
.private_key()
|
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
||||||
.sign(sender.as_bytes())
|
|
||||||
.to_base58_string();
|
let gateway = Gateway {
|
||||||
|
identity_key,
|
||||||
|
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||||
|
..tests::fixtures::gateway_fixture()
|
||||||
|
};
|
||||||
|
|
||||||
|
let msg = gateway_bonding_sign_payload(deps, sender, None, gateway.clone(), stake);
|
||||||
|
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||||
|
|
||||||
let identity_key = keypair.public_key().to_base58_string();
|
let identity_key = keypair.public_key().to_base58_string();
|
||||||
(
|
(
|
||||||
ExecuteMsg::BondGateway {
|
ExecuteMsg::BondGateway {
|
||||||
gateway: Gateway {
|
gateway,
|
||||||
identity_key: identity_key.clone(),
|
|
||||||
..tests::fixtures::gateway_fixture()
|
|
||||||
},
|
|
||||||
owner_signature,
|
owner_signature,
|
||||||
},
|
},
|
||||||
identity_key,
|
identity_key,
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
pub mod fixtures;
|
pub mod fixtures;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
#[cfg(test)]
|
|
||||||
pub mod queries;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test_helpers {
|
pub mod test_helpers {
|
||||||
@@ -24,7 +22,8 @@ pub mod test_helpers {
|
|||||||
use crate::interval::{pending_events, storage as interval_storage};
|
use crate::interval::{pending_events, storage as interval_storage};
|
||||||
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||||
use crate::mixnet_contract_settings::storage::{
|
use crate::mixnet_contract_settings::storage::{
|
||||||
minimum_mixnode_pledge, rewarding_denom, rewarding_validator_address,
|
minimum_gateway_pledge, minimum_mixnode_pledge, rewarding_denom,
|
||||||
|
rewarding_validator_address,
|
||||||
};
|
};
|
||||||
use crate::mixnodes::storage as mixnodes_storage;
|
use crate::mixnodes::storage as mixnodes_storage;
|
||||||
use crate::mixnodes::storage::mixnode_bonds;
|
use crate::mixnodes::storage::mixnode_bonds;
|
||||||
@@ -36,8 +35,11 @@ pub mod test_helpers {
|
|||||||
};
|
};
|
||||||
use crate::rewards::storage as rewards_storage;
|
use crate::rewards::storage as rewards_storage;
|
||||||
use crate::rewards::transactions::try_reward_mixnode;
|
use crate::rewards::transactions::try_reward_mixnode;
|
||||||
|
use crate::signing::storage as signing_storage;
|
||||||
use crate::support::tests;
|
use crate::support::tests;
|
||||||
use crate::support::tests::fixtures::TEST_COIN_DENOM;
|
use crate::support::tests::fixtures::{
|
||||||
|
good_gateway_pledge, good_mixnode_pledge, TEST_COIN_DENOM,
|
||||||
|
};
|
||||||
use cosmwasm_std::testing::mock_dependencies;
|
use cosmwasm_std::testing::mock_dependencies;
|
||||||
use cosmwasm_std::testing::mock_env;
|
use cosmwasm_std::testing::mock_env;
|
||||||
use cosmwasm_std::testing::mock_info;
|
use cosmwasm_std::testing::mock_info;
|
||||||
@@ -59,13 +61,19 @@ pub mod test_helpers {
|
|||||||
use mixnet_contract_common::rewarding::simulator::Simulator;
|
use mixnet_contract_common::rewarding::simulator::Simulator;
|
||||||
use mixnet_contract_common::rewarding::RewardDistribution;
|
use mixnet_contract_common::rewarding::RewardDistribution;
|
||||||
use mixnet_contract_common::{
|
use mixnet_contract_common::{
|
||||||
Delegation, EpochState, EpochStatus, Gateway, IdentityKey, InitialRewardingParams,
|
Delegation, EpochState, EpochStatus, Gateway, GatewayBondingPayload, IdentityKey,
|
||||||
InstantiateMsg, Interval, MixId, MixNode, MixNodeBond, Percent, RewardedSetNodeStatus,
|
InitialRewardingParams, InstantiateMsg, Interval, MixId, MixNode, MixNodeBond,
|
||||||
|
MixnodeBondingPayload, Percent, RewardedSetNodeStatus, SignableGatewayBondingMsg,
|
||||||
|
SignableMixNodeBondingMsg,
|
||||||
|
};
|
||||||
|
use nym_contracts_common::signing::{
|
||||||
|
ContractMessageContent, MessageSignature, SignableMessage, SigningAlgorithm, SigningPurpose,
|
||||||
};
|
};
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
use nym_crypto::asymmetric::identity::KeyPair;
|
use nym_crypto::asymmetric::identity::KeyPair;
|
||||||
use rand_chacha::rand_core::{CryptoRng, RngCore, SeedableRng};
|
use rand_chacha::rand_core::{CryptoRng, RngCore, SeedableRng};
|
||||||
use rand_chacha::ChaCha20Rng;
|
use rand_chacha::ChaCha20Rng;
|
||||||
|
use serde::Serialize;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub fn assert_eq_with_leeway(a: Uint128, b: Uint128, leeway: Uint128) {
|
pub fn assert_eq_with_leeway(a: Uint128, b: Uint128, leeway: Uint128) {
|
||||||
@@ -190,7 +198,7 @@ pub mod test_helpers {
|
|||||||
head: &str,
|
head: &str,
|
||||||
label: &str,
|
label: &str,
|
||||||
) -> (MixId, identity::KeyPair) {
|
) -> (MixId, identity::KeyPair) {
|
||||||
let (mix_id, keys) = self.add_dummy_mixnode_with_keypair(head, None);
|
let (mix_id, keys) = self.add_dummy_mixnode_with_proxy_and_keypair(head, None);
|
||||||
let sig = keys.private_key().sign_text(head);
|
let sig = keys.private_key().sign_text(head);
|
||||||
|
|
||||||
try_create_family(self.deps_mut(), mock_info(head, &[]), sig, label).unwrap();
|
try_create_family(self.deps_mut(), mock_info(head, &[]), sig, label).unwrap();
|
||||||
@@ -198,6 +206,57 @@ pub mod test_helpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_dummy_mixnode(&mut self, owner: &str, stake: Option<Uint128>) -> MixId {
|
pub fn add_dummy_mixnode(&mut self, owner: &str, stake: Option<Uint128>) -> MixId {
|
||||||
|
let stake = self.make_mix_pledge(stake);
|
||||||
|
let (mixnode, owner_signature, _) =
|
||||||
|
self.mixnode_with_signature(owner, Some(stake.clone()));
|
||||||
|
|
||||||
|
let info = mock_info(owner, stake.as_ref());
|
||||||
|
let current_id_counter = mixnodes_storage::MIXNODE_ID_COUNTER
|
||||||
|
.may_load(self.deps().storage)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let env = self.env();
|
||||||
|
|
||||||
|
try_add_mixnode(
|
||||||
|
self.deps_mut(),
|
||||||
|
env,
|
||||||
|
info,
|
||||||
|
mixnode,
|
||||||
|
tests::fixtures::mix_node_cost_params_fixture(),
|
||||||
|
owner_signature,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// newly added mixnode gets assigned the current counter + 1
|
||||||
|
current_id_counter + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_dummy_gateway(&mut self, sender: &str, stake: Option<Uint128>) -> IdentityKey {
|
||||||
|
let stake = self.make_gateway_pledge(stake);
|
||||||
|
let (gateway, owner_signature) =
|
||||||
|
self.gateway_with_signature(sender, Some(stake.clone()));
|
||||||
|
|
||||||
|
let info = mock_info(sender, &stake);
|
||||||
|
let key = gateway.identity_key.clone();
|
||||||
|
let env = self.env();
|
||||||
|
try_add_gateway(self.deps_mut(), env, info, gateway, owner_signature).unwrap();
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_dummy_mixnodes(&mut self, n: usize) {
|
||||||
|
for i in 0..n {
|
||||||
|
self.add_dummy_mixnode(&format!("owner{i}"), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_dummy_gateways(&mut self, n: usize) {
|
||||||
|
for i in 0..n {
|
||||||
|
self.add_dummy_gateway(&format!("owner{i}"), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_mix_pledge(&self, stake: Option<Uint128>) -> Vec<Coin> {
|
||||||
let stake = match stake {
|
let stake = match stake {
|
||||||
Some(amount) => {
|
Some(amount) => {
|
||||||
let denom = rewarding_denom(self.deps().storage).unwrap();
|
let denom = rewarding_denom(self.deps().storage).unwrap();
|
||||||
@@ -205,36 +264,61 @@ pub mod test_helpers {
|
|||||||
}
|
}
|
||||||
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
|
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
|
||||||
};
|
};
|
||||||
|
vec![stake]
|
||||||
let env = self.env();
|
|
||||||
add_mixnode(&mut self.rng, self.deps.as_mut(), env, owner, vec![stake])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_dummy_mixnode_with_keypair(
|
pub fn make_gateway_pledge(&self, stake: Option<Uint128>) -> Vec<Coin> {
|
||||||
|
let stake = match stake {
|
||||||
|
Some(amount) => {
|
||||||
|
let denom = rewarding_denom(self.deps().storage).unwrap();
|
||||||
|
Coin { denom, amount }
|
||||||
|
}
|
||||||
|
None => minimum_gateway_pledge(self.deps.as_ref().storage).unwrap(),
|
||||||
|
};
|
||||||
|
vec![stake]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mixnode_bonding_signature(
|
||||||
|
&mut self,
|
||||||
|
key: &identity::PrivateKey,
|
||||||
|
owner: &str,
|
||||||
|
mixnode: MixNode,
|
||||||
|
stake: Option<Uint128>,
|
||||||
|
) -> MessageSignature {
|
||||||
|
let stake = self.make_mix_pledge(stake);
|
||||||
|
let msg = mixnode_bonding_sign_payload(self.deps(), owner, None, mixnode, stake);
|
||||||
|
ed25519_sign_message(msg, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_dummy_mixnode_with_proxy_and_keypair(
|
||||||
&mut self,
|
&mut self,
|
||||||
owner: &str,
|
owner: &str,
|
||||||
stake: Option<Uint128>,
|
stake: Option<Uint128>,
|
||||||
) -> (MixId, identity::KeyPair) {
|
) -> (MixId, identity::KeyPair) {
|
||||||
let stake = match stake {
|
let stake = self.make_mix_pledge(stake);
|
||||||
Some(amount) => {
|
|
||||||
let denom = rewarding_denom(self.deps().storage).unwrap();
|
|
||||||
Coin { denom, amount }
|
|
||||||
}
|
|
||||||
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let proxy = self.vesting_contract();
|
let proxy = self.vesting_contract();
|
||||||
|
|
||||||
let keypair = identity::KeyPair::new(&mut self.rng);
|
let keypair = identity::KeyPair::new(&mut self.rng);
|
||||||
let owner_signature = keypair
|
let identity_key = keypair.public_key().to_base58_string();
|
||||||
.private_key()
|
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
||||||
.sign(owner.as_bytes())
|
|
||||||
.to_base58_string();
|
|
||||||
|
|
||||||
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
let mixnode = MixNode {
|
||||||
|
identity_key,
|
||||||
|
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||||
|
..tests::fixtures::mix_node_fixture()
|
||||||
|
};
|
||||||
|
|
||||||
let info = mock_info(proxy.as_str(), &[stake]);
|
let msg = mixnode_bonding_sign_payload(
|
||||||
let key = keypair.public_key().to_base58_string();
|
self.deps(),
|
||||||
|
owner,
|
||||||
|
Some(proxy.clone()),
|
||||||
|
mixnode.clone(),
|
||||||
|
stake.clone(),
|
||||||
|
);
|
||||||
|
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||||
|
|
||||||
|
let info = mock_info(proxy.as_str(), &stake);
|
||||||
let current_id_counter = mixnodes_storage::MIXNODE_ID_COUNTER
|
let current_id_counter = mixnodes_storage::MIXNODE_ID_COUNTER
|
||||||
.may_load(self.deps().storage)
|
.may_load(self.deps().storage)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -245,11 +329,7 @@ pub mod test_helpers {
|
|||||||
self.deps_mut(),
|
self.deps_mut(),
|
||||||
env,
|
env,
|
||||||
info,
|
info,
|
||||||
MixNode {
|
mixnode,
|
||||||
identity_key: key,
|
|
||||||
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
|
|
||||||
..tests::fixtures::mix_node_fixture()
|
|
||||||
},
|
|
||||||
tests::fixtures::mix_node_cost_params_fixture(),
|
tests::fixtures::mix_node_cost_params_fixture(),
|
||||||
owner.to_string(),
|
owner.to_string(),
|
||||||
owner_signature,
|
owner_signature,
|
||||||
@@ -265,7 +345,8 @@ pub mod test_helpers {
|
|||||||
owner: &str,
|
owner: &str,
|
||||||
stake: Option<Uint128>,
|
stake: Option<Uint128>,
|
||||||
) -> MixId {
|
) -> MixId {
|
||||||
self.add_dummy_mixnode_with_keypair(owner, stake).0
|
self.add_dummy_mixnode_with_proxy_and_keypair(owner, stake)
|
||||||
|
.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_illegal_mixnode_proxy(&mut self, mix_id: MixId, proxy: Addr) {
|
pub fn set_illegal_mixnode_proxy(&mut self, mix_id: MixId, proxy: Addr) {
|
||||||
@@ -312,14 +393,26 @@ pub mod test_helpers {
|
|||||||
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
|
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let keypair = identity::KeyPair::new(&mut self.rng);
|
||||||
|
let identity_key = keypair.public_key().to_base58_string();
|
||||||
|
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
||||||
|
|
||||||
let proxy = self.vesting_contract();
|
let proxy = self.vesting_contract();
|
||||||
|
|
||||||
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
let gateway = Gateway {
|
||||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut self.rng);
|
identity_key,
|
||||||
let owner_signature = keypair
|
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||||
.private_key()
|
..tests::fixtures::gateway_fixture()
|
||||||
.sign(owner.as_bytes())
|
};
|
||||||
.to_base58_string();
|
|
||||||
|
let msg = gateway_bonding_sign_payload(
|
||||||
|
self.deps(),
|
||||||
|
owner,
|
||||||
|
Some(proxy.clone()),
|
||||||
|
gateway.clone(),
|
||||||
|
vec![stake.clone()],
|
||||||
|
);
|
||||||
|
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||||
|
|
||||||
let env = self.env();
|
let env = self.env();
|
||||||
let info = mock_info(proxy.as_ref(), &[stake]);
|
let info = mock_info(proxy.as_ref(), &[stake]);
|
||||||
@@ -328,11 +421,7 @@ pub mod test_helpers {
|
|||||||
self.deps_mut(),
|
self.deps_mut(),
|
||||||
env,
|
env,
|
||||||
info,
|
info,
|
||||||
Gateway {
|
gateway,
|
||||||
identity_key: keypair.public_key().to_base58_string(),
|
|
||||||
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
|
|
||||||
..tests::fixtures::gateway_fixture()
|
|
||||||
},
|
|
||||||
owner.to_string(),
|
owner.to_string(),
|
||||||
owner_signature,
|
owner_signature,
|
||||||
)
|
)
|
||||||
@@ -351,6 +440,63 @@ pub mod test_helpers {
|
|||||||
mix_id
|
mix_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mixnode_with_signature(
|
||||||
|
&mut self,
|
||||||
|
sender: &str,
|
||||||
|
stake: Option<Vec<Coin>>,
|
||||||
|
) -> (MixNode, MessageSignature, KeyPair) {
|
||||||
|
let stake = stake.unwrap_or(good_mixnode_pledge());
|
||||||
|
|
||||||
|
let keypair = identity::KeyPair::new(&mut self.rng);
|
||||||
|
let identity_key = keypair.public_key().to_base58_string();
|
||||||
|
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
||||||
|
|
||||||
|
let mixnode = MixNode {
|
||||||
|
identity_key,
|
||||||
|
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||||
|
..tests::fixtures::mix_node_fixture()
|
||||||
|
};
|
||||||
|
let msg = mixnode_bonding_sign_payload(
|
||||||
|
self.deps(),
|
||||||
|
sender,
|
||||||
|
None,
|
||||||
|
mixnode.clone(),
|
||||||
|
stake.clone(),
|
||||||
|
);
|
||||||
|
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||||
|
|
||||||
|
(mixnode, owner_signature, keypair)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gateway_with_signature(
|
||||||
|
&mut self,
|
||||||
|
sender: &str,
|
||||||
|
stake: Option<Vec<Coin>>,
|
||||||
|
) -> (Gateway, MessageSignature) {
|
||||||
|
let stake = stake.unwrap_or(good_gateway_pledge());
|
||||||
|
|
||||||
|
let keypair = identity::KeyPair::new(&mut self.rng);
|
||||||
|
let identity_key = keypair.public_key().to_base58_string();
|
||||||
|
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
||||||
|
|
||||||
|
let gateway = Gateway {
|
||||||
|
identity_key,
|
||||||
|
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||||
|
..tests::fixtures::gateway_fixture()
|
||||||
|
};
|
||||||
|
|
||||||
|
let msg = gateway_bonding_sign_payload(
|
||||||
|
self.deps(),
|
||||||
|
sender,
|
||||||
|
None,
|
||||||
|
gateway.clone(),
|
||||||
|
stake.clone(),
|
||||||
|
);
|
||||||
|
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||||
|
|
||||||
|
(gateway, owner_signature)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start_unbonding_mixnode(&mut self, mix_id: MixId) {
|
pub fn start_unbonding_mixnode(&mut self, mix_id: MixId) {
|
||||||
let bond_details = mixnodes_storage::mixnode_bonds()
|
let bond_details = mixnodes_storage::mixnode_bonds()
|
||||||
.load(self.deps().storage, mix_id)
|
.load(self.deps().storage, mix_id)
|
||||||
@@ -714,6 +860,22 @@ pub mod test_helpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ed25519_sign_message<T: Serialize + SigningPurpose>(
|
||||||
|
message: SignableMessage<T>,
|
||||||
|
private_key: &identity::PrivateKey,
|
||||||
|
) -> MessageSignature {
|
||||||
|
match message.algorithm {
|
||||||
|
SigningAlgorithm::Ed25519 => {
|
||||||
|
let plaintext = message.to_plaintext().unwrap();
|
||||||
|
let signature = private_key.sign(&plaintext);
|
||||||
|
MessageSignature::from(signature.to_bytes().as_ref())
|
||||||
|
}
|
||||||
|
SigningAlgorithm::Secp256k1 => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn simulator_from_single_node_state(deps: Deps<'_>, node: MixId) -> Simulator {
|
pub fn simulator_from_single_node_state(deps: Deps<'_>, node: MixId) -> Simulator {
|
||||||
let mix_rewarding = rewards_storage::MIXNODE_REWARDING
|
let mix_rewarding = rewards_storage::MIXNODE_REWARDING
|
||||||
.load(deps.storage, node)
|
.load(deps.storage, node)
|
||||||
@@ -800,54 +962,54 @@ pub mod test_helpers {
|
|||||||
perform_pending_interval_actions(deps.branch(), &env, None).unwrap();
|
perform_pending_interval_actions(deps.branch(), &env, None).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mixnode_with_signature(
|
// pub fn mixnode_with_signature(
|
||||||
mut rng: impl RngCore + CryptoRng,
|
// mut rng: impl RngCore + CryptoRng,
|
||||||
sender: &str,
|
// deps: Deps<'_>,
|
||||||
) -> (MixNode, String, KeyPair) {
|
// sender: &str,
|
||||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
// stake: Option<Vec<Coin>>,
|
||||||
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
// ) -> (MixNode, MessageSignature, KeyPair) {
|
||||||
let owner_signature = keypair
|
// // hehe stupid workaround for bypassing the immutable borrow and removing duplicate code
|
||||||
.private_key()
|
//
|
||||||
.sign(sender.as_bytes())
|
// let stake = stake.unwrap_or(good_mixnode_pledge());
|
||||||
.to_base58_string();
|
//
|
||||||
|
// let keypair = identity::KeyPair::new(&mut rng);
|
||||||
|
// let identity_key = keypair.public_key().to_base58_string();
|
||||||
|
// let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
||||||
|
//
|
||||||
|
// let mixnode = MixNode {
|
||||||
|
// identity_key,
|
||||||
|
// sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||||
|
// ..tests::fixtures::mix_node_fixture()
|
||||||
|
// };
|
||||||
|
// let msg = mixnode_bonding_sign_payload(deps, sender, None, mixnode.clone(), stake.clone());
|
||||||
|
// let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||||
|
//
|
||||||
|
// (mixnode, owner_signature, keypair)
|
||||||
|
// }
|
||||||
|
|
||||||
let identity_key = keypair.public_key().to_base58_string();
|
// pub fn gateway_with_signature(
|
||||||
let sphinx_key = legit_sphinx_key.public_key().to_base58_string();
|
// mut rng: impl RngCore + CryptoRng,
|
||||||
|
// deps: Deps<'_>,
|
||||||
(
|
// sender: &str,
|
||||||
MixNode {
|
// stake: Option<Vec<Coin>>,
|
||||||
identity_key,
|
// ) -> (Gateway, MessageSignature) {
|
||||||
sphinx_key,
|
// let stake = stake.unwrap_or(good_gateway_pledge());
|
||||||
..tests::fixtures::mix_node_fixture()
|
//
|
||||||
},
|
// let keypair = identity::KeyPair::new(&mut rng);
|
||||||
owner_signature,
|
// let identity_key = keypair.public_key().to_base58_string();
|
||||||
keypair,
|
// let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
||||||
)
|
//
|
||||||
}
|
// let gateway = Gateway {
|
||||||
|
// identity_key,
|
||||||
pub fn gateway_with_signature(
|
// sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||||
mut rng: impl RngCore + CryptoRng,
|
// ..tests::fixtures::gateway_fixture()
|
||||||
sender: &str,
|
// };
|
||||||
) -> (Gateway, String) {
|
//
|
||||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
// let msg = gateway_bonding_sign_payload(deps, sender, None, gateway.clone(), stake.clone());
|
||||||
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
// let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||||
let owner_signature = keypair
|
//
|
||||||
.private_key()
|
// (gateway, owner_signature)
|
||||||
.sign(sender.as_bytes())
|
// }
|
||||||
.to_base58_string();
|
|
||||||
|
|
||||||
let identity_key = keypair.public_key().to_base58_string();
|
|
||||||
let sphinx_key = legit_sphinx_key.public_key().to_base58_string();
|
|
||||||
|
|
||||||
(
|
|
||||||
Gateway {
|
|
||||||
identity_key,
|
|
||||||
sphinx_key,
|
|
||||||
..tests::fixtures::gateway_fixture()
|
|
||||||
},
|
|
||||||
owner_signature,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_dummy_delegations(mut deps: DepsMut<'_>, env: Env, mix_id: MixId, n: usize) {
|
pub fn add_dummy_delegations(mut deps: DepsMut<'_>, env: Env, mix_id: MixId, n: usize) {
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
@@ -864,39 +1026,22 @@ pub mod test_helpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_dummy_mixnodes(
|
// pub fn add_dummy_mixnodes(
|
||||||
mut rng: impl RngCore + CryptoRng,
|
// mut rng: impl RngCore + CryptoRng,
|
||||||
mut deps: DepsMut<'_>,
|
// mut deps: DepsMut<'_>,
|
||||||
env: Env,
|
// env: Env,
|
||||||
n: usize,
|
// n: usize,
|
||||||
) {
|
// ) {
|
||||||
for i in 0..n {
|
// for i in 0..n {
|
||||||
add_mixnode(
|
// add_mixnode(
|
||||||
&mut rng,
|
// &mut rng,
|
||||||
deps.branch(),
|
// deps.branch(),
|
||||||
env.clone(),
|
// env.clone(),
|
||||||
&format!("owner{}", i),
|
// &format!("owner{}", i),
|
||||||
tests::fixtures::good_mixnode_pledge(),
|
// tests::fixtures::good_mixnode_pledge(),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn add_dummy_gateways(
|
|
||||||
mut rng: impl RngCore + CryptoRng,
|
|
||||||
mut deps: DepsMut<'_>,
|
|
||||||
env: Env,
|
|
||||||
n: usize,
|
|
||||||
) {
|
|
||||||
for i in 0..n {
|
|
||||||
add_gateway(
|
|
||||||
&mut rng,
|
|
||||||
deps.branch(),
|
|
||||||
env.clone(),
|
|
||||||
&format!("owner{}", i),
|
|
||||||
tests::fixtures::good_mixnode_pledge(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_dummy_unbonded_mixnodes(
|
pub fn add_dummy_unbonded_mixnodes(
|
||||||
mut rng: impl RngCore + CryptoRng,
|
mut rng: impl RngCore + CryptoRng,
|
||||||
@@ -968,79 +1113,35 @@ pub mod test_helpers {
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
// note to whoever wants to refactor this function, you dont want to grab rng here directly
|
pub fn mixnode_bonding_sign_payload(
|
||||||
// via `let rng = test_rng()`
|
deps: Deps<'_>,
|
||||||
// because it's extremely likely you might end up calling `add_mixnode()` multiple times
|
owner: &str,
|
||||||
// in the same test and thus you're going to get mixnodes with the same keys and that's
|
proxy: Option<Addr>,
|
||||||
// not what you want (presumably)
|
mixnode: MixNode,
|
||||||
pub fn add_mixnode(
|
|
||||||
mut rng: impl RngCore + CryptoRng,
|
|
||||||
deps: DepsMut<'_>,
|
|
||||||
env: Env,
|
|
||||||
sender: &str,
|
|
||||||
stake: Vec<Coin>,
|
stake: Vec<Coin>,
|
||||||
) -> MixId {
|
) -> SignableMixNodeBondingMsg {
|
||||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
let cost_params = tests::fixtures::mix_node_cost_params_fixture();
|
||||||
let owner_signature = keypair
|
let nonce =
|
||||||
.private_key()
|
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
|
||||||
.sign(sender.as_bytes())
|
|
||||||
.to_base58_string();
|
|
||||||
|
|
||||||
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
let payload = MixnodeBondingPayload::new(mixnode, cost_params);
|
||||||
|
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
|
||||||
let info = mock_info(sender, &stake);
|
SignableMixNodeBondingMsg::new(nonce, content)
|
||||||
let key = keypair.public_key().to_base58_string();
|
|
||||||
let current_id_counter = mixnodes_storage::MIXNODE_ID_COUNTER
|
|
||||||
.may_load(deps.storage)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
try_add_mixnode(
|
|
||||||
deps,
|
|
||||||
env,
|
|
||||||
info,
|
|
||||||
MixNode {
|
|
||||||
identity_key: key,
|
|
||||||
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
|
|
||||||
..tests::fixtures::mix_node_fixture()
|
|
||||||
},
|
|
||||||
tests::fixtures::mix_node_cost_params_fixture(),
|
|
||||||
owner_signature,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// newly added mixnode gets assigned the current counter + 1
|
|
||||||
current_id_counter + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// same note as with `add_mixnode`
|
pub fn gateway_bonding_sign_payload(
|
||||||
pub fn add_gateway(
|
deps: Deps<'_>,
|
||||||
mut rng: impl RngCore + CryptoRng,
|
owner: &str,
|
||||||
deps: DepsMut<'_>,
|
proxy: Option<Addr>,
|
||||||
env: Env,
|
gateway: Gateway,
|
||||||
sender: &str,
|
|
||||||
stake: Vec<Coin>,
|
stake: Vec<Coin>,
|
||||||
) -> String {
|
) -> SignableGatewayBondingMsg {
|
||||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
let nonce =
|
||||||
let owner_signature = keypair
|
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
|
||||||
.private_key()
|
|
||||||
.sign(sender.as_bytes())
|
|
||||||
.to_base58_string();
|
|
||||||
|
|
||||||
let info = mock_info(sender, &stake);
|
let payload = GatewayBondingPayload::new(gateway);
|
||||||
let key = keypair.public_key().to_base58_string();
|
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
|
||||||
try_add_gateway(
|
SignableGatewayBondingMsg::new(nonce, content)
|
||||||
deps,
|
|
||||||
env,
|
|
||||||
info,
|
|
||||||
Gateway {
|
|
||||||
identity_key: key.clone(),
|
|
||||||
..tests::fixtures::gateway_fixture()
|
|
||||||
},
|
|
||||||
owner_signature,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initial_rewarding_params() -> InitialRewardingParams {
|
fn initial_rewarding_params() -> InitialRewardingParams {
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
use crate::contract::query;
|
|
||||||
use cosmwasm_std::{
|
|
||||||
from_binary,
|
|
||||||
testing::{mock_env, MockApi, MockQuerier, MockStorage},
|
|
||||||
OwnedDeps,
|
|
||||||
};
|
|
||||||
use mixnet_contract_common::{GatewayBond, PagedGatewayResponse, QueryMsg};
|
|
||||||
|
|
||||||
// I honestly don't know why we're using this way of querying in tests, but I couldn't be bothered to change it
|
|
||||||
// since I haven't done anything to gateways
|
|
||||||
pub fn get_gateways(deps: &mut OwnedDeps<MockStorage, MockApi, MockQuerier>) -> Vec<GatewayBond> {
|
|
||||||
let result = query(
|
|
||||||
deps.as_ref(),
|
|
||||||
mock_env(),
|
|
||||||
QueryMsg::GetGateways {
|
|
||||||
start_after: None,
|
|
||||||
limit: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let page: PagedGatewayResponse = from_binary(&result).unwrap();
|
|
||||||
if page.start_next_after.is_some() {
|
|
||||||
let next_page = query(
|
|
||||||
deps.as_ref(),
|
|
||||||
mock_env(),
|
|
||||||
QueryMsg::GetGateways {
|
|
||||||
start_after: page.start_next_after,
|
|
||||||
limit: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let next_page: PagedGatewayResponse = from_binary(&next_page).unwrap();
|
|
||||||
if !next_page.nodes.is_empty() {
|
|
||||||
panic!("we didn't manage to get all gateways in a single query")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
page.nodes
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ use crate::traits::{
|
|||||||
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, NodeFamilies, VestingAccount,
|
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, NodeFamilies, VestingAccount,
|
||||||
};
|
};
|
||||||
use crate::vesting::{populate_vesting_periods, Account};
|
use crate::vesting::{populate_vesting_periods, Account};
|
||||||
|
use contracts_common::signing::MessageSignature;
|
||||||
use contracts_common::ContractBuildInformation;
|
use contracts_common::ContractBuildInformation;
|
||||||
use cosmwasm_std::{
|
use cosmwasm_std::{
|
||||||
coin, entry_point, to_binary, Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Order,
|
coin, entry_point, to_binary, Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Order,
|
||||||
@@ -411,7 +412,7 @@ fn try_update_staking_address(
|
|||||||
/// Bond a gateway, sends [mixnet_contract_common::ExecuteMsg::BondGatewayOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].
|
/// Bond a gateway, sends [mixnet_contract_common::ExecuteMsg::BondGatewayOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].
|
||||||
pub fn try_bond_gateway(
|
pub fn try_bond_gateway(
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
info: MessageInfo,
|
info: MessageInfo,
|
||||||
env: Env,
|
env: Env,
|
||||||
@@ -448,7 +449,7 @@ pub fn try_track_unbond_gateway(
|
|||||||
pub fn try_bond_mixnode(
|
pub fn try_bond_mixnode(
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
info: MessageInfo,
|
info: MessageInfo,
|
||||||
env: Env,
|
env: Env,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::errors::ContractError;
|
use crate::errors::ContractError;
|
||||||
|
use contracts_common::signing::MessageSignature;
|
||||||
use cosmwasm_std::{Coin, Env, Response, Storage};
|
use cosmwasm_std::{Coin, Env, Response, Storage};
|
||||||
use mixnet_contract_common::{
|
use mixnet_contract_common::{
|
||||||
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
|
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
|
||||||
@@ -12,7 +13,7 @@ pub trait MixnodeBondingAccount {
|
|||||||
&self,
|
&self,
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
env: &Env,
|
env: &Env,
|
||||||
storage: &mut dyn Storage,
|
storage: &mut dyn Storage,
|
||||||
@@ -50,7 +51,7 @@ pub trait GatewayBondingAccount {
|
|||||||
fn try_bond_gateway(
|
fn try_bond_gateway(
|
||||||
&self,
|
&self,
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
env: &Env,
|
env: &Env,
|
||||||
storage: &mut dyn Storage,
|
storage: &mut dyn Storage,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use super::PledgeData;
|
|||||||
use crate::errors::ContractError;
|
use crate::errors::ContractError;
|
||||||
use crate::storage::MIXNET_CONTRACT_ADDRESS;
|
use crate::storage::MIXNET_CONTRACT_ADDRESS;
|
||||||
use crate::traits::GatewayBondingAccount;
|
use crate::traits::GatewayBondingAccount;
|
||||||
|
use contracts_common::signing::MessageSignature;
|
||||||
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
|
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
|
||||||
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway};
|
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway};
|
||||||
use vesting_contract_common::events::{
|
use vesting_contract_common::events::{
|
||||||
@@ -14,7 +15,7 @@ impl GatewayBondingAccount for Account {
|
|||||||
fn try_bond_gateway(
|
fn try_bond_gateway(
|
||||||
&self,
|
&self,
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
env: &Env,
|
env: &Env,
|
||||||
storage: &mut dyn Storage,
|
storage: &mut dyn Storage,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use super::Account;
|
|||||||
use crate::errors::ContractError;
|
use crate::errors::ContractError;
|
||||||
use crate::storage::MIXNET_CONTRACT_ADDRESS;
|
use crate::storage::MIXNET_CONTRACT_ADDRESS;
|
||||||
use crate::traits::MixnodeBondingAccount;
|
use crate::traits::MixnodeBondingAccount;
|
||||||
|
use contracts_common::signing::MessageSignature;
|
||||||
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
|
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
|
||||||
use mixnet_contract_common::mixnode::MixNodeConfigUpdate;
|
use mixnet_contract_common::mixnode::MixNodeConfigUpdate;
|
||||||
use mixnet_contract_common::mixnode::MixNodeCostParams;
|
use mixnet_contract_common::mixnode::MixNodeCostParams;
|
||||||
@@ -32,7 +33,7 @@ impl MixnodeBondingAccount for Account {
|
|||||||
&self,
|
&self,
|
||||||
mix_node: MixNode,
|
mix_node: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
env: &Env,
|
env: &Env,
|
||||||
storage: &mut dyn Storage,
|
storage: &mut dyn Storage,
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ mod tests {
|
|||||||
use crate::traits::VestingAccount;
|
use crate::traits::VestingAccount;
|
||||||
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
|
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
|
||||||
use crate::vesting::{populate_vesting_periods, Account};
|
use crate::vesting::{populate_vesting_periods, Account};
|
||||||
|
use contracts_common::signing::MessageSignature;
|
||||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||||
use cosmwasm_std::{coin, coins, Addr, Coin, Timestamp, Uint128};
|
use cosmwasm_std::{coin, coins, Addr, Coin, Timestamp, Uint128};
|
||||||
use mixnet_contract_common::mixnode::MixNodeCostParams;
|
use mixnet_contract_common::mixnode::MixNodeCostParams;
|
||||||
@@ -674,7 +675,7 @@ mod tests {
|
|||||||
let err = account.try_bond_mixnode(
|
let err = account.try_bond_mixnode(
|
||||||
mix_node.clone(),
|
mix_node.clone(),
|
||||||
cost_params.clone(),
|
cost_params.clone(),
|
||||||
"alice".to_string(),
|
MessageSignature::from(vec![1, 2, 3]),
|
||||||
Coin {
|
Coin {
|
||||||
amount: Uint128::new(1_000_000_000_001),
|
amount: Uint128::new(1_000_000_000_001),
|
||||||
denom: TEST_COIN_DENOM.to_string(),
|
denom: TEST_COIN_DENOM.to_string(),
|
||||||
@@ -687,7 +688,7 @@ mod tests {
|
|||||||
let ok = account.try_bond_mixnode(
|
let ok = account.try_bond_mixnode(
|
||||||
mix_node.clone(),
|
mix_node.clone(),
|
||||||
cost_params.clone(),
|
cost_params.clone(),
|
||||||
"alice".to_string(),
|
MessageSignature::from(vec![1, 2, 3]),
|
||||||
Coin {
|
Coin {
|
||||||
amount: Uint128::new(90_000_000_000),
|
amount: Uint128::new(90_000_000_000),
|
||||||
denom: TEST_COIN_DENOM.to_string(),
|
denom: TEST_COIN_DENOM.to_string(),
|
||||||
@@ -704,7 +705,7 @@ mod tests {
|
|||||||
let err = account.try_bond_mixnode(
|
let err = account.try_bond_mixnode(
|
||||||
mix_node,
|
mix_node,
|
||||||
cost_params,
|
cost_params,
|
||||||
"alice".to_string(),
|
MessageSignature::from(vec![1, 2, 3]),
|
||||||
Coin {
|
Coin {
|
||||||
amount: Uint128::new(10_000_000_001),
|
amount: Uint128::new(10_000_000_001),
|
||||||
denom: TEST_COIN_DENOM.to_string(),
|
denom: TEST_COIN_DENOM.to_string(),
|
||||||
@@ -738,7 +739,7 @@ mod tests {
|
|||||||
// Try delegating too much
|
// Try delegating too much
|
||||||
let err = account.try_bond_gateway(
|
let err = account.try_bond_gateway(
|
||||||
gateway.clone(),
|
gateway.clone(),
|
||||||
"alice".to_string(),
|
MessageSignature::from(vec![1, 2, 3]),
|
||||||
Coin {
|
Coin {
|
||||||
amount: Uint128::new(1_000_000_000_001),
|
amount: Uint128::new(1_000_000_000_001),
|
||||||
denom: TEST_COIN_DENOM.to_string(),
|
denom: TEST_COIN_DENOM.to_string(),
|
||||||
@@ -750,7 +751,7 @@ mod tests {
|
|||||||
|
|
||||||
let ok = account.try_bond_gateway(
|
let ok = account.try_bond_gateway(
|
||||||
gateway.clone(),
|
gateway.clone(),
|
||||||
"alice".to_string(),
|
MessageSignature::from(vec![1, 2, 3]),
|
||||||
Coin {
|
Coin {
|
||||||
amount: Uint128::new(90_000_000_000),
|
amount: Uint128::new(90_000_000_000),
|
||||||
denom: TEST_COIN_DENOM.to_string(),
|
denom: TEST_COIN_DENOM.to_string(),
|
||||||
@@ -766,7 +767,7 @@ mod tests {
|
|||||||
// Try delegating too much again
|
// Try delegating too much again
|
||||||
let err = account.try_bond_gateway(
|
let err = account.try_bond_gateway(
|
||||||
gateway,
|
gateway,
|
||||||
"alice".to_string(),
|
MessageSignature::from(vec![1, 2, 3]),
|
||||||
Coin {
|
Coin {
|
||||||
amount: Uint128::new(500_000_000_001),
|
amount: Uint128::new(500_000_000_001),
|
||||||
denom: TEST_COIN_DENOM.to_string(),
|
denom: TEST_COIN_DENOM.to_string(),
|
||||||
|
|||||||
@@ -8,31 +8,37 @@ use crate::{
|
|||||||
commands::ensure_config_version_compatibility,
|
commands::ensure_config_version_compatibility,
|
||||||
config::persistence::pathfinder::GatewayPathfinder,
|
config::persistence::pathfinder::GatewayPathfinder,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{bail, Result};
|
||||||
use clap::{ArgGroup, Args};
|
use clap::{ArgGroup, Args};
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use validator_client::nyxd;
|
use validator_client::nyxd;
|
||||||
|
|
||||||
#[derive(Args, Clone)]
|
#[derive(Args, Clone)]
|
||||||
#[clap(group(ArgGroup::new("sign").required(true).args(&["wallet_address", "text"])))]
|
#[clap(group(ArgGroup::new("sign").required(true).args(&["wallet_address", "text", "contract_msg"])))]
|
||||||
pub struct Sign {
|
pub struct Sign {
|
||||||
/// The id of the mixnode you want to sign with
|
/// The id of the mixnode you want to sign with
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
id: String,
|
id: String,
|
||||||
|
|
||||||
/// Signs your blockchain address with your identity key
|
/// Signs your blockchain address with your identity key
|
||||||
#[clap(long)]
|
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||||
|
#[clap(long, alias = "address")]
|
||||||
wallet_address: Option<nyxd::AccountId>,
|
wallet_address: Option<nyxd::AccountId>,
|
||||||
|
|
||||||
/// Signs an arbitrary piece of text with your identity key
|
/// Signs an arbitrary piece of text with your identity key
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
text: Option<String>,
|
text: Option<String>,
|
||||||
|
|
||||||
|
/// Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key
|
||||||
|
#[clap(long)]
|
||||||
|
contract_msg: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SignedTarget {
|
enum SignedTarget {
|
||||||
Text(String),
|
Text(String),
|
||||||
Address(nyxd::AccountId),
|
Address(nyxd::AccountId),
|
||||||
|
ContractMsg(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Sign> for SignedTarget {
|
impl TryFrom<Sign> for SignedTarget {
|
||||||
@@ -43,11 +49,13 @@ impl TryFrom<Sign> for SignedTarget {
|
|||||||
Ok(SignedTarget::Text(text))
|
Ok(SignedTarget::Text(text))
|
||||||
} else if let Some(address) = args.wallet_address {
|
} else if let Some(address) = args.wallet_address {
|
||||||
Ok(SignedTarget::Address(address))
|
Ok(SignedTarget::Address(address))
|
||||||
|
} else if let Some(msg) = args.contract_msg {
|
||||||
|
Ok(SignedTarget::ContractMsg(msg))
|
||||||
} else {
|
} else {
|
||||||
// This is unreachable, and hopefully clap will support it explicitly by outputting an
|
// This is unreachable, and hopefully clap will support it explicitly by outputting an
|
||||||
// enum from the ArgGroup in the future.
|
// enum from the ArgGroup in the future.
|
||||||
// See: https://github.com/clap-rs/clap/issues/2621
|
// See: https://github.com/clap-rs/clap/issues/2621
|
||||||
Err(anyhow!("Error: missing signed target flag"))
|
bail!("Error: missing signed target flag")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,6 +96,32 @@ fn print_signed_text(private_key: &identity::PrivateKey, text: &str) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_signed_contract_msg(private_key: &identity::PrivateKey, raw_msg: &str) {
|
||||||
|
let trimmed = raw_msg.trim();
|
||||||
|
println!(">>> attempting to sign {trimmed}");
|
||||||
|
|
||||||
|
let Ok(decoded) = bs58::decode(trimmed).into_vec() else {
|
||||||
|
println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
println!(">>> decoding the message...");
|
||||||
|
|
||||||
|
// we don't really care about what particular information is embedded inside of it,
|
||||||
|
// we just want to know if user correctly copied the string, i.e. whether it's a valid bs58 encoded json
|
||||||
|
if serde_json::from_slice::<serde_json::Value>(&decoded).is_err() {
|
||||||
|
println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// if this is a valid json, it MUST be a valid string
|
||||||
|
let decoded_string = String::from_utf8(decoded.clone()).unwrap();
|
||||||
|
println!(">>> message to sign: {decoded_string}");
|
||||||
|
|
||||||
|
let signature = private_key.sign(&decoded).to_base58_string();
|
||||||
|
println!(">>> The base58-encoded signature is:\n{signature}");
|
||||||
|
}
|
||||||
|
|
||||||
pub fn execute(args: Sign) -> Result<(), Box<dyn Error + Send + Sync>> {
|
pub fn execute(args: Sign) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
let config = build_config(args.id.clone(), OverrideConfig::default())?;
|
let config = build_config(args.id.clone(), OverrideConfig::default())?;
|
||||||
ensure_config_version_compatibility(&config)?;
|
ensure_config_version_compatibility(&config)?;
|
||||||
@@ -99,6 +133,9 @@ pub fn execute(args: Sign) -> Result<(), Box<dyn Error + Send + Sync>> {
|
|||||||
match signed_target {
|
match signed_target {
|
||||||
SignedTarget::Text(text) => print_signed_text(identity_keypair.private_key(), &text),
|
SignedTarget::Text(text) => print_signed_text(identity_keypair.private_key(), &text),
|
||||||
SignedTarget::Address(addr) => print_signed_address(identity_keypair.private_key(), addr)?,
|
SignedTarget::Address(addr) => print_signed_address(identity_keypair.private_key(), addr)?,
|
||||||
|
SignedTarget::ContractMsg(raw_msg) => {
|
||||||
|
print_signed_contract_msg(identity_keypair.private_key(), &raw_msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -291,6 +291,7 @@ impl Config {
|
|||||||
&self.gateway.version
|
&self.gateway.version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub fn get_wallet_address(&self) -> Option<nyxd::AccountId> {
|
pub fn get_wallet_address(&self) -> Option<nyxd::AccountId> {
|
||||||
self.gateway.wallet_address.clone()
|
self.gateway.wallet_address.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-22
@@ -2,7 +2,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use self::storage::PersistentStorage;
|
use self::storage::PersistentStorage;
|
||||||
use crate::commands::ensure_correct_bech32_prefix;
|
|
||||||
use crate::config::persistence::pathfinder::GatewayPathfinder;
|
use crate::config::persistence::pathfinder::GatewayPathfinder;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::error::GatewayError;
|
use crate::error::GatewayError;
|
||||||
@@ -12,8 +11,7 @@ use crate::node::client_handling::websocket::connection_handler::coconut::Coconu
|
|||||||
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
|
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
|
||||||
use crate::node::statistics::collector::GatewayStatisticsCollector;
|
use crate::node::statistics::collector::GatewayStatisticsCollector;
|
||||||
use crate::node::storage::Storage;
|
use crate::node::storage::Storage;
|
||||||
use crate::{commands::sign::load_identity_keys, OutputFormat};
|
use crate::OutputFormat;
|
||||||
use colored::Colorize;
|
|
||||||
use log::*;
|
use log::*;
|
||||||
use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder};
|
use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder};
|
||||||
use nym_crypto::asymmetric::{encryption, identity};
|
use nym_crypto::asymmetric::{encryption, identity};
|
||||||
@@ -24,7 +22,6 @@ use rand::seq::SliceRandom;
|
|||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::process;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use validator_client::Client;
|
use validator_client::Client;
|
||||||
|
|
||||||
@@ -109,28 +106,10 @@ where
|
|||||||
sphinx_keypair
|
sphinx_keypair
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Signs the node config's bech32 address to produce a verification code for use in the wallet.
|
|
||||||
/// Exits if the address isn't valid (which should protect against manual edits).
|
|
||||||
fn generate_owner_signature(&self) -> Result<String, GatewayError> {
|
|
||||||
let pathfinder = GatewayPathfinder::new_from_config(&self.config);
|
|
||||||
let identity_keypair = load_identity_keys(&pathfinder);
|
|
||||||
let Some(address) = self.config.get_wallet_address() else {
|
|
||||||
let error_message = "Error: gateway hasn't set its wallet address".red();
|
|
||||||
eprintln!("{error_message}");
|
|
||||||
eprintln!("Exiting...");
|
|
||||||
process::exit(1);
|
|
||||||
};
|
|
||||||
// perform extra validation to ensure we have correct prefix
|
|
||||||
ensure_correct_bech32_prefix(&address)?;
|
|
||||||
let verification_code = identity_keypair.private_key().sign_text(address.as_ref());
|
|
||||||
Ok(verification_code)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn print_node_details(&self, output: OutputFormat) -> Result<(), GatewayError> {
|
pub(crate) fn print_node_details(&self, output: OutputFormat) -> Result<(), GatewayError> {
|
||||||
let node_details = nym_types::gateway::GatewayNodeDetailsResponse {
|
let node_details = nym_types::gateway::GatewayNodeDetailsResponse {
|
||||||
identity_key: self.identity_keypair.public_key().to_base58_string(),
|
identity_key: self.identity_keypair.public_key().to_base58_string(),
|
||||||
sphinx_key: self.sphinx_keypair.public_key().to_base58_string(),
|
sphinx_key: self.sphinx_keypair.public_key().to_base58_string(),
|
||||||
owner_signature: self.generate_owner_signature()?,
|
|
||||||
announce_address: self.config.get_announce_address(),
|
announce_address: self.config.get_announce_address(),
|
||||||
bind_address: self.config.get_listening_address().to_string(),
|
bind_address: self.config.get_listening_address().to_string(),
|
||||||
version: self.config.get_version().to_string(),
|
version: self.config.get_version().to_string(),
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ atty = "0.2"
|
|||||||
## internal
|
## internal
|
||||||
nym-config = { path="../common/config" }
|
nym-config = { path="../common/config" }
|
||||||
nym-crypto = { path="../common/crypto" }
|
nym-crypto = { path="../common/crypto" }
|
||||||
|
nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" }
|
||||||
mixnet-client = { path="../common/client-libs/mixnet-client" }
|
mixnet-client = { path="../common/client-libs/mixnet-client" }
|
||||||
mixnode-common = { path="../common/mixnode-common" }
|
mixnode-common = { path="../common/mixnode-common" }
|
||||||
nym-nonexhaustive-delayqueue = { path="../common/nonexhaustive-delayqueue" }
|
nym-nonexhaustive-delayqueue = { path="../common/nonexhaustive-delayqueue" }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::convert::TryFrom;
|
|||||||
use crate::commands::validate_bech32_address_or_exit;
|
use crate::commands::validate_bech32_address_or_exit;
|
||||||
use crate::config::{persistence::pathfinder::MixNodePathfinder, Config};
|
use crate::config::{persistence::pathfinder::MixNodePathfinder, Config};
|
||||||
use crate::node::MixNode;
|
use crate::node::MixNode;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{bail, Result};
|
||||||
use clap::{ArgGroup, Args};
|
use clap::{ArgGroup, Args};
|
||||||
use log::error;
|
use log::error;
|
||||||
use nym_config::NymConfig;
|
use nym_config::NymConfig;
|
||||||
@@ -16,7 +16,7 @@ use validator_client::nyxd;
|
|||||||
use super::version_check;
|
use super::version_check;
|
||||||
|
|
||||||
#[derive(Args, Clone)]
|
#[derive(Args, Clone)]
|
||||||
#[clap(group(ArgGroup::new("sign").required(true).args(&["wallet_address", "text"])))]
|
#[clap(group(ArgGroup::new("sign").required(true).args(&["wallet_address", "text", "contract_msg"])))]
|
||||||
pub(crate) struct Sign {
|
pub(crate) struct Sign {
|
||||||
/// The id of the mixnode you want to sign with
|
/// The id of the mixnode you want to sign with
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
@@ -30,11 +30,16 @@ pub(crate) struct Sign {
|
|||||||
/// Signs an arbitrary piece of text with your identity key
|
/// Signs an arbitrary piece of text with your identity key
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
text: Option<String>,
|
text: Option<String>,
|
||||||
|
|
||||||
|
/// Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key
|
||||||
|
#[clap(long)]
|
||||||
|
contract_msg: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SignedTarget {
|
enum SignedTarget {
|
||||||
Text(String),
|
Text(String),
|
||||||
Address(nyxd::AccountId),
|
Address(nyxd::AccountId),
|
||||||
|
ContractMsg(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Sign> for SignedTarget {
|
impl TryFrom<Sign> for SignedTarget {
|
||||||
@@ -45,11 +50,13 @@ impl TryFrom<Sign> for SignedTarget {
|
|||||||
Ok(SignedTarget::Text(text))
|
Ok(SignedTarget::Text(text))
|
||||||
} else if let Some(address) = args.wallet_address {
|
} else if let Some(address) = args.wallet_address {
|
||||||
Ok(SignedTarget::Address(address))
|
Ok(SignedTarget::Address(address))
|
||||||
|
} else if let Some(msg) = args.contract_msg {
|
||||||
|
Ok(SignedTarget::ContractMsg(msg))
|
||||||
} else {
|
} else {
|
||||||
// This is unreachable, and hopefully clap will support it explicitly by outputting an
|
// This is unreachable, and hopefully clap will support it explicitly by outputting an
|
||||||
// enum from the ArgGroup in the future.
|
// enum from the ArgGroup in the future.
|
||||||
// See: https://github.com/clap-rs/clap/issues/2621
|
// See: https://github.com/clap-rs/clap/issues/2621
|
||||||
Err(anyhow!("Error: missing signed target flag"))
|
bail!("Error: missing signed target flag")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,14 +77,39 @@ fn print_signed_text(private_key: &identity::PrivateKey, text: &str) {
|
|||||||
println!("The base58-encoded signature on '{text}' is: {signature}");
|
println!("The base58-encoded signature on '{text}' is: {signature}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_signed_contract_msg(private_key: &identity::PrivateKey, raw_msg: &str) {
|
||||||
|
let trimmed = raw_msg.trim();
|
||||||
|
println!(">>> attempting to sign {trimmed}");
|
||||||
|
|
||||||
|
let Ok(decoded) = bs58::decode(trimmed).into_vec() else {
|
||||||
|
println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
println!(">>> decoding the message...");
|
||||||
|
|
||||||
|
// we don't really care about what particular information is embedded inside of it,
|
||||||
|
// we just want to know if user correctly copied the string, i.e. whether it's a valid bs58 encoded json
|
||||||
|
if serde_json::from_slice::<serde_json::Value>(&decoded).is_err() {
|
||||||
|
println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// if this is a valid json, it MUST be a valid string
|
||||||
|
let decoded_string = String::from_utf8(decoded.clone()).unwrap();
|
||||||
|
println!(">>> message to sign: {decoded_string}");
|
||||||
|
|
||||||
|
let signature = private_key.sign(&decoded).to_base58_string();
|
||||||
|
println!(">>> The base58-encoded signature is:\n{signature}");
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn execute(args: &Sign) {
|
pub(crate) fn execute(args: &Sign) {
|
||||||
let config = match Config::load_from_file(&args.id) {
|
let config = match Config::load_from_file(&args.id) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(
|
error!(
|
||||||
"Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})",
|
"Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})",
|
||||||
args.id,
|
args.id,
|
||||||
err,
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -101,5 +133,8 @@ pub(crate) fn execute(args: &Sign) {
|
|||||||
match signed_target {
|
match signed_target {
|
||||||
SignedTarget::Text(text) => print_signed_text(identity_keypair.private_key(), &text),
|
SignedTarget::Text(text) => print_signed_text(identity_keypair.private_key(), &text),
|
||||||
SignedTarget::Address(addr) => print_signed_address(identity_keypair.private_key(), addr),
|
SignedTarget::Address(addr) => print_signed_address(identity_keypair.private_key(), addr),
|
||||||
|
SignedTarget::ContractMsg(raw_msg) => {
|
||||||
|
print_signed_contract_msg(identity_keypair.private_key(), &raw_msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::commands::validate_bech32_address_or_exit;
|
|
||||||
use crate::config::persistence::pathfinder::MixNodePathfinder;
|
use crate::config::persistence::pathfinder::MixNodePathfinder;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::node::http::{
|
use crate::node::http::{
|
||||||
@@ -18,7 +17,6 @@ use crate::node::node_description::NodeDescription;
|
|||||||
use crate::node::node_statistics::SharedNodeStats;
|
use crate::node::node_statistics::SharedNodeStats;
|
||||||
use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSender};
|
use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSender};
|
||||||
use crate::OutputFormat;
|
use crate::OutputFormat;
|
||||||
use colored::Colorize;
|
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use mixnode_common::verloc::{self, AtomicVerlocResult, VerlocMeasurer};
|
use mixnode_common::verloc::{self, AtomicVerlocResult, VerlocMeasurer};
|
||||||
use nym_bin_common::version_checker::parse_version;
|
use nym_bin_common::version_checker::parse_version;
|
||||||
@@ -84,29 +82,11 @@ impl MixNode {
|
|||||||
sphinx_keypair
|
sphinx_keypair
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Signs the node config's bech32 address to produce a verification code for use in the wallet.
|
|
||||||
/// Exits if the address isn't valid (which should protect against manual edits).
|
|
||||||
fn generate_owner_signature(&self) -> String {
|
|
||||||
let pathfinder = MixNodePathfinder::new_from_config(&self.config);
|
|
||||||
let identity_keypair = Self::load_identity_keys(&pathfinder);
|
|
||||||
let Some(address) = self.config.get_wallet_address() else {
|
|
||||||
let error_message = "Error: mixnode hasn't set its wallet address".red();
|
|
||||||
println!("{error_message}");
|
|
||||||
println!("Exiting...");
|
|
||||||
process::exit(1);
|
|
||||||
};
|
|
||||||
// perform extra validation to ensure we have correct prefix
|
|
||||||
validate_bech32_address_or_exit(address.as_ref());
|
|
||||||
let verification_code = identity_keypair.private_key().sign_text(address.as_ref());
|
|
||||||
verification_code
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints relevant node details to the console
|
/// Prints relevant node details to the console
|
||||||
pub(crate) fn print_node_details(&self, output: OutputFormat) {
|
pub(crate) fn print_node_details(&self, output: OutputFormat) {
|
||||||
let node_details = nym_types::mixnode::MixnodeNodeDetailsResponse {
|
let node_details = nym_types::mixnode::MixnodeNodeDetailsResponse {
|
||||||
identity_key: self.identity_keypair.public_key().to_base58_string(),
|
identity_key: self.identity_keypair.public_key().to_base58_string(),
|
||||||
sphinx_key: self.sphinx_keypair.public_key().to_base58_string(),
|
sphinx_key: self.sphinx_keypair.public_key().to_base58_string(),
|
||||||
owner_signature: self.generate_owner_signature(),
|
|
||||||
announce_address: self.config.get_announce_address(),
|
announce_address: self.config.get_announce_address(),
|
||||||
bind_address: self.config.get_listening_address().to_string(),
|
bind_address: self.config.get_listening_address().to_string(),
|
||||||
version: self.config.get_version().to_string(),
|
version: self.config.get_version().to_string(),
|
||||||
|
|||||||
Generated
+220
-36
@@ -20,7 +20,7 @@ version = "0.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
|
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -32,7 +32,8 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cipher",
|
"cipher",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"opaque-debug",
|
"ctr",
|
||||||
|
"opaque-debug 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -46,7 +47,7 @@ dependencies = [
|
|||||||
"cipher",
|
"cipher",
|
||||||
"ctr",
|
"ctr",
|
||||||
"ghash",
|
"ghash",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -97,7 +98,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "25df3c03f1040d0069fcd3907e24e36d59f9b6fa07ba49be0eb25a794f036ba7"
|
checksum = "25df3c03f1040d0069fcd3907e24e36d59f9b6fa07ba49be0eb25a794f036ba7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64ct",
|
"base64ct",
|
||||||
"blake2",
|
"blake2 0.10.6",
|
||||||
"password-hash 0.3.2",
|
"password-hash 0.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -108,10 +109,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a27e27b63e4a34caee411ade944981136fdfa535522dc9944d6700196cbd899f"
|
checksum = "a27e27b63e4a34caee411ade944981136fdfa535522dc9944d6700196cbd899f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64ct",
|
"base64ct",
|
||||||
"blake2",
|
"blake2 0.10.6",
|
||||||
"password-hash 0.4.2",
|
"password-hash 0.4.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayref"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.64"
|
version = "0.1.64"
|
||||||
@@ -227,7 +234,7 @@ dependencies = [
|
|||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"ripemd160",
|
"ripemd160",
|
||||||
"sha2 0.9.9",
|
"sha2 0.9.9",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -268,6 +275,18 @@ dependencies = [
|
|||||||
"wyz",
|
"wyz",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330"
|
||||||
|
dependencies = [
|
||||||
|
"byte-tools",
|
||||||
|
"crypto-mac 0.7.0",
|
||||||
|
"digest 0.8.1",
|
||||||
|
"opaque-debug 0.2.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake2"
|
name = "blake2"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -290,7 +309,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-padding",
|
"block-padding",
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -299,7 +318,7 @@ version = "0.10.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
|
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -318,7 +337,7 @@ dependencies = [
|
|||||||
"group",
|
"group",
|
||||||
"pairing",
|
"pairing",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -368,6 +387,12 @@ version = "3.12.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byte-tools"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
@@ -469,13 +494,23 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chacha"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"keystream",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cipher"
|
name = "cipher"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -785,9 +820,9 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21"
|
checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -797,18 +832,28 @@ version = "0.1.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-mac"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.12.4",
|
||||||
|
"subtle 1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-mac"
|
name = "crypto-mac"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
|
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -881,7 +926,7 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
"digest 0.9.0",
|
"digest 0.9.0",
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1017,13 +1062,22 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.12.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1034,7 +1088,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer 0.10.3",
|
"block-buffer 0.10.3",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1146,6 +1200,8 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"ed25519",
|
"ed25519",
|
||||||
|
"rand 0.7.3",
|
||||||
|
"serde",
|
||||||
"sha2 0.9.9",
|
"sha2 0.9.9",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -1181,11 +1237,11 @@ dependencies = [
|
|||||||
"crypto-bigint",
|
"crypto-bigint",
|
||||||
"der",
|
"der",
|
||||||
"ff",
|
"ff",
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
"group",
|
"group",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"sec1",
|
"sec1",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1294,7 +1350,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924"
|
checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1580,6 +1636,15 @@ dependencies = [
|
|||||||
"windows 0.39.0",
|
"windows 0.39.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.6"
|
version = "0.14.6"
|
||||||
@@ -1597,8 +1662,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1632,7 +1699,7 @@ version = "0.4.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
|
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"opaque-debug",
|
"opaque-debug 0.3.0",
|
||||||
"polyval",
|
"polyval",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1763,7 +1830,7 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
"ff",
|
"ff",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1939,13 +2006,23 @@ version = "0.3.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0"
|
checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hkdf"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b"
|
||||||
|
dependencies = [
|
||||||
|
"digest 0.9.0",
|
||||||
|
"hmac",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
|
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-mac",
|
"crypto-mac 0.11.1",
|
||||||
"digest 0.9.0",
|
"digest 0.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2338,6 +2415,12 @@ dependencies = [
|
|||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keystream"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kuchiki"
|
name = "kuchiki"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
@@ -2374,6 +2457,12 @@ dependencies = [
|
|||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libz-sys"
|
name = "libz-sys"
|
||||||
version = "1.1.8"
|
version = "1.1.8"
|
||||||
@@ -2401,6 +2490,18 @@ version = "0.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lioness"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"blake2 0.8.1",
|
||||||
|
"chacha",
|
||||||
|
"keystream",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@@ -2650,6 +2751,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.1.0",
|
"autocfg 1.1.0",
|
||||||
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2797,6 +2899,20 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nym-crypto"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bs58",
|
||||||
|
"ed25519-dalek",
|
||||||
|
"nym-pemstore",
|
||||||
|
"nym-sphinx-types",
|
||||||
|
"rand 0.7.3",
|
||||||
|
"subtle-encoding",
|
||||||
|
"thiserror",
|
||||||
|
"x25519-dalek",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-execute"
|
name = "nym-execute"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -2866,6 +2982,13 @@ dependencies = [
|
|||||||
"pem",
|
"pem",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nym-sphinx-types"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"sphinx-packet",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-types"
|
name = "nym-types"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -2961,6 +3084,7 @@ version = "1.1.11"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"argon2 0.3.4",
|
"argon2 0.3.4",
|
||||||
|
"async-trait",
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
"bip39",
|
"bip39",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -2977,6 +3101,8 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"nym-coconut-interface",
|
"nym-coconut-interface",
|
||||||
"nym-config",
|
"nym-config",
|
||||||
|
"nym-contracts-common",
|
||||||
|
"nym-crypto",
|
||||||
"nym-mixnet-contract-common",
|
"nym-mixnet-contract-common",
|
||||||
"nym-types",
|
"nym-types",
|
||||||
"nym-vesting-contract",
|
"nym-vesting-contract",
|
||||||
@@ -2985,6 +3111,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"rand 0.6.5",
|
"rand 0.6.5",
|
||||||
|
"rand_chacha 0.2.2",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -3071,6 +3198,12 @@ version = "1.17.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -3209,7 +3342,7 @@ checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64ct",
|
"base64ct",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3220,7 +3353,7 @@ checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64ct",
|
"base64ct",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3241,7 +3374,7 @@ version = "0.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739"
|
checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-mac",
|
"crypto-mac 0.11.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3513,7 +3646,7 @@ checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"opaque-debug",
|
"opaque-debug 0.3.0",
|
||||||
"universal-hash",
|
"universal-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3764,6 +3897,16 @@ dependencies = [
|
|||||||
"getrandom 0.2.8",
|
"getrandom 0.2.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_distr"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9e9532ada3929fb8b2e9dbe28d1e06c9b2cc65813f074fcb6bd5fbefeff9d56"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"rand 0.7.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_hc"
|
name = "rand_hc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -4012,7 +4155,7 @@ checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer 0.9.0",
|
"block-buffer 0.9.0",
|
||||||
"digest 0.9.0",
|
"digest 0.9.0",
|
||||||
"opaque-debug",
|
"opaque-debug 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4162,9 +4305,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1"
|
checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"der",
|
"der",
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4396,7 +4539,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"digest 0.9.0",
|
"digest 0.9.0",
|
||||||
"opaque-debug",
|
"opaque-debug 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4419,7 +4562,7 @@ dependencies = [
|
|||||||
"block-buffer 0.9.0",
|
"block-buffer 0.9.0",
|
||||||
"digest 0.9.0",
|
"digest 0.9.0",
|
||||||
"keccak",
|
"keccak",
|
||||||
"opaque-debug",
|
"opaque-debug 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4518,6 +4661,30 @@ dependencies = [
|
|||||||
"system-deps 5.0.0",
|
"system-deps 5.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sphinx-packet"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc43eda802856ee82a7555c7b75ceb9e07451741c7a2f5f23d036020e01189d4"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"arrayref",
|
||||||
|
"blake2 0.8.1",
|
||||||
|
"bs58",
|
||||||
|
"byteorder",
|
||||||
|
"chacha",
|
||||||
|
"curve25519-dalek",
|
||||||
|
"digest 0.9.0",
|
||||||
|
"hkdf",
|
||||||
|
"hmac",
|
||||||
|
"lioness",
|
||||||
|
"log",
|
||||||
|
"rand 0.7.3",
|
||||||
|
"rand_distr",
|
||||||
|
"sha2 0.9.9",
|
||||||
|
"subtle 2.4.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -4609,6 +4776,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
@@ -4951,7 +5124,7 @@ dependencies = [
|
|||||||
"serde_repr",
|
"serde_repr",
|
||||||
"sha2 0.9.9",
|
"sha2 0.9.9",
|
||||||
"signature",
|
"signature",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
"subtle-encoding",
|
"subtle-encoding",
|
||||||
"tendermint-proto",
|
"tendermint-proto",
|
||||||
"time",
|
"time",
|
||||||
@@ -5369,8 +5542,8 @@ version = "0.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
|
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.6",
|
||||||
"subtle",
|
"subtle 2.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6029,6 +6202,17 @@ dependencies = [
|
|||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x25519-dalek"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek",
|
||||||
|
"rand_core 0.5.1",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xattr"
|
name = "xattr"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ tauri-codegen = "=1.2.1"
|
|||||||
tauri-macros = "=1.2.1"
|
tauri-macros = "=1.2.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-trait = "0.1.64"
|
||||||
bip39 = "1.0"
|
bip39 = "1.0"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
colored = "2.0"
|
colored = "2.0"
|
||||||
@@ -57,6 +58,8 @@ cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegr
|
|||||||
validator-client = { path = "../../common/client-libs/validator-client", features = [
|
validator-client = { path = "../../common/client-libs/validator-client", features = [
|
||||||
"nyxd-client",
|
"nyxd-client",
|
||||||
] }
|
] }
|
||||||
|
nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] }
|
||||||
|
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" }
|
||||||
nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
|
nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||||
nym-vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" }
|
nym-vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" }
|
||||||
nym-config = { path = "../../common/config" }
|
nym-config = { path = "../../common/config" }
|
||||||
@@ -67,6 +70,8 @@ nym-types = { path = "../../common/types" }
|
|||||||
nym-wallet-types = { path = "../nym-wallet-types" }
|
nym-wallet-types = { path = "../nym-wallet-types" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
nym-crypto = { path = "../../common/crypto", features = ["rand"] }
|
||||||
|
rand_chacha = "0.2"
|
||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
ts-rs = "6.1.2"
|
ts-rs = "6.1.2"
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use nym_contracts_common::signing::SigningAlgorithm;
|
||||||
|
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||||
use nym_types::error::TypesError;
|
use nym_types::error::TypesError;
|
||||||
use nym_wallet_types::network::Network;
|
use nym_wallet_types::network::Network;
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
@@ -122,6 +124,14 @@ pub enum BackendError {
|
|||||||
#[error("Unable to open a new window")]
|
#[error("Unable to open a new window")]
|
||||||
NewWindowError,
|
NewWindowError,
|
||||||
|
|
||||||
|
#[error("received unexpected signing algorithm: {received:?}. Expected to get {expected:?}")]
|
||||||
|
UnexpectedSigningAlgorithm {
|
||||||
|
received: SigningAlgorithm,
|
||||||
|
expected: SigningAlgorithm,
|
||||||
|
},
|
||||||
|
#[error(transparent)]
|
||||||
|
Ed25519Recovery(#[from] Ed25519RecoveryError),
|
||||||
|
|
||||||
#[error("This command ({name}) has been removed. Please try to use {alternative} instead.")]
|
#[error("This command ({name}) has been removed. Please try to use {alternative} instead.")]
|
||||||
RemovedCommand { name: String, alternative: String },
|
RemovedCommand { name: String, alternative: String },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,6 +169,10 @@ fn main() {
|
|||||||
simulate::mixnet::simulate_claim_operator_reward,
|
simulate::mixnet::simulate_claim_operator_reward,
|
||||||
signatures::sign::sign,
|
signatures::sign::sign,
|
||||||
signatures::sign::verify,
|
signatures::sign::verify,
|
||||||
|
signatures::ed25519_signing_payload::generate_mixnode_bonding_msg_payload,
|
||||||
|
signatures::ed25519_signing_payload::vesting_generate_mixnode_bonding_msg_payload,
|
||||||
|
signatures::ed25519_signing_payload::generate_gateway_bonding_msg_payload,
|
||||||
|
signatures::ed25519_signing_payload::vesting_generate_gateway_bonding_msg_payload,
|
||||||
help::log::help_log_toggle_window,
|
help::log::help_log_toggle_window,
|
||||||
])
|
])
|
||||||
.menu(Menu::os_default(&context.package_info().name).add_default_app_submenus())
|
.menu(Menu::os_default(&context.package_info().name).add_default_app_submenus())
|
||||||
|
|||||||
@@ -0,0 +1,391 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::error::BackendError;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use cosmwasm_std::Addr;
|
||||||
|
use nym_contracts_common::signing::{
|
||||||
|
ContractMessageContent, MessageSignature, Nonce, SignableMessage, SigningAlgorithm,
|
||||||
|
};
|
||||||
|
use nym_crypto::asymmetric::identity;
|
||||||
|
use nym_mixnet_contract_common::{
|
||||||
|
construct_mixnode_bonding_sign_payload, Gateway, GatewayBondingPayload, MixNode,
|
||||||
|
MixNodeCostParams, SignableGatewayBondingMsg, SignableMixNodeBondingMsg,
|
||||||
|
};
|
||||||
|
use validator_client::nyxd::error::NyxdError;
|
||||||
|
use validator_client::nyxd::traits::MixnetQueryClient;
|
||||||
|
use validator_client::nyxd::{Coin, SigningNyxdClient};
|
||||||
|
use validator_client::Client;
|
||||||
|
|
||||||
|
// define this as a separate trait for mocking purposes
|
||||||
|
#[async_trait]
|
||||||
|
pub(crate) trait AddressAndNonceProvider {
|
||||||
|
async fn get_signing_nonce(&self) -> Result<Nonce, NyxdError>;
|
||||||
|
fn vesting_contract_address(&self) -> Addr;
|
||||||
|
fn cw_address(&self) -> Addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AddressAndNonceProvider for Client<SigningNyxdClient> {
|
||||||
|
async fn get_signing_nonce(&self) -> Result<Nonce, NyxdError> {
|
||||||
|
self.nyxd.get_signing_nonce(self.nyxd.address()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vesting_contract_address(&self) -> Addr {
|
||||||
|
// the call to unchecked is fine here as we're converting directly from `AccountId`
|
||||||
|
// which must have been a valid bech32 address
|
||||||
|
Addr::unchecked(self.nyxd.vesting_contract_address().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cw_address(&self) -> Addr {
|
||||||
|
self.nyxd.cw_address()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn proxy<P: AddressAndNonceProvider>(client: &P, vesting: bool) -> Option<Addr> {
|
||||||
|
if vesting {
|
||||||
|
Some(client.vesting_contract_address())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// since the message has to go back to the user due to the increasing nonce, we might as well sign the entire payload
|
||||||
|
pub(crate) async fn create_mixnode_bonding_sign_payload<P: AddressAndNonceProvider>(
|
||||||
|
client: &P,
|
||||||
|
mix_node: MixNode,
|
||||||
|
cost_params: MixNodeCostParams,
|
||||||
|
pledge: Coin,
|
||||||
|
vesting: bool,
|
||||||
|
) -> Result<SignableMixNodeBondingMsg, BackendError> {
|
||||||
|
let sender = client.cw_address();
|
||||||
|
let proxy = proxy(client, vesting);
|
||||||
|
let nonce = client.get_signing_nonce().await?;
|
||||||
|
|
||||||
|
Ok(construct_mixnode_bonding_sign_payload(
|
||||||
|
nonce,
|
||||||
|
sender,
|
||||||
|
proxy,
|
||||||
|
pledge.into(),
|
||||||
|
mix_node,
|
||||||
|
cost_params,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn verify_mixnode_bonding_sign_payload<P: AddressAndNonceProvider>(
|
||||||
|
client: &P,
|
||||||
|
mix_node: &MixNode,
|
||||||
|
cost_params: &MixNodeCostParams,
|
||||||
|
pledge: &Coin,
|
||||||
|
vesting: bool,
|
||||||
|
msg_signature: &MessageSignature,
|
||||||
|
) -> Result<(), BackendError> {
|
||||||
|
let identity_key = identity::PublicKey::from_base58_string(&mix_node.identity_key)?;
|
||||||
|
let signature = identity::Signature::from_bytes(msg_signature.as_ref())?;
|
||||||
|
|
||||||
|
// recreate the plaintext
|
||||||
|
let msg = create_mixnode_bonding_sign_payload(
|
||||||
|
client,
|
||||||
|
mix_node.clone(),
|
||||||
|
cost_params.clone(),
|
||||||
|
pledge.clone(),
|
||||||
|
vesting,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let plaintext = msg.to_plaintext()?;
|
||||||
|
|
||||||
|
if !msg.algorithm.is_ed25519() {
|
||||||
|
return Err(BackendError::UnexpectedSigningAlgorithm {
|
||||||
|
received: msg.algorithm,
|
||||||
|
expected: SigningAlgorithm::Ed25519,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: possibly provide better error message if this check fails
|
||||||
|
identity_key.verify(&plaintext, &signature)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// since the message has to go back to the user due to the increasing nonce, we might as well sign the entire payload
|
||||||
|
pub(crate) async fn create_gateway_bonding_sign_payload<P: AddressAndNonceProvider>(
|
||||||
|
client: &P,
|
||||||
|
gateway: Gateway,
|
||||||
|
pledge: Coin,
|
||||||
|
vesting: bool,
|
||||||
|
) -> Result<SignableGatewayBondingMsg, BackendError> {
|
||||||
|
let payload = GatewayBondingPayload::new(gateway);
|
||||||
|
let sender = client.cw_address();
|
||||||
|
let proxy = proxy(client, vesting);
|
||||||
|
let content = ContractMessageContent::new(sender, proxy, vec![pledge.into()], payload);
|
||||||
|
let nonce = client.get_signing_nonce().await?;
|
||||||
|
|
||||||
|
Ok(SignableMessage::new(nonce, content))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn verify_gateway_bonding_sign_payload<P: AddressAndNonceProvider>(
|
||||||
|
client: &P,
|
||||||
|
gateway: &Gateway,
|
||||||
|
pledge: &Coin,
|
||||||
|
vesting: bool,
|
||||||
|
msg_signature: &MessageSignature,
|
||||||
|
) -> Result<(), BackendError> {
|
||||||
|
let identity_key = identity::PublicKey::from_base58_string(&gateway.identity_key)?;
|
||||||
|
let signature = identity::Signature::from_bytes(msg_signature.as_ref())?;
|
||||||
|
|
||||||
|
// recreate the plaintext
|
||||||
|
let msg = create_gateway_bonding_sign_payload(client, gateway.clone(), pledge.clone(), vesting)
|
||||||
|
.await?;
|
||||||
|
let plaintext = msg.to_plaintext()?;
|
||||||
|
|
||||||
|
if !msg.algorithm.is_ed25519() {
|
||||||
|
return Err(BackendError::UnexpectedSigningAlgorithm {
|
||||||
|
received: msg.algorithm,
|
||||||
|
expected: SigningAlgorithm::Ed25519,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: possibly provide better error message if this check fails
|
||||||
|
identity_key.verify(&plaintext, &signature)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use cosmwasm_std::coin;
|
||||||
|
use nym_contracts_common::Percent;
|
||||||
|
use rand_chacha::rand_core::SeedableRng;
|
||||||
|
use rand_chacha::ChaCha20Rng;
|
||||||
|
|
||||||
|
// use rng with constant seed for all tests so that they would be deterministic
|
||||||
|
pub fn test_rng() -> ChaCha20Rng {
|
||||||
|
let dummy_seed = [42u8; 32];
|
||||||
|
rand_chacha::ChaCha20Rng::from_seed(dummy_seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MockClient {
|
||||||
|
address: Addr,
|
||||||
|
vesting_contract: Addr,
|
||||||
|
signing_nonce: Nonce,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AddressAndNonceProvider for MockClient {
|
||||||
|
async fn get_signing_nonce(&self) -> Result<Nonce, NyxdError> {
|
||||||
|
Ok(self.signing_nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vesting_contract_address(&self) -> Addr {
|
||||||
|
self.vesting_contract.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cw_address(&self) -> Addr {
|
||||||
|
self.address.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn dummy_mix_bonding_signature_verification() {
|
||||||
|
let mut rng = test_rng();
|
||||||
|
let identity_keypair = identity::KeyPair::new(&mut rng);
|
||||||
|
let dummy_mixnode = MixNode {
|
||||||
|
host: "1.2.3.4".to_string(),
|
||||||
|
mix_port: 1234,
|
||||||
|
verloc_port: 2345,
|
||||||
|
http_api_port: 3456,
|
||||||
|
sphinx_key: "totally-legit-sphinx-key".to_string(),
|
||||||
|
identity_key: identity_keypair.public_key().to_base58_string(),
|
||||||
|
version: "v1.2.3".to_string(),
|
||||||
|
};
|
||||||
|
let dummy_cost_params = MixNodeCostParams {
|
||||||
|
profit_margin_percent: Percent::from_percentage_value(42).unwrap(),
|
||||||
|
interval_operating_cost: coin(1111111, "unym"),
|
||||||
|
};
|
||||||
|
let dummy_pledge: Coin = coin(10000000000, "unym").into();
|
||||||
|
|
||||||
|
let dummy_account = Addr::unchecked("n16t2umcd83zjpl5puyuuq6lgmy4p3qedjd8ynn6");
|
||||||
|
let dummy_client = MockClient {
|
||||||
|
address: dummy_account,
|
||||||
|
vesting_contract: Addr::unchecked("n17tj0a0w6v7r2dc54rnkzfza6s8hxs87rj273a5"),
|
||||||
|
signing_nonce: 42,
|
||||||
|
};
|
||||||
|
|
||||||
|
let signing_msg_liquid = create_mixnode_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
dummy_mixnode.clone(),
|
||||||
|
dummy_cost_params.clone(),
|
||||||
|
dummy_pledge.clone(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let plaintext_liquid = signing_msg_liquid.to_plaintext().unwrap();
|
||||||
|
let sig_liquid: MessageSignature = identity_keypair
|
||||||
|
.private_key()
|
||||||
|
.sign(&plaintext_liquid)
|
||||||
|
.to_bytes()
|
||||||
|
.as_ref()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let signing_msg_vesting = create_mixnode_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
dummy_mixnode.clone(),
|
||||||
|
dummy_cost_params.clone(),
|
||||||
|
dummy_pledge.clone(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let plaintext_vesting = signing_msg_vesting.to_plaintext().unwrap();
|
||||||
|
let sig_vesting: MessageSignature = identity_keypair
|
||||||
|
.private_key()
|
||||||
|
.sign(&plaintext_vesting)
|
||||||
|
.to_bytes()
|
||||||
|
.as_ref()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let res = verify_mixnode_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
&dummy_mixnode,
|
||||||
|
&dummy_cost_params,
|
||||||
|
&dummy_pledge,
|
||||||
|
false,
|
||||||
|
&sig_liquid,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
let res = verify_mixnode_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
&dummy_mixnode,
|
||||||
|
&dummy_cost_params,
|
||||||
|
&dummy_pledge,
|
||||||
|
true,
|
||||||
|
&sig_vesting,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
let res = verify_mixnode_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
&dummy_mixnode,
|
||||||
|
&dummy_cost_params,
|
||||||
|
&dummy_pledge,
|
||||||
|
false,
|
||||||
|
&sig_vesting,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
let res = verify_mixnode_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
&dummy_mixnode,
|
||||||
|
&dummy_cost_params,
|
||||||
|
&dummy_pledge,
|
||||||
|
true,
|
||||||
|
&sig_liquid,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(res.is_err())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn dummy_gateway_bonding_signature_verification() {
|
||||||
|
let mut rng = test_rng();
|
||||||
|
let identity_keypair = identity::KeyPair::new(&mut rng);
|
||||||
|
let dummy_gateway = Gateway {
|
||||||
|
host: "1.2.3.4".to_string(),
|
||||||
|
mix_port: 1234,
|
||||||
|
clients_port: 2345,
|
||||||
|
location: "whatever".to_string(),
|
||||||
|
sphinx_key: "totally-legit-sphinx-key".to_string(),
|
||||||
|
identity_key: identity_keypair.public_key().to_base58_string(),
|
||||||
|
version: "v1.2.3".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let dummy_pledge: Coin = coin(10000000000, "unym").into();
|
||||||
|
|
||||||
|
let dummy_account = Addr::unchecked("n16t2umcd83zjpl5puyuuq6lgmy4p3qedjd8ynn6");
|
||||||
|
let dummy_client = MockClient {
|
||||||
|
address: dummy_account,
|
||||||
|
vesting_contract: Addr::unchecked("n17tj0a0w6v7r2dc54rnkzfza6s8hxs87rj273a5"),
|
||||||
|
signing_nonce: 42,
|
||||||
|
};
|
||||||
|
|
||||||
|
let signing_msg_liquid = create_gateway_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
dummy_gateway.clone(),
|
||||||
|
dummy_pledge.clone(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let plaintext_liquid = signing_msg_liquid.to_plaintext().unwrap();
|
||||||
|
let sig_liquid: MessageSignature = identity_keypair
|
||||||
|
.private_key()
|
||||||
|
.sign(&plaintext_liquid)
|
||||||
|
.to_bytes()
|
||||||
|
.as_ref()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let signing_msg_vesting = create_gateway_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
dummy_gateway.clone(),
|
||||||
|
dummy_pledge.clone(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let plaintext_vesting = signing_msg_vesting.to_plaintext().unwrap();
|
||||||
|
let sig_vesting: MessageSignature = identity_keypair
|
||||||
|
.private_key()
|
||||||
|
.sign(&plaintext_vesting)
|
||||||
|
.to_bytes()
|
||||||
|
.as_ref()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let res = verify_gateway_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
&dummy_gateway,
|
||||||
|
&dummy_pledge,
|
||||||
|
false,
|
||||||
|
&sig_liquid,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
let res = verify_gateway_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
&dummy_gateway,
|
||||||
|
&dummy_pledge,
|
||||||
|
true,
|
||||||
|
&sig_vesting,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
let res = verify_gateway_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
&dummy_gateway,
|
||||||
|
&dummy_pledge,
|
||||||
|
false,
|
||||||
|
&sig_vesting,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
let res = verify_gateway_bonding_sign_payload(
|
||||||
|
&dummy_client,
|
||||||
|
&dummy_gateway,
|
||||||
|
&dummy_pledge,
|
||||||
|
true,
|
||||||
|
&sig_liquid,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(res.is_err())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,12 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::error::BackendError;
|
use crate::error::BackendError;
|
||||||
|
use crate::operations::helpers::{
|
||||||
|
verify_gateway_bonding_sign_payload, verify_mixnode_bonding_sign_payload,
|
||||||
|
};
|
||||||
use crate::state::WalletState;
|
use crate::state::WalletState;
|
||||||
use crate::{nyxd_client, Gateway, MixNode};
|
use crate::{nyxd_client, Gateway, MixNode};
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_mixnet_contract_common::{MixId, MixNodeConfigUpdate};
|
use nym_mixnet_contract_common::{MixId, MixNodeConfigUpdate};
|
||||||
use nym_types::currency::DecCoin;
|
use nym_types::currency::DecCoin;
|
||||||
use nym_types::gateway::GatewayBond;
|
use nym_types::gateway::GatewayBond;
|
||||||
@@ -26,7 +30,7 @@ pub struct NodeDescription {
|
|||||||
pub async fn bond_gateway(
|
pub async fn bond_gateway(
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
pledge: DecCoin,
|
pledge: DecCoin,
|
||||||
owner_signature: String,
|
msg_signature: MessageSignature,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
state: tauri::State<'_, WalletState>,
|
state: tauri::State<'_, WalletState>,
|
||||||
) -> Result<TransactionExecuteResult, BackendError> {
|
) -> Result<TransactionExecuteResult, BackendError> {
|
||||||
@@ -41,10 +45,20 @@ pub async fn bond_gateway(
|
|||||||
pledge_base,
|
pledge_base,
|
||||||
fee,
|
fee,
|
||||||
);
|
);
|
||||||
let res = guard
|
|
||||||
.current_client()?
|
let client = guard.current_client()?;
|
||||||
|
// check the signature to make sure the user copied it correctly
|
||||||
|
if let Err(err) =
|
||||||
|
verify_gateway_bonding_sign_payload(client, &gateway, &pledge_base, false, &msg_signature)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
log::warn!("failed to verify provided gateway bonding signature: {err}");
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = client
|
||||||
.nyxd
|
.nyxd
|
||||||
.bond_gateway(gateway, owner_signature, pledge_base, fee)
|
.bond_gateway(gateway, msg_signature, pledge_base, fee)
|
||||||
.await?;
|
.await?;
|
||||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||||
log::trace!("<<< {:?}", res);
|
log::trace!("<<< {:?}", res);
|
||||||
@@ -73,7 +87,7 @@ pub async fn unbond_gateway(
|
|||||||
pub async fn bond_mixnode(
|
pub async fn bond_mixnode(
|
||||||
mixnode: MixNode,
|
mixnode: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
msg_signature: MessageSignature,
|
||||||
pledge: DecCoin,
|
pledge: DecCoin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
state: tauri::State<'_, WalletState>,
|
state: tauri::State<'_, WalletState>,
|
||||||
@@ -90,10 +104,26 @@ pub async fn bond_mixnode(
|
|||||||
pledge_base,
|
pledge_base,
|
||||||
fee,
|
fee,
|
||||||
);
|
);
|
||||||
let res = guard
|
|
||||||
.current_client()?
|
let client = guard.current_client()?;
|
||||||
|
// check the signature to make sure the user copied it correctly
|
||||||
|
if let Err(err) = verify_mixnode_bonding_sign_payload(
|
||||||
|
client,
|
||||||
|
&mixnode,
|
||||||
|
&cost_params,
|
||||||
|
&pledge_base,
|
||||||
|
false,
|
||||||
|
&msg_signature,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
log::warn!("failed to verify provided mixnode bonding signature: {err}");
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = client
|
||||||
.nyxd
|
.nyxd
|
||||||
.bond_mixnode(mixnode, cost_params, owner_signature, pledge_base, fee)
|
.bond_mixnode(mixnode, cost_params, msg_signature, pledge_base, fee)
|
||||||
.await?;
|
.await?;
|
||||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||||
log::trace!("<<< {:?}", res);
|
log::trace!("<<< {:?}", res);
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
pub mod help;
|
pub mod help;
|
||||||
|
pub(crate) mod helpers;
|
||||||
pub mod mixnet;
|
pub mod mixnet;
|
||||||
pub mod nym_api;
|
pub mod nym_api;
|
||||||
pub mod signatures;
|
pub mod signatures;
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::error::BackendError;
|
||||||
|
use crate::operations::helpers::{
|
||||||
|
create_gateway_bonding_sign_payload, create_mixnode_bonding_sign_payload,
|
||||||
|
};
|
||||||
|
use crate::state::WalletState;
|
||||||
|
use nym_mixnet_contract_common::{Gateway, MixNode};
|
||||||
|
use nym_types::currency::DecCoin;
|
||||||
|
use nym_types::mixnode::MixNodeCostParams;
|
||||||
|
|
||||||
|
async fn mixnode_bonding_msg_payload(
|
||||||
|
mixnode: MixNode,
|
||||||
|
cost_params: MixNodeCostParams,
|
||||||
|
pledge: DecCoin,
|
||||||
|
vesting: bool,
|
||||||
|
state: tauri::State<'_, WalletState>,
|
||||||
|
) -> Result<String, BackendError> {
|
||||||
|
let guard = state.read().await;
|
||||||
|
let reg = guard.registered_coins()?;
|
||||||
|
let pledge_base = guard.attempt_convert_to_base_coin(pledge.clone())?;
|
||||||
|
let cost_params = cost_params.try_convert_to_mixnet_contract_cost_params(reg)?;
|
||||||
|
log::info!(
|
||||||
|
">>> Bond mixnode bonding signature: identity_key = {}, pledge_display = {}, pledge_base = {}, vesting = {vesting}",
|
||||||
|
mixnode.identity_key,
|
||||||
|
pledge,
|
||||||
|
pledge_base,
|
||||||
|
);
|
||||||
|
|
||||||
|
let client = guard.current_client()?;
|
||||||
|
|
||||||
|
// TODO: decide on exact structure here. Json? base58? some hash?
|
||||||
|
// to be determined
|
||||||
|
let msg =
|
||||||
|
create_mixnode_bonding_sign_payload(client, mixnode, cost_params, pledge_base, vesting)
|
||||||
|
.await?;
|
||||||
|
Ok(msg.to_base58_string()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn gateway_bonding_msg_payload(
|
||||||
|
gateway: Gateway,
|
||||||
|
pledge: DecCoin,
|
||||||
|
vesting: bool,
|
||||||
|
state: tauri::State<'_, WalletState>,
|
||||||
|
) -> Result<String, BackendError> {
|
||||||
|
let guard = state.read().await;
|
||||||
|
let pledge_base = guard.attempt_convert_to_base_coin(pledge.clone())?;
|
||||||
|
log::info!(
|
||||||
|
">>> Bond gateway bonding signature: identity_key = {}, pledge_display = {}, pledge_base = {}, vesting = {vesting}",
|
||||||
|
gateway.identity_key,
|
||||||
|
pledge,
|
||||||
|
pledge_base,
|
||||||
|
);
|
||||||
|
|
||||||
|
let client = guard.current_client()?;
|
||||||
|
|
||||||
|
// TODO: decide on exact structure here. Json? base58? some hash?
|
||||||
|
// to be determined
|
||||||
|
let msg = create_gateway_bonding_sign_payload(client, gateway, pledge_base, vesting).await?;
|
||||||
|
Ok(msg.to_base58_string()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn generate_mixnode_bonding_msg_payload(
|
||||||
|
mixnode: MixNode,
|
||||||
|
cost_params: MixNodeCostParams,
|
||||||
|
pledge: DecCoin,
|
||||||
|
state: tauri::State<'_, WalletState>,
|
||||||
|
) -> Result<String, BackendError> {
|
||||||
|
mixnode_bonding_msg_payload(mixnode, cost_params, pledge, false, state).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn vesting_generate_mixnode_bonding_msg_payload(
|
||||||
|
mixnode: MixNode,
|
||||||
|
cost_params: MixNodeCostParams,
|
||||||
|
pledge: DecCoin,
|
||||||
|
state: tauri::State<'_, WalletState>,
|
||||||
|
) -> Result<String, BackendError> {
|
||||||
|
mixnode_bonding_msg_payload(mixnode, cost_params, pledge, true, state).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn generate_gateway_bonding_msg_payload(
|
||||||
|
gateway: Gateway,
|
||||||
|
pledge: DecCoin,
|
||||||
|
state: tauri::State<'_, WalletState>,
|
||||||
|
) -> Result<String, BackendError> {
|
||||||
|
gateway_bonding_msg_payload(gateway, pledge, false, state).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn vesting_generate_gateway_bonding_msg_payload(
|
||||||
|
gateway: Gateway,
|
||||||
|
pledge: DecCoin,
|
||||||
|
state: tauri::State<'_, WalletState>,
|
||||||
|
) -> Result<String, BackendError> {
|
||||||
|
gateway_bonding_msg_payload(gateway, pledge, true, state).await
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
pub mod ed25519_signing_payload;
|
||||||
pub mod sign;
|
pub mod sign;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use crate::error::BackendError;
|
use crate::error::BackendError;
|
||||||
use crate::operations::simulate::FeeDetails;
|
use crate::operations::simulate::FeeDetails;
|
||||||
use crate::WalletState;
|
use crate::WalletState;
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_mixnet_contract_common::MixNodeConfigUpdate;
|
use nym_mixnet_contract_common::MixNodeConfigUpdate;
|
||||||
use nym_mixnet_contract_common::{ExecuteMsg, Gateway, MixId, MixNode};
|
use nym_mixnet_contract_common::{ExecuteMsg, Gateway, MixId, MixNode};
|
||||||
use nym_types::currency::DecCoin;
|
use nym_types::currency::DecCoin;
|
||||||
@@ -39,13 +40,13 @@ async fn simulate_mixnet_operation(
|
|||||||
pub async fn simulate_bond_gateway(
|
pub async fn simulate_bond_gateway(
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
pledge: DecCoin,
|
pledge: DecCoin,
|
||||||
owner_signature: String,
|
msg_signature: MessageSignature,
|
||||||
state: tauri::State<'_, WalletState>,
|
state: tauri::State<'_, WalletState>,
|
||||||
) -> Result<FeeDetails, BackendError> {
|
) -> Result<FeeDetails, BackendError> {
|
||||||
simulate_mixnet_operation(
|
simulate_mixnet_operation(
|
||||||
ExecuteMsg::BondGateway {
|
ExecuteMsg::BondGateway {
|
||||||
gateway,
|
gateway,
|
||||||
owner_signature,
|
owner_signature: msg_signature,
|
||||||
},
|
},
|
||||||
Some(pledge),
|
Some(pledge),
|
||||||
&state,
|
&state,
|
||||||
@@ -64,7 +65,7 @@ pub async fn simulate_unbond_gateway(
|
|||||||
pub async fn simulate_bond_mixnode(
|
pub async fn simulate_bond_mixnode(
|
||||||
mixnode: MixNode,
|
mixnode: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
msg_signature: MessageSignature,
|
||||||
pledge: DecCoin,
|
pledge: DecCoin,
|
||||||
state: tauri::State<'_, WalletState>,
|
state: tauri::State<'_, WalletState>,
|
||||||
) -> Result<FeeDetails, BackendError> {
|
) -> Result<FeeDetails, BackendError> {
|
||||||
@@ -76,7 +77,7 @@ pub async fn simulate_bond_mixnode(
|
|||||||
ExecuteMsg::BondMixnode {
|
ExecuteMsg::BondMixnode {
|
||||||
mix_node: mixnode,
|
mix_node: mixnode,
|
||||||
cost_params,
|
cost_params,
|
||||||
owner_signature,
|
owner_signature: msg_signature,
|
||||||
},
|
},
|
||||||
Some(pledge),
|
Some(pledge),
|
||||||
&state,
|
&state,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use crate::error::BackendError;
|
use crate::error::BackendError;
|
||||||
use crate::operations::simulate::FeeDetails;
|
use crate::operations::simulate::FeeDetails;
|
||||||
use crate::WalletState;
|
use crate::WalletState;
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_mixnet_contract_common::MixNodeConfigUpdate;
|
use nym_mixnet_contract_common::MixNodeConfigUpdate;
|
||||||
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
|
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
|
||||||
use nym_types::currency::DecCoin;
|
use nym_types::currency::DecCoin;
|
||||||
@@ -40,7 +41,7 @@ async fn simulate_vesting_operation(
|
|||||||
pub async fn simulate_vesting_bond_gateway(
|
pub async fn simulate_vesting_bond_gateway(
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
pledge: DecCoin,
|
pledge: DecCoin,
|
||||||
owner_signature: String,
|
msg_signature: MessageSignature,
|
||||||
state: tauri::State<'_, WalletState>,
|
state: tauri::State<'_, WalletState>,
|
||||||
) -> Result<FeeDetails, BackendError> {
|
) -> Result<FeeDetails, BackendError> {
|
||||||
let guard = state.read().await;
|
let guard = state.read().await;
|
||||||
@@ -49,7 +50,7 @@ pub async fn simulate_vesting_bond_gateway(
|
|||||||
simulate_vesting_operation(
|
simulate_vesting_operation(
|
||||||
ExecuteMsg::BondGateway {
|
ExecuteMsg::BondGateway {
|
||||||
gateway,
|
gateway,
|
||||||
owner_signature,
|
owner_signature: msg_signature,
|
||||||
amount,
|
amount,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
@@ -69,7 +70,7 @@ pub async fn simulate_vesting_unbond_gateway(
|
|||||||
pub async fn simulate_vesting_bond_mixnode(
|
pub async fn simulate_vesting_bond_mixnode(
|
||||||
mixnode: MixNode,
|
mixnode: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
msg_signature: MessageSignature,
|
||||||
pledge: DecCoin,
|
pledge: DecCoin,
|
||||||
state: tauri::State<'_, WalletState>,
|
state: tauri::State<'_, WalletState>,
|
||||||
) -> Result<FeeDetails, BackendError> {
|
) -> Result<FeeDetails, BackendError> {
|
||||||
@@ -82,7 +83,7 @@ pub async fn simulate_vesting_bond_mixnode(
|
|||||||
ExecuteMsg::BondMixnode {
|
ExecuteMsg::BondMixnode {
|
||||||
mix_node: mixnode,
|
mix_node: mixnode,
|
||||||
cost_params,
|
cost_params,
|
||||||
owner_signature,
|
owner_signature: msg_signature,
|
||||||
amount,
|
amount,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -2,8 +2,12 @@ use crate::error::BackendError;
|
|||||||
use crate::nyxd_client;
|
use crate::nyxd_client;
|
||||||
use crate::state::WalletState;
|
use crate::state::WalletState;
|
||||||
use crate::{Gateway, MixNode};
|
use crate::{Gateway, MixNode};
|
||||||
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_mixnet_contract_common::MixNodeConfigUpdate;
|
use nym_mixnet_contract_common::MixNodeConfigUpdate;
|
||||||
|
|
||||||
|
use crate::operations::helpers::{
|
||||||
|
verify_gateway_bonding_sign_payload, verify_mixnode_bonding_sign_payload,
|
||||||
|
};
|
||||||
use nym_types::currency::DecCoin;
|
use nym_types::currency::DecCoin;
|
||||||
use nym_types::mixnode::MixNodeCostParams;
|
use nym_types::mixnode::MixNodeCostParams;
|
||||||
use nym_types::transaction::TransactionExecuteResult;
|
use nym_types::transaction::TransactionExecuteResult;
|
||||||
@@ -13,7 +17,7 @@ use validator_client::nyxd::{Fee, VestingSigningClient};
|
|||||||
pub async fn vesting_bond_gateway(
|
pub async fn vesting_bond_gateway(
|
||||||
gateway: Gateway,
|
gateway: Gateway,
|
||||||
pledge: DecCoin,
|
pledge: DecCoin,
|
||||||
owner_signature: String,
|
msg_signature: MessageSignature,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
state: tauri::State<'_, WalletState>,
|
state: tauri::State<'_, WalletState>,
|
||||||
) -> Result<TransactionExecuteResult, BackendError> {
|
) -> Result<TransactionExecuteResult, BackendError> {
|
||||||
@@ -28,10 +32,20 @@ pub async fn vesting_bond_gateway(
|
|||||||
pledge_base,
|
pledge_base,
|
||||||
fee,
|
fee,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let client = guard.current_client()?;
|
||||||
|
// check the signature to make sure the user copied it correctly
|
||||||
|
if let Err(err) =
|
||||||
|
verify_gateway_bonding_sign_payload(client, &gateway, &pledge_base, true, &msg_signature)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
log::warn!("failed to verify provided gateway bonding signature: {err}");
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
let res = guard
|
let res = guard
|
||||||
.current_client()?
|
.current_client()?
|
||||||
.nyxd
|
.nyxd
|
||||||
.vesting_bond_gateway(gateway, &owner_signature, pledge_base, fee)
|
.vesting_bond_gateway(gateway, msg_signature, pledge_base, fee)
|
||||||
.await?;
|
.await?;
|
||||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||||
log::trace!("<<< {:?}", res);
|
log::trace!("<<< {:?}", res);
|
||||||
@@ -63,7 +77,7 @@ pub async fn vesting_unbond_gateway(
|
|||||||
pub async fn vesting_bond_mixnode(
|
pub async fn vesting_bond_mixnode(
|
||||||
mixnode: MixNode,
|
mixnode: MixNode,
|
||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
owner_signature: String,
|
msg_signature: MessageSignature,
|
||||||
pledge: DecCoin,
|
pledge: DecCoin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
state: tauri::State<'_, WalletState>,
|
state: tauri::State<'_, WalletState>,
|
||||||
@@ -81,10 +95,27 @@ pub async fn vesting_bond_mixnode(
|
|||||||
pledge_base,
|
pledge_base,
|
||||||
fee
|
fee
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let client = guard.current_client()?;
|
||||||
|
// check the signature to make sure the user copied it correctly
|
||||||
|
if let Err(err) = verify_mixnode_bonding_sign_payload(
|
||||||
|
client,
|
||||||
|
&mixnode,
|
||||||
|
&cost_params,
|
||||||
|
&pledge_base,
|
||||||
|
true,
|
||||||
|
&msg_signature,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
log::warn!("failed to verify provided mixnode bonding signature: {err}");
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
let res = guard
|
let res = guard
|
||||||
.current_client()?
|
.current_client()?
|
||||||
.nyxd
|
.nyxd
|
||||||
.vesting_bond_mixnode(mixnode, cost_params, &owner_signature, pledge_base, fee)
|
.vesting_bond_mixnode(mixnode, cost_params, msg_signature, pledge_base, fee)
|
||||||
.await?;
|
.await?;
|
||||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||||
log::trace!("<<< {:?}", res);
|
log::trace!("<<< {:?}", res);
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ pub(crate) async fn execute(
|
|||||||
nym_cli_commands::validator::mixnet::operators::gateway::MixnetOperatorsGatewayCommands::Unbound(_args) => {
|
nym_cli_commands::validator::mixnet::operators::gateway::MixnetOperatorsGatewayCommands::Unbound(_args) => {
|
||||||
nym_cli_commands::validator::mixnet::operators::gateway::unbond_gateway::unbond_gateway(create_signing_client(global_args, network_details)?).await
|
nym_cli_commands::validator::mixnet::operators::gateway::unbond_gateway::unbond_gateway(create_signing_client(global_args, network_details)?).await
|
||||||
},
|
},
|
||||||
|
nym_cli_commands::validator::mixnet::operators::gateway::MixnetOperatorsGatewayCommands::CreateGatewayBondingSignPayload(args) => {
|
||||||
|
nym_cli_commands::validator::mixnet::operators::gateway::gateway_bonding_sign_payload::create_payload(args,create_signing_client(global_args, network_details)?).await
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ pub(crate) async fn execute(
|
|||||||
nym_cli_commands::validator::mixnet::operators::mixnode::MixnetOperatorsMixnodeCommands::Unbound(args) => {
|
nym_cli_commands::validator::mixnet::operators::mixnode::MixnetOperatorsMixnodeCommands::Unbound(args) => {
|
||||||
nym_cli_commands::validator::mixnet::operators::mixnode::unbond_mixnode::unbond_mixnode(args, create_signing_client(global_args, network_details)?).await
|
nym_cli_commands::validator::mixnet::operators::mixnode::unbond_mixnode::unbond_mixnode(args, create_signing_client(global_args, network_details)?).await
|
||||||
}
|
}
|
||||||
|
nym_cli_commands::validator::mixnet::operators::mixnode::MixnetOperatorsMixnodeCommands::CreateMixnodeBondingSignPayload(args) => {
|
||||||
|
nym_cli_commands::validator::mixnet::operators::mixnode::mixnode_bonding_sign_payload::create_payload(args,create_signing_client(global_args, network_details)?).await
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user