Compare commits

...

14 Commits

Author SHA1 Message Date
pierre dd13073037 docs(connect-android): add build note 2023-03-15 12:29:23 +01:00
Pierre Dommerc 1010df1077 refactor(wallet): ui adjustments (#3182) 2023-03-15 12:22:00 +01:00
Bogdan-Ștefan Neacşu 9eaf9cf491 Feature/fix resharing (#3139)
* Compare verified vks against current group instead of initial dealers

* Fix various dkg logs

* API auto-advance epoch even on corrupt states

* Use verified vks as ultimate truth for dealers

* Set initial dealers based of verified vk

* Extend register period even more

* Fix test

* Use shares from current epoch

* Save initial dealers only when triggering resharing

* Fix tests

* Backup the last InProgress state too

* Reset previous signers that are not initial dealers

* Add unit test for bug reproduction

* More verbose debug logging

* Handle edge case for coconut keypair removal

* Update dkg api test

* Remove dealings directly for each key

* Replacement data is saved only on the first reshare start

* More debug logging

* On failed DKG, just reset

* Clippy fix
2023-03-15 11:16:43 +00:00
pierre 8e96318478 build(connect-android): try to fix fdroid build 2023-03-15 10:25:53 +01:00
Jędrzej Stuczyński 54287666e8 chore: simplify mnemonic zeroize story (#3165)
* updated bip39 dependency to simplify our zeroize story

* Replaced UserPassword wrapper with Zeroizing type alias

* fixed wallet-types cosmwasm-std dependency version
2023-03-13 11:29:56 +00:00
Tommy Verrall 6de829163d Merge pull request #3173 from nymtech/feature/nym-cli-tweak
Feature/nym cli tweak
2023-03-13 13:24:13 +02:00
benedettadavico adb5ed7c30 typo fix 2023-03-13 12:14:14 +01:00
benedettadavico 2b019e57df merge develop 2023-03-13 12:12:00 +01:00
benedettadavico 30c07712e3 format 2023-03-13 12:03:38 +01:00
benedettadavico 82c92501d9 vesting stuff 2023-03-13 12:01:22 +01:00
benedettadavico c2a871a1a7 typo 2023-03-10 17:10:27 +01:00
benedettadavico dfd7bd5889 adding pledge more 2023-03-10 17:10:01 +01:00
Pierre Dommerc 7ff043d8df Feature/bonding signature UI (#3157)
* wip

* updated gateways 'sign' command

* in-wallet verification of mix bonding signature

* changed signature of vesting contract trait method

* updated wallet bonding endpoints

* tauri commands for generating signing payloads

* renamed signer to sender

* verifying new signatures in the contract

* fixed existing mixnet unit tests

* unit tests for invalid signatures

* fixed other usages of MessageSignature + FromStr

* using base58-encoded serialization

* removed owner-signature from details response

* added ability to construct bonding payloads via nym-cli

* removed signature from bonding payload args

* moved 'message_type' from 'ContractMessageContent' to 'SignableMessage'

* refactor(wallet-rust): rename owner_signature args

* feat(wallet-bonding): handle user signature

* feat(wallet-bonding): fix bonding

* feat(wallet-bonding): fix lint issue

* feat(wallet-bonding): ui adjustment

* make the location field mandatory for payload signing

* feat(wallet-bonding): remove ownersignature field, remove dead code

---------

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
Co-authored-by: Tommy Verrall <tommyvez@protonmail.com>
2023-03-10 15:53:21 +01:00
Jon Häggblad c904d245d2 Remove old erc20 bandwidth-claim-contract (#3162) 2023-03-08 13:57:11 +01:00
151 changed files with 4634 additions and 2639 deletions
Generated
+33 -38
View File
@@ -302,22 +302,23 @@ dependencies = [
[[package]]
name = "bip39"
version = "1.0.1"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e89470017230c38e52b82b3ee3f530db1856ba1d434e3a67a3456a8a8dec5f"
checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f"
dependencies = [
"bitcoin_hashes",
"rand 0.6.5",
"rand_core 0.4.2",
"rand 0.8.5",
"rand_core 0.6.4",
"serde",
"unicode-normalization",
"zeroize",
]
[[package]]
name = "bitcoin_hashes"
version = "0.9.7"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1"
checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4"
[[package]]
name = "bitflags"
@@ -2845,12 +2846,6 @@ dependencies = [
"serde",
]
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.5.0"
@@ -3140,14 +3135,6 @@ dependencies = [
"ts-rs",
]
[[package]]
name = "nym-bandwidth-claim-contract"
version = "0.1.0"
dependencies = [
"schemars",
"serde",
]
[[package]]
name = "nym-bin-common"
version = "0.2.0"
@@ -3220,6 +3207,7 @@ dependencies = [
"log",
"nym-coconut-bandwidth-contract-common",
"nym-coconut-dkg-common",
"nym-contracts-common",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
"nym-network-defaults",
@@ -3515,6 +3503,7 @@ dependencies = [
"mixnode-common",
"nym-bin-common",
"nym-config",
"nym-contracts-common",
"nym-crypto",
"nym-nonexhaustive-delayqueue",
"nym-pemstore",
@@ -4177,7 +4166,7 @@ dependencies = [
"instant",
"libc",
"redox_syscall",
"smallvec 1.10.0",
"smallvec",
"winapi",
]
@@ -4190,7 +4179,7 @@ dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec 1.10.0",
"smallvec",
"windows-sys 0.45.0",
]
@@ -5063,7 +5052,7 @@ dependencies = [
"pin-project-lite",
"ref-cast",
"serde",
"smallvec 1.10.0",
"smallvec",
"stable-pattern",
"state",
"time 0.3.17",
@@ -5558,15 +5547,6 @@ dependencies = [
"autocfg 1.1.0",
]
[[package]]
name = "smallvec"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
dependencies = [
"maybe-uninit",
]
[[package]]
name = "smallvec"
version = "1.10.0"
@@ -5731,7 +5711,7 @@ dependencies = [
"percent-encoding",
"rustls 0.19.1",
"sha2 0.10.6",
"smallvec 1.10.0",
"smallvec",
"sqlformat 0.1.8",
"sqlx-rt 0.5.13",
"stringprep",
@@ -5779,7 +5759,7 @@ dependencies = [
"rustls 0.20.8",
"rustls-pemfile",
"sha2 0.10.6",
"smallvec 1.10.0",
"smallvec",
"sqlformat 0.2.1",
"sqlx-rt 0.6.2",
"stringprep",
@@ -6204,6 +6184,21 @@ dependencies = [
"serde_json",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.25.0"
@@ -6501,7 +6496,7 @@ dependencies = [
"once_cell",
"regex",
"sharded-slab",
"smallvec 1.10.0",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
@@ -6667,11 +6662,11 @@ checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-normalization"
version = "0.1.9"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"smallvec 0.6.14",
"tinyvec",
]
[[package]]
+1 -1
View File
@@ -22,7 +22,6 @@ members = [
"clients/native",
"clients/native/websocket-requests",
"clients/socks5",
"common/bandwidth-claim-contract",
"common/bin-common",
"common/client-libs/gateway-client",
"common/client-libs/mixnet-client",
@@ -106,6 +105,7 @@ license = "Apache-2.0"
[workspace.dependencies]
async-trait = "0.1.64"
bip39 = { version = "2.0.0", features = ["zeroize"] }
cfg-if = "1.0.0"
dotenvy = "0.15.6"
lazy_static = "1.4.0"
+1 -1
View File
@@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bip39 = "1.0.1"
bip39 = { workspace = true }
clap = { version = "4.0", features = ["cargo", "derive"] }
log = "0.4"
rand = "0.7.3"
@@ -1,10 +0,0 @@
[package]
name = "nym-bandwidth-claim-contract"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
@@ -1,45 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
// Serializable structures for what we find in common/crypto
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct PublicKey([u8; 32]);
impl PublicKey {
pub fn new(bytes: [u8; 32]) -> Self {
PublicKey(bytes)
}
pub fn to_bytes(&self) -> [u8; 32] {
self.0
}
}
impl AsRef<[u8]> for PublicKey {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct Signature([u8; 32], [u8; 32]);
impl Signature {
pub fn new(bytes: [u8; 64]) -> Self {
let mut sig1 = [0u8; 32];
let mut sig2 = [0u8; 32];
sig1.copy_from_slice(&bytes[..32]);
sig2.copy_from_slice(&bytes[32..]);
Signature(sig1, sig2)
}
pub fn to_bytes(&self) -> [u8; 64] {
let mut res = [0u8; 64];
res[..32].copy_from_slice(&self.0);
res[32..].copy_from_slice(&self.1);
res
}
}
@@ -1,3 +0,0 @@
pub mod keys;
pub mod msg;
pub mod payment;
@@ -1,30 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::keys::PublicKey;
use crate::payment::LinkPaymentData;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct InstantiateMsg {}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
LinkPayment { data: LinkPaymentData },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
GetPayments {
limit: Option<u32>,
start_after: Option<PublicKey>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {}
@@ -1,73 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::keys::{PublicKey, Signature};
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
pub struct Payment {
verification_key: PublicKey,
gateway_identity: PublicKey,
bandwidth: u64,
}
impl Payment {
pub fn new(verification_key: PublicKey, gateway_identity: PublicKey, bandwidth: u64) -> Self {
Payment {
verification_key,
gateway_identity,
bandwidth,
}
}
pub fn verification_key(&self) -> PublicKey {
self.verification_key
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct LinkPaymentData {
pub verification_key: PublicKey,
pub gateway_identity: PublicKey,
pub bandwidth: u64,
pub signature: Signature,
}
impl LinkPaymentData {
pub fn new(
verification_key: [u8; 32],
gateway_identity: [u8; 32],
bandwidth: u64,
signature: [u8; 64],
) -> Self {
LinkPaymentData {
verification_key: PublicKey::new(verification_key),
gateway_identity: PublicKey::new(gateway_identity),
bandwidth,
signature: Signature::new(signature),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
pub struct PagedPaymentResponse {
pub payments: Vec<Payment>,
pub per_page: usize,
pub start_next_after: Option<PublicKey>,
}
impl PagedPaymentResponse {
pub fn new(
payments: Vec<Payment>,
per_page: usize,
start_next_after: Option<PublicKey>,
) -> Self {
PagedPaymentResponse {
payments,
per_page,
start_next_after,
}
}
}
@@ -37,7 +37,7 @@ nym-execute = { path = "../../execute" }
# at some point it might be possible to make it wasm-compatible
# perhaps after https://github.com/cosmos/cosmos-rust/pull/97 is resolved (and tendermint-rs is updated)
async-trait = { workspace = true, optional = true }
bip39 = { version = "1", features = ["rand"], optional = true }
bip39 = { workspace = true, features = ["rand"], optional = true }
nym-config = { path = "../../config", optional = true }
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true}
cw3 = { version = "0.13.4", optional = true }
@@ -47,7 +47,7 @@ flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true }
itertools = { version = "0.10", optional = true }
cosmwasm-std = { version = "1.0.0", optional = true }
zeroize = { version = "1.5.7", optional = true }
zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
[dev-dependencies]
ts-rs = "6.1.2"
@@ -43,6 +43,7 @@ pub use cosmrs::tendermint::Time as TendermintTime;
pub use cosmrs::tx::{self, Gas};
pub use cosmrs::Coin as CosmosCoin;
pub use cosmrs::{bip32, AccountId, Decimal, Denom};
use cosmwasm_std::Addr;
pub use cosmwasm_std::Coin as CosmWasmCoin;
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
pub use signing_client::Client as SigningNyxdClient;
@@ -369,6 +370,15 @@ where
&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
where
C: SigningCosmWasmClient,
@@ -6,6 +6,7 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::NyxdClient;
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::signing::Nonce;
use nym_mixnet_contract_common::delegation::{MixNodeDelegationResponse, OwnerProxySubKey};
use nym_mixnet_contract_common::families::Family;
use nym_mixnet_contract_common::mixnode::{
@@ -390,6 +391,13 @@ pub trait MixnetQueryClient {
.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> {
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel {
label: label.to_string(),
@@ -8,6 +8,7 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient};
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
use nym_mixnet_contract_common::{
@@ -290,7 +291,7 @@ pub trait MixnetSigningClient {
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -311,7 +312,7 @@ pub trait MixnetSigningClient {
owner: AccountId,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -442,7 +443,7 @@ pub trait MixnetSigningClient {
async fn bond_gateway(
&self,
gateway: Gateway,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -461,7 +462,7 @@ pub trait MixnetSigningClient {
&self,
owner: AccountId,
gateway: Gateway,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -6,6 +6,7 @@ use crate::nyxd::cosmwasm_client::types::ExecuteResult;
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Coin, Fee, NyxdClient};
use async_trait::async_trait;
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
use nym_vesting_contract_common::messages::{
@@ -43,7 +44,7 @@ pub trait VestingSigningClient {
async fn vesting_bond_gateway(
&self,
gateway: Gateway,
owner_signature: &str,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError>;
@@ -61,7 +62,7 @@ pub trait VestingSigningClient {
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: &str,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError>;
@@ -208,14 +209,14 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
async fn vesting_bond_gateway(
&self,
gateway: Gateway,
owner_signature: &str,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::BondGateway {
gateway,
owner_signature: owner_signature.to_string(),
owner_signature,
amount: pledge.into(),
};
self.client
@@ -272,7 +273,7 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: &str,
owner_signature: MessageSignature,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -281,7 +282,7 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
VestingExecuteMsg::BondMixnode {
mix_node,
cost_params,
owner_signature: owner_signature.to_string(),
owner_signature,
amount: pledge.into(),
},
vec![],
@@ -8,7 +8,7 @@ use cosmrs::crypto::PublicKey;
use cosmrs::tx::SignDoc;
use cosmrs::{tx, AccountId};
use nym_config::defaults;
use zeroize::Zeroize;
use zeroize::{Zeroize, ZeroizeOnDrop};
/// Derivation information required to derive a keypair and an address from a mnemonic.
#[derive(Debug, Clone)]
@@ -41,7 +41,7 @@ impl AccountData {
type Secp256k1Keypair = (SigningKey, PublicKey);
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)]
pub struct DirectSecp256k1HdWallet {
/// Base secret
secret: bip39::Mnemonic,
@@ -54,30 +54,10 @@ pub struct DirectSecp256k1HdWallet {
// that would include the secret key which is a dyn EcdsaSigner and hence not Sync making the wallet
// not Sync and if used on the signing client in an async trait, it wouldn't be Send
/// Derivation instructions
#[zeroize(skip)]
accounts: Vec<Secp256k1Derivation>,
}
impl Zeroize for DirectSecp256k1HdWallet {
fn zeroize(&mut self) {
// in ideal world, Mnemonic would have had zeroize defined on it (there's an almost year old PR that introduces it)
// and the memory would have been filled with zeroes.
//
// we really don't want to keep our real mnemonic in memory, so let's do the semi-nasty thing
// of overwriting it with a fresh mnemonic that was never used before
//
// note: this function can only fail on an invalid word count, which clearly is not the case here
self.secret = bip39::Mnemonic::generate(self.secret.word_count()).unwrap();
self.seed.zeroize();
// there's nothing secret about derivation paths
}
}
impl Drop for DirectSecp256k1HdWallet {
fn drop(&mut self) {
self.zeroize()
}
}
impl DirectSecp256k1HdWallet {
pub fn builder(prefix: &str) -> DirectSecp256k1HdWalletBuilder {
DirectSecp256k1HdWalletBuilder::new(prefix)
+2 -1
View File
@@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
base64 = "0.13.0"
bip39 = "1.0.1"
bip39 = { workspace = true }
bs58 = "0.4"
comfy-table = "6.0.0"
cfg-if = "1.0.0"
@@ -30,6 +30,7 @@ cosmwasm-std = { version = "1.0.0" }
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
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-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" }
nym-coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
+10 -3
View File
@@ -1,13 +1,20 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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::fmt::Display;
use cosmwasm_std::{Coin as CosmWasmCoin, Decimal};
use log::error;
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 {
let amount = Decimal::from_ratio(coin.amount, 1_000_000u128);
let denom = if coin.denom.starts_with('u') {
@@ -6,9 +6,11 @@ use clap::{Args, Subcommand};
pub mod rewards;
pub mod delegate_to_mixnode;
pub mod pledge_more;
pub mod query_for_delegations;
pub mod undelegate_from_mixnode;
pub mod vesting_delegate_to_mixnode;
pub mod vesting_pledge_more;
pub mod vesting_undelegate_from_mixnode;
#[derive(Debug, Args)]
@@ -32,4 +34,8 @@ pub enum MixnetDelegatorsCommands {
DelegateVesting(vesting_delegate_to_mixnode::Args),
/// Undelegate from a mixnode (when originally using locked tokens)
UndelegateVesting(vesting_undelegate_from_mixnode::Args),
/// Pledge more
PledgeMore(pledge_more::Args),
/// Pledge more with locked tokens
PledgeMoreVesting(vesting_pledge_more::Args),
}
@@ -0,0 +1,29 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use nym_mixnet_contract_common::Coin;
use validator_client::nyxd::traits::MixnetSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub amount: u128,
}
pub async fn pledge_more(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
info!("Starting to pledge more");
let coin = Coin::new(args.amount, denom);
let res = client
.pledge_more(coin.into(), None)
.await
.expect("failed to pledge more!");
info!("pledging more: {:?}", res);
}
@@ -0,0 +1,30 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use nym_mixnet_contract_common::Coin;
use validator_client::nyxd::VestingSigningClient;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub amount: u128,
}
pub async fn vesting_pledge_more(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
info!("Starting vesting pledge more");
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_pledge_more(coin.into(), None)
.await
.expect("failed to pledge more!");
info!("vesting pledge more: {:?}", res);
}
@@ -4,6 +4,7 @@
use crate::context::SigningClient;
use clap::Parser;
use log::{info, warn};
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::Coin;
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use validator_client::nyxd::traits::MixnetSigningClient;
@@ -14,7 +15,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: String,
pub signature: MessageSignature,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -23,7 +24,7 @@ pub struct Args {
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
pub location: String,
#[clap(long)]
pub sphinx_key: String,
@@ -59,9 +60,7 @@ pub async fn bond_gateway(args: Args, client: SigningClient) {
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()),
location: args.location,
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
@@ -0,0 +1,81 @@
// 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: 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,
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};
pub mod bond_gateway;
pub mod gateway_bonding_sign_payload;
pub mod unbond_gateway;
pub mod vesting_bond_gateway;
pub mod vesting_unbond_gateway;
@@ -19,10 +20,12 @@ pub struct MixnetOperatorsGateway {
pub enum MixnetOperatorsGatewayCommands {
/// Bond to a gateway
Bond(bond_gateway::Args),
/// Unbound from a gateway
Unbound(unbond_gateway::Args),
/// Unbond from a gateway
Unbond(unbond_gateway::Args),
/// Bond to a gateway with locked tokens
VestingBond(vesting_bond_gateway::Args),
/// Unbound from a gateway (when originally using locked tokens)
VestingUnbound(vesting_unbond_gateway::Args),
/// Unbond from a gateway (when originally using locked tokens)
VestingUnbond(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 clap::Parser;
use log::{info, warn};
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::{Coin, Gateway};
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use validator_client::nyxd::VestingSigningClient;
@@ -14,7 +15,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: String,
pub signature: MessageSignature,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -23,7 +24,7 @@ pub struct Args {
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
pub location: String,
#[clap(long)]
pub sphinx_key: String,
@@ -57,9 +58,7 @@ pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str
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()),
location: args.location,
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
@@ -68,7 +67,7 @@ pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_bond_gateway(gateway, &args.signature, coin.into(), None)
.vesting_bond_gateway(gateway, args.signature, coin.into(), None)
.await
.expect("failed to bond gateway!");
@@ -5,6 +5,7 @@ use clap::Parser;
use cosmwasm_std::Uint128;
use log::{info, warn};
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::{Coin, MixNodeCostParams, Percent};
use nym_network_defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
@@ -20,7 +21,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: String,
pub signature: MessageSignature,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -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 keys;
pub mod mixnode_bonding_sign_payload;
pub mod rewards;
pub mod settings;
pub mod unbond_mixnode;
@@ -28,10 +29,12 @@ pub enum MixnetOperatorsMixnodeCommands {
Settings(settings::MixnetOperatorsMixnodeSettings),
/// Bond to a mixnode
Bond(bond_mixnode::Args),
/// Unbound from a mixnode
Unbound(unbond_mixnode::Args),
/// Unbond from a mixnode
Unbond(unbond_mixnode::Args),
/// Bond to a mixnode with locked tokens
BondVesting(vesting_bond_mixnode::Args),
/// Unbound from a mixnode (when originally using locked tokens)
UnboundVesting(vesting_unbond_mixnode::Args),
/// Unbond from a mixnode (when originally using locked tokens)
UnbondVesting(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
use crate::context::SigningClient;
use clap::Parser;
use cosmwasm_std::Uint128;
use log::{info, warn};
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::{Coin, MixNodeCostParams};
use nym_mixnet_contract_common::{MixNode, Percent};
use nym_network_defaults::{
@@ -18,7 +19,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: String,
pub signature: MessageSignature,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -95,7 +96,7 @@ pub async fn vesting_bond_mixnode(client: SigningClient, args: Args, denom: &str
};
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
.expect("failed to bond vesting mixnode!");
@@ -21,7 +21,7 @@ pub const TOTAL_DEALINGS: usize = 2 + 2 + 1;
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
pub struct InitialReplacementData {
pub initial_dealers: Vec<Addr>,
pub initial_height: Option<u64>,
pub initial_height: u64,
}
#[derive(
@@ -6,6 +6,7 @@
pub mod dealings;
pub mod events;
pub mod signing;
pub mod 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
use crate::{EpochState, IdentityKey, MixId};
use contracts_common::signing::verifier::ApiVerifierError;
use cosmwasm_std::{Addr, Coin, Decimal};
use thiserror::Error;
@@ -218,4 +219,10 @@ pub enum MixnetContractError {
value: 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 reward_params;
pub mod rewarding;
pub mod signing_types;
mod types;
pub use contracts_common::types::*;
@@ -43,4 +44,5 @@ pub use pending_events::{
PendingIntervalEventData, PendingIntervalEventKind,
};
pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams};
pub use signing_types::*;
pub use types::*;
@@ -10,6 +10,7 @@ use crate::reward_params::{
};
use crate::{delegation, ContractStateParams, Layer, LayerAssignment, MixId, Percent};
use crate::{Gateway, IdentityKey, MixNode};
use contracts_common::signing::MessageSignature;
use cosmwasm_std::Decimal;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -153,12 +154,12 @@ pub enum ExecuteMsg {
BondMixnode {
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
},
BondMixnodeOnBehalf {
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
owner: String,
},
PledgeMore {},
@@ -187,12 +188,12 @@ pub enum ExecuteMsg {
// gateway-related:
BondGateway {
gateway: Gateway,
owner_signature: String,
owner_signature: MessageSignature,
},
BondGatewayOnBehalf {
gateway: Gateway,
owner: String,
owner_signature: String,
owner_signature: MessageSignature,
},
UnbondGateway {},
UnbondGatewayOnBehalf {
@@ -500,6 +501,11 @@ pub enum QueryMsg {
start_after: Option<u32>,
},
GetNumberOfPendingEvents {},
// signing-related
GetSigningNonce {
address: String,
},
}
#[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 mixnet_contract_common::{
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
@@ -118,7 +119,7 @@ pub enum ExecuteMsg {
BondMixnode {
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
amount: Coin,
},
PledgeMore {
@@ -131,7 +132,7 @@ pub enum ExecuteMsg {
},
BondGateway {
gateway: Gateway,
owner_signature: String,
owner_signature: MessageSignature,
amount: Coin,
},
UnbondGateway {},
-2
View File
@@ -81,7 +81,6 @@ impl GatewayBond {
pub struct GatewayNodeDetailsResponse {
pub identity_key: String,
pub sphinx_key: String,
pub owner_signature: String,
pub announce_address: String,
pub bind_address: String,
pub version: String,
@@ -94,7 +93,6 @@ impl fmt::Display for GatewayNodeDetailsResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Identity Key: {}", self.identity_key)?;
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
writeln!(
f,
"Host: {} (bind address: {})",
-2
View File
@@ -167,7 +167,6 @@ impl MixNodeCostParams {
pub struct MixnodeNodeDetailsResponse {
pub identity_key: String,
pub sphinx_key: String,
pub owner_signature: String,
pub announce_address: String,
pub bind_address: String,
pub version: String,
@@ -182,7 +181,6 @@ impl fmt::Display for MixnodeNodeDetailsResponse {
let wallet_address = self.wallet_address.clone().unwrap_or_default();
writeln!(f, "Identity Key: {}", self.identity_key)?;
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
writeln!(
f,
"Host: {} (bind address: {})",
-1
View File
@@ -9,7 +9,6 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]
[dependencies]
nym-bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" }
nym-coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" }
@@ -22,7 +22,7 @@ fn verify_dealer(deps: DepsMut<'_>, dealer: &Addr, resharing: bool) -> Result<()
let state = STATE.load(deps.storage)?;
let height = if resharing {
INITIAL_REPLACEMENT_DATA.load(deps.storage)?.initial_height
Some(INITIAL_REPLACEMENT_DATA.load(deps.storage)?.initial_height)
} else {
None
};
@@ -105,7 +105,7 @@ pub(crate) mod tests {
deps.as_mut().storage,
&InitialReplacementData {
initial_dealers: vec![details1.address, details2.address, details3.address],
initial_height: Some(1),
initial_height: 1,
},
)
.unwrap();
@@ -126,7 +126,7 @@ pub(crate) mod tests {
INITIAL_REPLACEMENT_DATA
.update::<_, ContractError>(deps.as_mut().storage, |mut data| {
data.initial_height = Some(2);
data.initial_height = 2;
Ok(data)
})
.unwrap();
@@ -110,7 +110,7 @@ pub(crate) mod tests {
deps.as_mut().storage,
&InitialReplacementData {
initial_dealers: vec![],
initial_height: None,
initial_height: 1,
},
)
.unwrap();
@@ -7,6 +7,7 @@ use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA, THRES
use crate::epoch_state::utils::check_epoch_state;
use crate::error::ContractError;
use crate::state::STATE;
use crate::verification_key_shares::storage::verified_dealers;
use cosmwasm_std::{Addr, Deps, DepsMut, Env, Order, Response, Storage};
use nym_coconut_dkg_common::types::{Epoch, EpochState, InitialReplacementData};
@@ -19,7 +20,13 @@ fn reset_epoch_state(storage: &mut dyn Storage) -> Result<(), ContractError> {
for dealer_addr in dealers {
let details = current_dealers().load(storage, &dealer_addr)?;
for dealings in DEALINGS_BYTES {
dealings.remove(storage, &details.address);
let dealing_keys: Vec<_> = dealings
.keys(storage, None, None, Order::Ascending)
.flatten()
.collect();
for key in dealing_keys {
dealings.remove(storage, &key);
}
}
current_dealers().remove(storage, &dealer_addr)?;
past_dealers().save(storage, &dealer_addr, &details)?;
@@ -46,15 +53,9 @@ fn dealers_still_active(
}
fn dealers_eq_members(deps: &DepsMut<'_>) -> Result<bool, ContractError> {
let dealers_still_active = dealers_still_active(
&deps.as_ref(),
current_dealers()
.keys(deps.storage, None, None, Order::Ascending)
.flatten(),
)?;
let all_dealers = current_dealers()
.keys(deps.storage, None, None, Order::Ascending)
.count();
let verified_dealers = verified_dealers(deps.storage)?;
let all_dealers = verified_dealers.len();
let dealers_still_active = dealers_still_active(&deps.as_ref(), verified_dealers.into_iter())?;
let group_members = STATE
.load(deps.storage)?
.group_addr
@@ -66,7 +67,11 @@ fn dealers_eq_members(deps: &DepsMut<'_>) -> Result<bool, ContractError> {
fn replacement_threshold_surpassed(deps: &DepsMut<'_>) -> Result<bool, ContractError> {
let threshold = THRESHOLD.load(deps.storage)? as usize;
let initial_dealers = INITIAL_REPLACEMENT_DATA.load(deps.storage)?.initial_dealers;
let initial_dealers = verified_dealers(deps.storage)?;
if initial_dealers.is_empty() {
// possibly failed DKG, just reset and start again
return Ok(true);
}
let initial_dealer_count = initial_dealers.len();
let replacement_threshold = threshold - (initial_dealers.len() + 2 - 1) / 2 + 1;
let removed_dealer_count =
@@ -90,24 +95,23 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
let next_epoch = if let Some(state) = current_epoch.state.next() {
// We are during DKG process
let mut new_state = state;
if let EpochState::DealingExchange { resharing } = state {
if let EpochState::DealingExchange { .. } = state {
let current_dealers = current_dealers()
.keys(deps.storage, None, None, Order::Ascending)
.collect::<Result<Vec<Addr>, _>>()?;
if current_dealers.is_empty() {
// If no dealer registered yet, we just stay in the same state until there's at least one
let group_members =
STATE
.load(deps.storage)?
.group_addr
.list_members(&deps.querier, None, None)?;
if current_dealers.len() < group_members.len() {
// If not all group members registered yet, we just stay in the same state until
// they either register or they get kicked out of the group
new_state = current_epoch.state;
} else {
// note: ceiling in integer division can be achieved via q = (x + y - 1) / y;
let threshold = (2 * current_dealers.len() as u64 + 3 - 1) / 3;
THRESHOLD.save(deps.storage, &threshold)?;
if !resharing {
let replacement_data = InitialReplacementData {
initial_dealers: current_dealers,
initial_height: None,
};
INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?;
}
}
};
Epoch::new(
@@ -129,13 +133,23 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
// Dealer set changed, we need to redo DKG...
let state = if replacement_threshold_surpassed(&deps)? {
// ... in reset mode
INITIAL_REPLACEMENT_DATA.remove(deps.storage);
EpochState::default()
} else {
// ... in reshare mode
INITIAL_REPLACEMENT_DATA.update::<_, ContractError>(deps.storage, |mut data| {
data.initial_height = Some(env.block.height);
Ok(data)
})?;
if INITIAL_REPLACEMENT_DATA.may_load(deps.storage)?.is_some() {
INITIAL_REPLACEMENT_DATA.update::<_, ContractError>(deps.storage, |mut data| {
data.initial_height = env.block.height;
Ok(data)
})?;
} else {
let replacement_data = InitialReplacementData {
initial_dealers: verified_dealers(deps.storage)?,
initial_height: env.block.height,
};
INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?;
}
EpochState::PublicKeySubmission { resharing: true }
};
reset_epoch_state(deps.storage)?;
@@ -158,10 +172,8 @@ pub(crate) fn try_surpassed_threshold(
check_epoch_state(deps.storage, EpochState::InProgress)?;
let threshold = THRESHOLD.load(deps.storage)?;
let dealers = current_dealers()
.keys(deps.storage, None, None, Order::Ascending)
.flatten();
if dealers_still_active(&deps.as_ref(), dealers)? < threshold as usize {
let dealers = verified_dealers(deps.storage)?;
if dealers_still_active(&deps.as_ref(), dealers.into_iter())? < threshold as usize {
reset_epoch_state(deps.storage)?;
CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |epoch| {
Ok(Epoch::new(
@@ -180,8 +192,9 @@ pub(crate) fn try_surpassed_threshold(
pub(crate) mod tests {
use super::*;
use crate::error::ContractError::EarlyEpochStateAdvancement;
use crate::support::tests::fixtures::dealer_details_fixture;
use crate::support::tests::fixtures::{dealer_details_fixture, vk_share_fixture};
use crate::support::tests::helpers::{init_contract, GROUP_MEMBERS};
use crate::verification_key_shares::storage::vk_shares;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::Addr;
use cw4::Member;
@@ -204,11 +217,15 @@ pub(crate) mod tests {
for n in [10, 25, 50, 100] {
let dealers: Vec<_> = (0..n).map(dealer_details_fixture).collect();
let shares: Vec<_> = (0..n).map(|idx| vk_share_fixture(&format!("owner{}", idx), 0)).collect();
let initial_dealers = dealers.iter().map(|d| d.address.clone()).collect();
let data = InitialReplacementData {
initial_dealers,
initial_height: None,
initial_height: 1,
};
for share in shares {
vk_shares().save(deps.as_mut().storage, (&share.owner, 0), &share).unwrap();
}
for f in [two_thirds, three_fourths, ninty_pc] {
let threshold = f(n);
THRESHOLD.save(deps.as_mut().storage, &threshold).unwrap();
@@ -247,39 +264,39 @@ pub(crate) mod tests {
assert!(dealers_eq_members(&deps.as_mut()).unwrap());
let details = dealer_details_fixture(1);
let different_details = dealer_details_fixture(2);
current_dealers()
.save(deps.as_mut().storage, &details.address, &details)
let share = vk_share_fixture("owner2", 0);
let different_share = vk_share_fixture("owner4", 0);
vk_shares()
.save(deps.as_mut().storage, (&share.owner, 0), &share)
.unwrap();
assert!(!dealers_eq_members(&deps.as_mut()).unwrap());
current_dealers()
.remove(deps.as_mut().storage, &details.address)
vk_shares()
.remove(deps.as_mut().storage, (&share.owner, 0))
.unwrap();
GROUP_MEMBERS.lock().unwrap().push((
Member {
addr: "owner1".to_string(),
addr: "owner2".to_string(),
weight: 10,
},
1,
));
assert!(!dealers_eq_members(&deps.as_mut()).unwrap());
current_dealers()
vk_shares()
.save(
deps.as_mut().storage,
&different_details.address,
&different_details,
(&different_share.owner, 0),
&different_share,
)
.unwrap();
assert!(!dealers_eq_members(&deps.as_mut()).unwrap());
current_dealers()
.remove(deps.as_mut().storage, &different_details.address)
vk_shares()
.remove(deps.as_mut().storage, (&different_share.owner, 0))
.unwrap();
current_dealers()
.save(deps.as_mut().storage, &details.address, &details)
vk_shares()
.save(deps.as_mut().storage, (&share.owner, 0), &share)
.unwrap();
assert!(dealers_eq_members(&deps.as_mut()).unwrap());
}
@@ -407,6 +424,12 @@ pub(crate) mod tests {
);
// setup dealer details
let all_shares: [_; 4] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
for share in all_shares.iter() {
vk_shares()
.save(deps.as_mut().storage, (&share.owner, 0), share)
.unwrap();
}
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1));
for details in all_details.iter() {
current_dealers()
@@ -431,12 +454,6 @@ pub(crate) mod tests {
.time
.plus_seconds(epoch.time_configuration.dealing_exchange_time_secs)
);
let replacement_data = INITIAL_REPLACEMENT_DATA.load(&deps.storage).unwrap();
let expected_replacement_data = InitialReplacementData {
initial_dealers: all_details.iter().map(|d| d.address.clone()).collect(),
initial_height: None,
};
assert_eq!(replacement_data, expected_replacement_data);
env.block.time = env
.block
@@ -588,8 +605,14 @@ pub(crate) mod tests {
);
assert_eq!(curr_epoch, expected_epoch);
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
let replacement_data = INITIAL_REPLACEMENT_DATA.load(&deps.storage).unwrap();
let expected_replacement_data = InitialReplacementData {
initial_dealers: all_details.iter().map(|d| d.address.clone()).collect(),
initial_height: 12345,
};
assert_eq!(replacement_data, expected_replacement_data);
let all_details: [_; 2] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 2));
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 2));
for details in all_details.iter() {
past_dealers().remove(deps.as_mut().storage, &details.address).unwrap();
current_dealers()
@@ -607,6 +630,17 @@ pub(crate) mod tests {
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
}
let all_shares: [_; 4] = std::array::from_fn(|i| {
let mut share = vk_share_fixture(&format!("owner{}", i + 1), 1);
share.verified = i % 2 == 0;
share
});
for share in all_shares.iter() {
vk_shares()
.save(deps.as_mut().storage, (&share.owner, 0), share)
.unwrap();
}
// Group changed even more, surpassing threshold, so re-run dkg in reset mode
*GROUP_MEMBERS.lock().unwrap().last_mut().unwrap() = (
Member {
@@ -623,7 +657,7 @@ pub(crate) mod tests {
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
let expected_epoch = Epoch::new(
EpochState::PublicKeySubmission { resharing: false },
EpochState::PublicKeySubmission { resharing: true },
prev_epoch.epoch_id + 1,
prev_epoch.time_configuration,
env.block.time,
@@ -672,12 +706,25 @@ pub(crate) mod tests {
}
);
let all_shares: [_; 3] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
for share in all_shares.iter() {
vk_shares()
.save(deps.as_mut().storage, (&share.owner, 0), share)
.unwrap();
}
let all_details: [_; 3] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1));
for details in all_details.iter() {
current_dealers()
.save(deps.as_mut().storage, &details.address, details)
.unwrap();
}
let all_shares: [_; 3] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
for share in all_shares.iter() {
vk_shares()
.save(deps.as_mut().storage, (&share.owner, share.epoch_id), share)
.unwrap();
}
for times in [
time_configuration.public_key_submission_time_secs,
@@ -4,7 +4,9 @@
// SPDX-License-Identifier: Apache-2.0
use crate::constants::{VK_SHARES_EPOCH_ID_IDX_NAMESPACE, VK_SHARES_PK_NAMESPACE};
use cosmwasm_std::Addr;
use crate::epoch_state::storage::CURRENT_EPOCH;
use crate::error::ContractError;
use cosmwasm_std::{Addr, Order, Storage};
use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex};
use nym_coconut_dkg_common::types::EpochId;
use nym_coconut_dkg_common::verification_key::ContractVKShare;
@@ -35,3 +37,21 @@ pub(crate) fn vk_shares<'a>() -> IndexedMap<'a, VKShareKey<'a>, ContractVKShare,
};
IndexedMap::new(VK_SHARES_PK_NAMESPACE, indexes)
}
pub(crate) fn verified_dealers(storage: &dyn Storage) -> Result<Vec<Addr>, ContractError> {
let epoch_id = CURRENT_EPOCH.load(storage)?.epoch_id;
Ok(vk_shares()
.idx
.epoch_id
.prefix(epoch_id)
.range(storage, None, None, Order::Ascending)
.flatten()
.filter_map(|(_, share)| {
if share.verified {
Some(share.owner)
} else {
None
}
})
.collect())
}
-1
View File
@@ -7,7 +7,6 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nym-bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" }
nym-coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" }
+1 -2
View File
@@ -24,7 +24,7 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
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" }
#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-storage = "1.0.0"
@@ -41,7 +41,6 @@ semver = { version = "1.0.16", default-features = false }
[dev-dependencies]
cosmwasm-schema = "1.0.0"
rand_chacha = "0.2"
#rand = "0.7"
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
[build-dependencies]
+32 -30
View File
@@ -39,41 +39,43 @@ pub const FAMILIES_DEFAULT_RETRIEVAL_LIMIT: u32 = 10;
pub const FAMILIES_MAX_RETRIEVAL_LIMIT: u32 = 20;
// storage keys
pub(crate) const DELEGATION_PK_NAMESPACE: &str = "dl";
pub(crate) const DELEGATION_OWNER_IDX_NAMESPACE: &str = "dlo";
pub(crate) const DELEGATION_MIXNODE_IDX_NAMESPACE: &str = "dlm";
pub const DELEGATION_PK_NAMESPACE: &str = "dl";
pub const DELEGATION_OWNER_IDX_NAMESPACE: &str = "dlo";
pub const DELEGATION_MIXNODE_IDX_NAMESPACE: &str = "dlm";
pub(crate) const GATEWAYS_PK_NAMESPACE: &str = "gt";
pub(crate) const GATEWAYS_OWNER_IDX_NAMESPACE: &str = "gto";
pub const GATEWAYS_PK_NAMESPACE: &str = "gt";
pub const GATEWAYS_OWNER_IDX_NAMESPACE: &str = "gto";
pub(crate) const REWARDED_SET_KEY: &str = "rs";
pub(crate) const CURRENT_EPOCH_STATUS_KEY: &str = "ces";
pub(crate) const CURRENT_INTERVAL_KEY: &str = "ci";
pub(crate) const EPOCH_EVENT_ID_COUNTER_KEY: &str = "eic";
pub(crate) const INTERVAL_EVENT_ID_COUNTER_KEY: &str = "iic";
pub(crate) const PENDING_EPOCH_EVENTS_NAMESPACE: &str = "pee";
pub(crate) const PENDING_INTERVAL_EVENTS_NAMESPACE: &str = "pie";
pub const REWARDED_SET_KEY: &str = "rs";
pub const CURRENT_EPOCH_STATUS_KEY: &str = "ces";
pub const CURRENT_INTERVAL_KEY: &str = "ci";
pub const EPOCH_EVENT_ID_COUNTER_KEY: &str = "eic";
pub const INTERVAL_EVENT_ID_COUNTER_KEY: &str = "iic";
pub const PENDING_EPOCH_EVENTS_NAMESPACE: &str = "pee";
pub const PENDING_INTERVAL_EVENTS_NAMESPACE: &str = "pie";
pub(crate) const LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
pub(crate) const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
pub const LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
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(crate) const NODE_ID_COUNTER_KEY: &str = "nic";
pub(crate) const MIXNODES_PK_NAMESPACE: &str = "mnn";
pub(crate) const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno";
pub(crate) const MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "mni";
pub(crate) const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns";
pub const LAYER_DISTRIBUTION_KEY: &str = "layers";
pub const NODE_ID_COUNTER_KEY: &str = "nic";
pub const MIXNODES_PK_NAMESPACE: &str = "mnn";
pub const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno";
pub const MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "mni";
pub const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns";
pub(crate) const UNBONDED_MIXNODES_PK_NAMESPACE: &str = "ubm";
pub(crate) const UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE: &str = "umo";
pub(crate) const UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "umi";
pub const UNBONDED_MIXNODES_PK_NAMESPACE: &str = "ubm";
pub const UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE: &str = "umo";
pub const UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "umi";
pub(crate) const REWARDING_PARAMS_KEY: &str = "rparams";
pub(crate) const PENDING_REWARD_POOL_KEY: &str = "prp";
pub(crate) const MIXNODES_REWARDING_PK_NAMESPACE: &str = "mnr";
pub const REWARDING_PARAMS_KEY: &str = "rparams";
pub const PENDING_REWARD_POOL_KEY: &str = "prp";
pub const MIXNODES_REWARDING_PK_NAMESPACE: &str = "mnr";
pub(crate) const FAMILIES_INDEX_NAMESPACE: &str = "faml2";
pub(crate) const FAMILIES_MAP_NAMESPACE: &str = "fam2";
pub(crate) const MEMBERS_MAP_NAMESPACE: &str = "memb2";
pub const FAMILIES_INDEX_NAMESPACE: &str = "faml2";
pub const FAMILIES_MAP_NAMESPACE: &str = "fam2";
pub const MEMBERS_MAP_NAMESPACE: &str = "memb2";
pub const SIGNING_NONCES_NAMESPACE: &str = "sn";
+3
View File
@@ -581,6 +581,9 @@ pub fn query(
QueryMsg::GetNumberOfPendingEvents {} => to_binary(
&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?)
+62 -50
View File
@@ -305,71 +305,77 @@ mod test {
use crate::families::queries::{get_family_by_head, get_family_by_label};
use crate::families::storage::is_family_member;
use crate::mixnet_contract_settings::storage::minimum_mixnode_pledge;
use crate::support::tests::{fixtures, test_helpers};
use cosmwasm_std::testing::{mock_env, mock_info};
use crate::support::tests::fixtures;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
#[test]
fn test_family_crud() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
let env = test.env();
let head = "alice";
let malicious_head = "timmy";
let member = "bob";
let minimum_pledge = minimum_mixnode_pledge(deps.as_ref().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 minimum_pledge = minimum_mixnode_pledge(test.deps().storage).unwrap();
let cost_params = fixtures::mix_node_cost_params_fixture();
let member = "bob";
let (member_mixnode, member_sig, _) =
test_helpers::mixnode_with_signature(&mut rng, member);
let (head_mixnode, head_bond_sig, head_keypair) = test.mixnode_with_signature(head, None);
let (malicious_mixnode, malicious_bond_sig, malicious_keypair) =
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(
deps.as_mut(),
test.deps_mut(),
env.clone(),
mock_info(head, &[minimum_pledge.clone()]),
head_mixnode.clone(),
cost_params.clone(),
head_sig.clone(),
head_bond_sig,
)
.unwrap();
crate::mixnodes::transactions::try_add_mixnode(
deps.as_mut(),
test.deps_mut(),
env.clone(),
mock_info(malicious_head, &[minimum_pledge.clone()]),
malicious_mixnode,
cost_params.clone(),
malicious_sig.clone(),
malicious_bond_sig,
)
.unwrap();
crate::mixnodes::transactions::try_add_mixnode(
deps.as_mut(),
test.deps_mut(),
env,
mock_info(member, &[minimum_pledge]),
member_mixnode.clone(),
cost_params,
member_sig.clone(),
member_bond_sig,
)
.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);
assert!(get_family(&family_head, &deps.storage).is_ok());
assert!(get_family(&family_head, test.deps().storage).is_ok());
let nope = try_create_family(
deps.as_mut(),
test.deps_mut(),
mock_info(malicious_head, &[]),
malicious_sig,
old_style_malicious_head_sig,
"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_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());
let join_signature = head_keypair
@@ -394,41 +400,47 @@ mod test {
.to_base58_string();
try_join_family(
deps.as_mut(),
test.deps_mut(),
mock_info(member, &[]),
Some(member_sig.clone()),
Some(old_style_member_sig.clone()),
join_signature.clone(),
head_mixnode.identity_key.clone(),
)
.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(
deps.as_mut(),
test.deps_mut(),
mock_info(member, &[]),
member_sig.clone(),
old_style_member_sig.clone(),
head_mixnode.identity_key.clone(),
)
.unwrap();
let family = get_family(&family_head, &deps.storage).unwrap();
assert!(!is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
let family = get_family(&family_head, test.deps().storage).unwrap();
assert!(
!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
try_join_family(
deps.as_mut(),
test.deps_mut(),
mock_info(member, &[]),
Some(member_sig.clone()),
join_signature.clone(),
head_mixnode.identity_key.clone(),
Some(old_style_member_sig),
join_signature,
head_mixnode.identity_key,
)
.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(
// deps.as_mut(),
@@ -438,8 +450,8 @@ mod test {
// )
// .unwrap();
// let family = get_family(&family_head, &deps.storage).unwrap();
// assert!(!is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
// let family = get_family(&family_head, test.deps().storage).unwrap();
// assert!(!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap());
}
#[cfg(test)]
@@ -456,7 +468,7 @@ mod test {
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 res = try_create_family_on_behalf(
@@ -495,7 +507,7 @@ mod test {
let new_member = "vin-diesel";
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
let join_signature = head_keys
@@ -542,7 +554,7 @@ mod test {
let new_member = "vin-diesel";
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
let join_signature = head_keys
@@ -599,7 +611,7 @@ mod test {
let new_member = "vin-diesel";
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
let join_signature = head_keys
@@ -613,7 +625,7 @@ mod test {
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
new_member.to_string(),
Some(member_sig.clone()),
Some(member_sig),
join_signature,
head_identity,
)
+1
View File
@@ -2,5 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
pub mod queries;
pub mod signature_helpers;
pub mod storage;
pub mod transactions;
+27 -46
View File
@@ -62,6 +62,7 @@ pub(crate) mod tests {
use crate::contract::execute;
use crate::support::tests;
use crate::support::tests::test_helpers;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::{mock_env, mock_info};
#[test]
@@ -73,27 +74,22 @@ pub(crate) mod tests {
#[test]
fn gateways_paged_retrieval_obeys_limits() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
test.add_dummy_gateways(1000);
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);
}
#[test]
fn gateways_paged_retrieval_has_default_limit() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
test_helpers::add_dummy_gateways(&mut rng, deps.as_mut(), env, 1000);
let mut test = TestSetup::new();
test.add_dummy_gateways(1000);
// 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!(
GATEWAY_BOND_DEFAULT_RETRIEVAL_LIMIT,
@@ -103,15 +99,12 @@ pub(crate) mod tests {
#[test]
fn gateways_paged_retrieval_has_max_limit() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
test_helpers::add_dummy_gateways(&mut rng, deps.as_mut(), env, 1000);
let mut test = TestSetup::new();
test.add_dummy_gateways(1000);
// query with a crazily high limit in an attempt to use too many resources
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
let expected_limit = GATEWAY_BOND_MAX_RETRIEVAL_LIMIT;
@@ -121,25 +114,27 @@ pub(crate) mod tests {
#[test]
fn gateway_pagination_works() {
let mut deps = test_helpers::init_contract();
let _env = mock_env();
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
// (because we query them in an ascended manner)
let mut exec_data = (0..4)
.map(|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))
})
.collect::<Vec<_>>();
exec_data.sort_by(|(_, (_, id1)), (_, (_, id2))| id1.cmp(id2));
let (messages, sender_identities): (Vec<_>, Vec<_>) = exec_data.into_iter().unzip();
let info = mock_info(
&sender_identities[0].0.clone(),
&tests::fixtures::good_gateway_pledge(),
);
let info = mock_info(&sender_identities[0].0.clone(), &stake);
execute(deps.as_mut(), mock_env(), info, messages[0].clone()).unwrap();
let per_page = 2;
@@ -200,43 +195,29 @@ pub(crate) mod tests {
#[test]
fn query_for_gateway_owner_works() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
// "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());
// mixnode was added to "bob", "fred" still does not own one
test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env.clone(),
"bob",
tests::fixtures::good_gateway_pledge(),
);
// gateway was added to "bob", "fred" still does not own one
test.add_dummy_gateway("bob", None);
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());
// "fred" now owns a gateway!
test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env,
"fred",
tests::fixtures::good_gateway_pledge(),
);
test.add_dummy_gateway("fred", None);
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());
// 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();
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());
}
}
@@ -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)
}
}
+129 -76
View File
@@ -2,15 +2,17 @@
// SPDX-License-Identifier: Apache-2.0
use super::storage;
use crate::gateways::signature_helpers::verify_gateway_bonding_signature;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::signing::storage as signing_storage;
use crate::support::helpers::{
ensure_no_existing_bond, ensure_sent_by_vesting_contract, validate_node_identity_signature,
validate_pledge,
ensure_no_existing_bond, ensure_sent_by_vesting_contract, validate_pledge,
};
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
use mixnet_contract_common::{Gateway, GatewayBond};
use nym_contracts_common::signing::MessageSignature;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
pub fn try_add_gateway(
@@ -18,7 +20,7 @@ pub fn try_add_gateway(
env: Env,
info: MessageInfo,
gateway: Gateway,
owner_signature: String,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
_try_add_gateway(
deps,
@@ -37,7 +39,7 @@ pub fn try_add_gateway_on_behalf(
info: MessageInfo,
gateway: Gateway,
owner: String,
owner_signature: String,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
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(
deps: DepsMut<'_>,
env: Env,
gateway: Gateway,
pledge: Vec<Coin>,
owner: Addr,
owner_signature: String,
owner_signature: MessageSignature,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// 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
validate_node_identity_signature(
verify_gateway_bonding_signature(
deps.as_ref(),
&owner,
&owner_signature,
&gateway.identity_key,
owner.clone(),
proxy.clone(),
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 bond = GatewayBond::new(
pledge.clone(),
@@ -182,39 +191,41 @@ pub(crate) fn _try_remove_gateway(
#[cfg(test)]
pub mod tests {
use super::*;
use crate::contract::execute;
use crate::gateways::queries;
use crate::gateways::transactions::{
try_add_gateway, try_add_gateway_on_behalf, try_remove_gateway_on_behalf,
};
use crate::interval::pending_events;
use crate::mixnet_contract_settings::storage::minimum_gateway_pledge;
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::{fixtures, test_helpers};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coin, Addr, BankMsg, Response, Uint128};
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{Addr, BankMsg, Response, Uint128};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::new_gateway_unbonding_event;
use mixnet_contract_common::ExecuteMsg;
#[test]
fn gateway_add() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
// if we fail validation (by say not sending enough funds
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();
insufficient_pledge.amount -= Uint128::new(1000);
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(
deps.as_mut(),
test.deps_mut(),
env.clone(),
info,
gateway.clone(),
@@ -233,47 +244,23 @@ pub mod tests {
// if the signature provided is invalid, the bonding also fails
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
test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env.clone(),
sender,
fixtures::good_gateway_pledge(),
);
test.add_dummy_gateway(sender, None);
// 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);
// the same holds if the user already owns a mixnode
let sender2 = "mixnode-owner";
let mix_id = test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
sender2,
vec![coin(100_000_000, TEST_COIN_DENOM)],
);
let mix_id = test.add_dummy_mixnode(sender2, None);
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(
deps.as_mut(),
test.deps_mut(),
env.clone(),
info.clone(),
gateway.clone(),
@@ -282,12 +269,84 @@ pub mod tests {
assert_eq!(Err(MixnetContractError::AlreadyOwnsMixnode), result);
// 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());
}
#[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]
fn gateway_add_with_illegal_proxy() {
let mut test = TestSetup::new();
@@ -297,7 +356,7 @@ pub mod tests {
let vesting_contract = test.vesting_contract();
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(
test.deps_mut(),
@@ -320,14 +379,13 @@ pub mod tests {
#[test]
fn gateway_remove() {
let mut deps = test_helpers::init_contract();
let mut rng = test_helpers::test_rng();
let env = mock_env();
let mut test = TestSetup::new();
let env = test.env();
// try unbond when no nodes exist yet
let info = mock_info("anyone", &[]);
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
assert_eq!(
@@ -338,18 +396,12 @@ pub mod tests {
);
// let's add a node owned by bob
test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env.clone(),
"bob",
fixtures::good_gateway_pledge(),
);
test.add_dummy_gateway("bob", None);
// attempt to unbond fred's node, which doesn't exist
let info = mock_info("fred", &[]);
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!(
result,
Err(MixnetContractError::NoAssociatedGatewayBond {
@@ -358,28 +410,27 @@ pub mod tests {
);
// 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());
let first_node = &nodes[0];
assert_eq!(&Addr::unchecked("bob"), first_node.owner());
// add a node owned by fred
let fred_identity = test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env,
"fred",
tests::fixtures::good_gateway_pledge(),
);
let fred_identity = test.add_dummy_gateway("fred", None);
// 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
let info = mock_info("fred", &[]);
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
let expected_message = BankMsg::Send {
@@ -401,9 +452,11 @@ pub mod tests {
assert_eq!(expected_response, remove_fred);
// only 1 node now exists, owned by bob:
let gateway_bonds = tests::queries::get_gateways(&mut deps);
assert_eq!(1, gateway_bonds.len());
assert_eq!(&Addr::unchecked("bob"), gateway_bonds[0].owner());
let nodes = queries::query_gateways_paged(test.deps(), None, None)
.unwrap()
.nodes;
assert_eq!(1, nodes.len());
assert_eq!(&Addr::unchecked("bob"), nodes[0].owner());
}
#[test]
+1
View File
@@ -14,6 +14,7 @@ mod mixnet_contract_settings;
mod mixnodes;
mod queued_migrations;
mod rewards;
mod signing;
mod support;
#[cfg(feature = "contract-testing")]
+1
View File
@@ -3,5 +3,6 @@
pub mod helpers;
pub mod queries;
pub mod signature_helpers;
pub mod storage;
pub mod transactions;
+71 -147
View File
@@ -273,30 +273,24 @@ pub(crate) mod tests {
#[cfg(test)]
mod mixnode_bonds {
use super::*;
use crate::support::tests::fixtures::good_mixnode_pledge;
#[test]
fn obeys_limits() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
test.add_dummy_mixnodes(1000);
let limit = 2;
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(limit)).unwrap();
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(limit)).unwrap();
assert_eq!(limit, page1.nodes.len() as u32);
}
#[test]
fn has_default_limit() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
let mut test = TestSetup::new();
test.add_dummy_mixnodes(1000);
// 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!(
MIXNODE_BOND_DEFAULT_RETRIEVAL_LIMIT,
@@ -306,14 +300,12 @@ pub(crate) mod tests {
#[test]
fn has_max_limit() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
let mut test = TestSetup::new();
test.add_dummy_mixnodes(1000);
// query with a crazily high limit in an attempt to use too many resources
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
assert_eq!(MIXNODE_BOND_MAX_RETRIEVAL_LIMIT, page1.nodes.len() as u32);
@@ -322,63 +314,43 @@ pub(crate) mod tests {
#[test]
fn pagination_works() {
// 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 env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr1",
good_mixnode_pledge(),
);
test.add_dummy_mixnode("addr1", None);
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
assert_eq!(1, page1.nodes.len());
// save another
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr2",
good_mixnode_pledge(),
);
test.add_dummy_mixnode("addr2", None);
// 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());
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr3",
good_mixnode_pledge(),
);
test.add_dummy_mixnode("addr3", None);
// page1 still has the same 2 results
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!(page1, another_page1);
// retrieving the next page should start after the last key on this page
let start_after = page1.start_next_after.unwrap();
let page2 = query_mixnode_bonds_paged(deps.as_ref(), Some(start_after), Some(per_page))
.unwrap();
let page2 =
query_mixnode_bonds_paged(test.deps(), Some(start_after), Some(per_page)).unwrap();
assert_eq!(1, page2.nodes.len());
// 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))
.unwrap();
let page2 =
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
assert_eq!(2, page2.nodes.len());
@@ -388,29 +360,24 @@ pub(crate) mod tests {
#[cfg(test)]
mod mixnode_details {
use super::*;
use crate::support::tests::fixtures::good_mixnode_pledge;
#[test]
fn obeys_limits() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
test.add_dummy_mixnodes(1000);
let limit = 2;
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, Some(limit)).unwrap();
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(limit)).unwrap();
assert_eq!(limit, page1.nodes.len() as u32);
}
#[test]
fn has_default_limit() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
let mut test = TestSetup::new();
test.add_dummy_mixnodes(1000);
// 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!(
MIXNODE_DETAILS_DEFAULT_RETRIEVAL_LIMIT,
@@ -420,15 +387,12 @@ pub(crate) mod tests {
#[test]
fn has_max_limit() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
let mut test = TestSetup::new();
test.add_dummy_mixnodes(1000);
// query with a crazily high limit in an attempt to use too many resources
let crazy_limit = 1000;
let page1 =
query_mixnodes_details_paged(deps.as_ref(), None, Some(crazy_limit)).unwrap();
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(crazy_limit)).unwrap();
// we default to a decent sized upper bound instead
assert_eq!(
@@ -440,64 +404,44 @@ pub(crate) mod tests {
#[test]
fn pagination_works() {
// 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 env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr1",
good_mixnode_pledge(),
);
test.add_dummy_mixnode("addr1", None);
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
assert_eq!(1, page1.nodes.len());
// save another
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr2",
good_mixnode_pledge(),
);
test.add_dummy_mixnode("addr2", None);
// 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());
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr3",
good_mixnode_pledge(),
);
test.add_dummy_mixnode("addr3", None);
// page1 still has the same 2 results
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!(page1, another_page1);
// retrieving the next page should start after the last key on this page
let start_after = page1.start_next_after.unwrap();
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();
assert_eq!(1, page2.nodes.len());
// 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_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();
// now we have 2 pages, with 2 results on the second page
@@ -1135,26 +1079,18 @@ pub(crate) mod tests {
#[test]
fn query_for_owned_mixnode() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
let address = "mix-owner".to_string();
// 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_eq!(address, res.address);
// when it [fully] exists
let id = test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env,
&address,
good_mixnode_pledge(),
);
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
let id = test.add_dummy_mixnode(&address, None);
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
let details = res.mixnode_details.unwrap();
assert_eq!(address, details.bond_information.owner);
assert_eq!(
@@ -1171,30 +1107,27 @@ pub(crate) mod tests {
rewarding_details.delegates = Decimal::raw(12345);
rewarding_details.unique_delegations = 1;
rewards_storage::MIXNODE_REWARDING
.save(deps.as_mut().storage, id, &rewarding_details)
.save(test.deps_mut().storage, id, &rewarding_details)
.unwrap();
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), 123, id).unwrap();
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, id).unwrap();
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
assert!(res.mixnode_details.is_none());
assert_eq!(address, res.address);
}
#[test]
fn query_for_mixnode_details() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
// 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_eq!(42, res.mix_id);
// it exists
let mix_id =
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "foomp", good_mixnode_pledge());
let res = query_mixnode_details(deps.as_ref(), mix_id).unwrap();
let mix_id = test.add_dummy_mixnode("foomp", None);
let res = query_mixnode_details(test.deps(), mix_id).unwrap();
let details = res.mixnode_details.unwrap();
assert_eq!(mix_id, details.bond_information.mix_id);
assert_eq!(
@@ -1227,18 +1160,15 @@ pub(crate) mod tests {
#[test]
fn query_for_mixnode_rewarding_details() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
// 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_eq!(42, res.mix_id);
let mix_id =
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "foomp", good_mixnode_pledge());
let res = query_mixnode_rewarding_details(deps.as_ref(), mix_id).unwrap();
let mix_id = test.add_dummy_mixnode("foomp", None);
let res = query_mixnode_rewarding_details(test.deps(), mix_id).unwrap();
let details = res.rewarding_details.unwrap();
assert_eq!(
fixtures::mix_node_cost_params_fixture(),
@@ -1249,80 +1179,74 @@ pub(crate) mod tests {
#[test]
fn query_for_unbonded_mixnode() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
let sender = "mix-owner";
// 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_eq!(42, res.mix_id);
// add and unbond the mixnode
let mix_id =
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, sender, good_mixnode_pledge());
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), 123, mix_id).unwrap();
let mix_id = test.add_dummy_mixnode(sender, None);
pending_events::unbond_mixnode(test.deps_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!(mix_id, res.mix_id);
}
#[test]
fn query_for_stake_saturation() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
// 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.uncapped_saturation.is_none());
assert_eq!(42, res.mix_id);
let rewarding_params = rewards_storage::REWARDING_PARAMS
.load(deps.as_ref().storage)
.load(test.deps().storage)
.unwrap();
let saturation_point = rewarding_params.interval.stake_saturation_point;
let mix_id =
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "foomp", good_mixnode_pledge());
let mix_id = test.add_dummy_mixnode("foomp", None);
// below saturation point
// there's only the base pledge without any delegation
let expected =
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.uncapped_saturation.unwrap());
assert_eq!(mix_id, res.mix_id);
// exactly at saturation point
let mut mix_rewarding = rewards_storage::MIXNODE_REWARDING
.load(deps.as_ref().storage, mix_id)
.load(test.deps().storage, mix_id)
.unwrap();
mix_rewarding.operator = saturation_point;
rewards_storage::MIXNODE_REWARDING
.save(deps.as_mut().storage, mix_id, &mix_rewarding)
.save(test.deps_mut().storage, mix_id, &mix_rewarding)
.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.uncapped_saturation.unwrap());
assert_eq!(mix_id, res.mix_id);
// above the saturation point
let mut mix_rewarding = rewards_storage::MIXNODE_REWARDING
.load(deps.as_ref().storage, mix_id)
.load(test.deps().storage, mix_id)
.unwrap();
mix_rewarding.delegates = mix_rewarding.operator * Decimal::percent(150);
rewards_storage::MIXNODE_REWARDING
.save(deps.as_mut().storage, mix_id, &mix_rewarding)
.save(test.deps_mut().storage, mix_id, &mix_rewarding)
.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::percent(250), res.uncapped_saturation.unwrap());
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)
}
}
+142 -82
View File
@@ -9,10 +9,11 @@ use crate::mixnet_contract_settings::storage::rewarding_denom;
use crate::mixnodes::helpers::{
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::{
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,
validate_pledge,
ensure_proxy_match, ensure_sent_by_vesting_contract, validate_pledge,
};
use cosmwasm_std::{coin, Addr, Coin, DepsMut, Env, MessageInfo, Response, Storage};
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::pending_events::{PendingEpochEventKind, PendingIntervalEventKind};
use mixnet_contract_common::{Layer, MixId, MixNode};
use nym_contracts_common::signing::MessageSignature;
pub(crate) fn update_mixnode_layer(
mix_id: MixId,
@@ -61,7 +63,7 @@ pub fn try_add_mixnode(
info: MessageInfo,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
_try_add_mixnode(
deps,
@@ -82,7 +84,7 @@ pub fn try_add_mixnode_on_behalf(
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner: String,
owner_signature: String,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
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
//
// 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)]
fn _try_add_mixnode(
deps: DepsMut<'_>,
@@ -109,7 +114,7 @@ fn _try_add_mixnode(
cost_params: MixNodeCostParams,
pledge: Vec<Coin>,
owner: Addr,
owner_signature: String,
owner_signature: MessageSignature,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// 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.
// check if this sender actually owns the mixnode by checking the signature
validate_node_identity_signature(
verify_mixnode_bonding_signature(
deps.as_ref(),
&owner,
&owner_signature,
&mixnode.identity_key,
owner.clone(),
proxy.clone(),
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_id, layer) = save_new_mixnode(
deps.storage,
@@ -393,7 +404,7 @@ pub mod tests {
use crate::support::tests::fixtures::{good_mixnode_pledge, TEST_COIN_DENOM};
use crate::support::tests::test_helpers::TestSetup;
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 mixnet_contract_common::{
EpochState, EpochStatus, ExecuteMsg, Layer, LayerDistribution, Percent,
@@ -401,23 +412,23 @@ pub mod tests {
#[test]
fn mixnode_add() {
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
let env = test.env();
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();
insufficient_pledge.amount -= Uint128::new(1000);
// if we don't send enough funds
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();
// we are informed that we didn't send enough funds
let result = try_add_mixnode(
deps.as_mut(),
test.deps_mut(),
env.clone(),
info,
mixnode.clone(),
@@ -435,31 +446,12 @@ pub mod tests {
// if the signature provided is invalid, the bonding also fails
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
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
sender,
fixtures::good_mixnode_pledge(),
);
test.add_dummy_mixnode(sender, None);
// it fails
let result = try_add_mixnode(
deps.as_mut(),
test.deps_mut(),
env.clone(),
info,
mixnode,
@@ -471,19 +463,13 @@ pub mod tests {
// the same holds if the user already owns a gateway
let sender2 = "gateway-owner";
test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env.clone(),
sender2,
tests::fixtures::good_gateway_pledge(),
);
test.add_dummy_gateway(sender2, None);
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(
deps.as_mut(),
test.deps_mut(),
env.clone(),
info.clone(),
mixnode.clone(),
@@ -494,14 +480,14 @@ pub mod tests {
// but after he unbonds it, it's all fine again
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());
// 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))
.unwrap();
let bond =
must_get_mixnode_bond_by_owner(test.deps().storage, &Addr::unchecked(sender2)).unwrap();
assert_eq!(2, bond.mix_id);
// and make sure we're on layer 2 (because it was the next empty one)
@@ -513,10 +499,84 @@ pub mod tests {
layer2: 1,
layer3: 0,
};
assert_eq!(
expected,
storage::LAYERS.load(deps.as_ref().storage).unwrap()
)
assert_eq!(expected, storage::LAYERS.load(test.deps().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]
@@ -528,10 +588,9 @@ pub mod tests {
let vesting_contract = test.vesting_contract();
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();
// we are informed that we didn't send enough funds
let res = try_add_mixnode_on_behalf(
test.deps_mut(),
env,
@@ -920,52 +979,53 @@ pub mod tests {
#[test]
fn adding_mixnode_with_duplicate_sphinx_key_errors_out() {
let mut deps = test_helpers::init_contract();
let mut rng = test_helpers::test_rng();
let mut test = TestSetup::new();
let env = test.env();
let keypair1 = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
let keypair2 = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
let sig1 = keypair1.private_key().sign_text("alice");
let sig2 = keypair1.private_key().sign_text("bob");
let keypair1 = nym_crypto::asymmetric::identity::KeyPair::new(&mut test.rng);
let keypair2 = nym_crypto::asymmetric::identity::KeyPair::new(&mut test.rng);
let info_alice = mock_info("alice", &tests::fixtures::good_mixnode_pledge());
let info_bob = mock_info("bob", &tests::fixtures::good_mixnode_pledge());
let mut mixnode = MixNode {
let cost_params = fixtures::mix_node_cost_params_fixture();
let mixnode1 = MixNode {
host: "1.2.3.4".to_string(),
mix_port: 1234,
verloc_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()
.to_base58_string(),
identity_key: keypair1.public_key().to_base58_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(
deps.as_mut(),
mock_env(),
test.deps_mut(),
env.clone(),
info_alice,
mixnode.clone(),
mixnode1,
cost_params.clone(),
sig1
)
.is_ok());
mixnode.identity_key = keypair2.public_key().to_base58_string();
// change identity but reuse sphinx key
assert!(try_add_mixnode(
deps.as_mut(),
mock_env(),
info_bob,
mixnode,
cost_params,
sig2
)
.is_err());
assert!(
try_add_mixnode(test.deps_mut(), env, info_bob, mixnode2, cost_params, sig2).is_err()
);
}
#[cfg(test)]
+5
View File
@@ -0,0 +1,5 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod queries;
pub mod storage;
+11
View File
@@ -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)
}
+30
View File
@@ -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)
}
+3 -3
View File
@@ -377,7 +377,7 @@ pub fn validate_node_identity_signature(
signature: &str,
identity: IdentityKeyRef<'_>,
) -> Result<(), MixnetContractError> {
validate_signature(deps, owner.as_bytes(), signature, identity)
validate_ed25519_signature(deps, owner.as_bytes(), signature, identity)
}
pub fn validate_family_signature(
@@ -386,10 +386,10 @@ pub fn validate_family_signature(
signature: &str,
family_head: IdentityKeyRef<'_>,
) -> 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<'_>,
signed_bytes: &[u8],
signature: &str,
+18 -9
View File
@@ -1,25 +1,34 @@
use cosmwasm_std::{Coin, Deps};
use mixnet_contract_common::{ExecuteMsg, Gateway, IdentityKey};
use nym_crypto::asymmetric::identity;
use rand_chacha::rand_core::{CryptoRng, RngCore};
use crate::support::tests;
use crate::support::tests::test_helpers::{ed25519_sign_message, gateway_bonding_sign_payload};
pub(crate) fn valid_bond_gateway_msg(
mut rng: impl RngCore + CryptoRng,
deps: Deps<'_>,
stake: Vec<Coin>,
sender: &str,
) -> (ExecuteMsg, IdentityKey) {
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
let owner_signature = keypair
.private_key()
.sign(sender.as_bytes())
.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 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();
(
ExecuteMsg::BondGateway {
gateway: Gateway {
identity_key: identity_key.clone(),
..tests::fixtures::gateway_fixture()
},
gateway,
owner_signature,
},
identity_key,
+290 -189
View File
@@ -5,8 +5,6 @@
pub mod fixtures;
#[cfg(test)]
pub mod messages;
#[cfg(test)]
pub mod queries;
#[cfg(test)]
pub mod test_helpers {
@@ -24,7 +22,8 @@ pub mod test_helpers {
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::{
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::mixnode_bonds;
@@ -36,8 +35,11 @@ pub mod test_helpers {
};
use crate::rewards::storage as rewards_storage;
use crate::rewards::transactions::try_reward_mixnode;
use crate::signing::storage as signing_storage;
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_env;
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::RewardDistribution;
use mixnet_contract_common::{
Delegation, EpochState, EpochStatus, Gateway, IdentityKey, InitialRewardingParams,
InstantiateMsg, Interval, MixId, MixNode, MixNodeBond, Percent, RewardedSetNodeStatus,
Delegation, EpochState, EpochStatus, Gateway, GatewayBondingPayload, IdentityKey,
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::KeyPair;
use rand_chacha::rand_core::{CryptoRng, RngCore, SeedableRng};
use rand_chacha::ChaCha20Rng;
use serde::Serialize;
use std::time::Duration;
pub fn assert_eq_with_leeway(a: Uint128, b: Uint128, leeway: Uint128) {
@@ -190,7 +198,7 @@ pub mod test_helpers {
head: &str,
label: &str,
) -> (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);
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 {
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 {
Some(amount) => {
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(),
};
let env = self.env();
add_mixnode(&mut self.rng, self.deps.as_mut(), env, owner, vec![stake])
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,
owner: &str,
stake: Option<Uint128>,
) -> (MixId, identity::KeyPair) {
let stake = match 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 stake = self.make_mix_pledge(stake);
let proxy = self.vesting_contract();
let keypair = identity::KeyPair::new(&mut self.rng);
let owner_signature = keypair
.private_key()
.sign(owner.as_bytes())
.to_base58_string();
let identity_key = keypair.public_key().to_base58_string();
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
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 key = keypair.public_key().to_base58_string();
let msg = mixnode_bonding_sign_payload(
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
.may_load(self.deps().storage)
.unwrap()
@@ -245,11 +329,7 @@ pub mod test_helpers {
self.deps_mut(),
env,
info,
MixNode {
identity_key: key,
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
..tests::fixtures::mix_node_fixture()
},
mixnode,
tests::fixtures::mix_node_cost_params_fixture(),
owner.to_string(),
owner_signature,
@@ -265,7 +345,8 @@ pub mod test_helpers {
owner: &str,
stake: Option<Uint128>,
) -> 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) {
@@ -312,14 +393,26 @@ pub mod test_helpers {
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 legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut self.rng);
let owner_signature = keypair
.private_key()
.sign(owner.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(
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 info = mock_info(proxy.as_ref(), &[stake]);
@@ -328,11 +421,7 @@ pub mod test_helpers {
self.deps_mut(),
env,
info,
Gateway {
identity_key: keypair.public_key().to_base58_string(),
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
..tests::fixtures::gateway_fixture()
},
gateway,
owner.to_string(),
owner_signature,
)
@@ -351,6 +440,63 @@ pub mod test_helpers {
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) {
let bond_details = mixnodes_storage::mixnode_bonds()
.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 {
let mix_rewarding = rewards_storage::MIXNODE_REWARDING
.load(deps.storage, node)
@@ -800,54 +962,54 @@ pub mod test_helpers {
perform_pending_interval_actions(deps.branch(), &env, None).unwrap();
}
pub fn mixnode_with_signature(
mut rng: impl RngCore + CryptoRng,
sender: &str,
) -> (MixNode, String, KeyPair) {
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
let owner_signature = keypair
.private_key()
.sign(sender.as_bytes())
.to_base58_string();
// pub fn mixnode_with_signature(
// mut rng: impl RngCore + CryptoRng,
// deps: Deps<'_>,
// sender: &str,
// stake: Option<Vec<Coin>>,
// ) -> (MixNode, MessageSignature, KeyPair) {
// // hehe stupid workaround for bypassing the immutable borrow and removing duplicate code
//
// let stake = stake.unwrap_or(good_mixnode_pledge());
//
// 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();
let sphinx_key = legit_sphinx_key.public_key().to_base58_string();
(
MixNode {
identity_key,
sphinx_key,
..tests::fixtures::mix_node_fixture()
},
owner_signature,
keypair,
)
}
pub fn gateway_with_signature(
mut rng: impl RngCore + CryptoRng,
sender: &str,
) -> (Gateway, String) {
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
let owner_signature = keypair
.private_key()
.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 gateway_with_signature(
// mut rng: impl RngCore + CryptoRng,
// deps: Deps<'_>,
// sender: &str,
// stake: Option<Vec<Coin>>,
// ) -> (Gateway, MessageSignature) {
// let stake = stake.unwrap_or(good_gateway_pledge());
//
// 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 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.clone());
// let owner_signature = ed25519_sign_message(msg, keypair.private_key());
//
// (gateway, owner_signature)
// }
pub fn add_dummy_delegations(mut deps: DepsMut<'_>, env: Env, mix_id: MixId, n: usize) {
for i in 0..n {
@@ -864,39 +1026,22 @@ pub mod test_helpers {
}
}
pub fn add_dummy_mixnodes(
mut rng: impl RngCore + CryptoRng,
mut deps: DepsMut<'_>,
env: Env,
n: usize,
) {
for i in 0..n {
add_mixnode(
&mut rng,
deps.branch(),
env.clone(),
&format!("owner{}", i),
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_mixnodes(
// mut rng: impl RngCore + CryptoRng,
// mut deps: DepsMut<'_>,
// env: Env,
// n: usize,
// ) {
// for i in 0..n {
// add_mixnode(
// &mut rng,
// deps.branch(),
// env.clone(),
// &format!("owner{}", i),
// tests::fixtures::good_mixnode_pledge(),
// );
// }
// }
pub fn add_dummy_unbonded_mixnodes(
mut rng: impl RngCore + CryptoRng,
@@ -968,79 +1113,35 @@ pub mod test_helpers {
id
}
// note to whoever wants to refactor this function, you dont want to grab rng here directly
// via `let rng = test_rng()`
// because it's extremely likely you might end up calling `add_mixnode()` multiple times
// in the same test and thus you're going to get mixnodes with the same keys and that's
// not what you want (presumably)
pub fn add_mixnode(
mut rng: impl RngCore + CryptoRng,
deps: DepsMut<'_>,
env: Env,
sender: &str,
pub fn mixnode_bonding_sign_payload(
deps: Deps<'_>,
owner: &str,
proxy: Option<Addr>,
mixnode: MixNode,
stake: Vec<Coin>,
) -> MixId {
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
let owner_signature = keypair
.private_key()
.sign(sender.as_bytes())
.to_base58_string();
) -> SignableMixNodeBondingMsg {
let cost_params = tests::fixtures::mix_node_cost_params_fixture();
let nonce =
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
let info = mock_info(sender, &stake);
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
let payload = MixnodeBondingPayload::new(mixnode, cost_params);
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
SignableMixNodeBondingMsg::new(nonce, content)
}
// same note as with `add_mixnode`
pub fn add_gateway(
mut rng: impl RngCore + CryptoRng,
deps: DepsMut<'_>,
env: Env,
sender: &str,
pub fn gateway_bonding_sign_payload(
deps: Deps<'_>,
owner: &str,
proxy: Option<Addr>,
gateway: Gateway,
stake: Vec<Coin>,
) -> String {
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
let owner_signature = keypair
.private_key()
.sign(sender.as_bytes())
.to_base58_string();
) -> SignableGatewayBondingMsg {
let nonce =
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
let info = mock_info(sender, &stake);
let key = keypair.public_key().to_base58_string();
try_add_gateway(
deps,
env,
info,
Gateway {
identity_key: key.clone(),
..tests::fixtures::gateway_fixture()
},
owner_signature,
)
.unwrap();
key
let payload = GatewayBondingPayload::new(gateway);
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
SignableGatewayBondingMsg::new(nonce, content)
}
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
}
+3 -2
View File
@@ -7,6 +7,7 @@ use crate::traits::{
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, NodeFamilies, VestingAccount,
};
use crate::vesting::{populate_vesting_periods, Account};
use contracts_common::signing::MessageSignature;
use contracts_common::ContractBuildInformation;
use cosmwasm_std::{
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].
pub fn try_bond_gateway(
gateway: Gateway,
owner_signature: String,
owner_signature: MessageSignature,
amount: Coin,
info: MessageInfo,
env: Env,
@@ -448,7 +449,7 @@ pub fn try_track_unbond_gateway(
pub fn try_bond_mixnode(
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
amount: Coin,
info: MessageInfo,
env: Env,
@@ -1,4 +1,5 @@
use crate::errors::ContractError;
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{Coin, Env, Response, Storage};
use mixnet_contract_common::{
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
@@ -12,7 +13,7 @@ pub trait MixnodeBondingAccount {
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
env: &Env,
storage: &mut dyn Storage,
@@ -50,7 +51,7 @@ pub trait GatewayBondingAccount {
fn try_bond_gateway(
&self,
gateway: Gateway,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
env: &Env,
storage: &mut dyn Storage,
@@ -2,6 +2,7 @@ use super::PledgeData;
use crate::errors::ContractError;
use crate::storage::MIXNET_CONTRACT_ADDRESS;
use crate::traits::GatewayBondingAccount;
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway};
use vesting_contract_common::events::{
@@ -14,7 +15,7 @@ impl GatewayBondingAccount for Account {
fn try_bond_gateway(
&self,
gateway: Gateway,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
env: &Env,
storage: &mut dyn Storage,
@@ -5,6 +5,7 @@ use super::Account;
use crate::errors::ContractError;
use crate::storage::MIXNET_CONTRACT_ADDRESS;
use crate::traits::MixnodeBondingAccount;
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::mixnode::MixNodeConfigUpdate;
use mixnet_contract_common::mixnode::MixNodeCostParams;
@@ -32,7 +33,7 @@ impl MixnodeBondingAccount for Account {
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
owner_signature: MessageSignature,
pledge: Coin,
env: &Env,
storage: &mut dyn Storage,
+7 -6
View File
@@ -47,6 +47,7 @@ mod tests {
use crate::traits::VestingAccount;
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
use crate::vesting::{populate_vesting_periods, Account};
use contracts_common::signing::MessageSignature;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coin, coins, Addr, Coin, Timestamp, Uint128};
use mixnet_contract_common::mixnode::MixNodeCostParams;
@@ -674,7 +675,7 @@ mod tests {
let err = account.try_bond_mixnode(
mix_node.clone(),
cost_params.clone(),
"alice".to_string(),
MessageSignature::from(vec![1, 2, 3]),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
@@ -687,7 +688,7 @@ mod tests {
let ok = account.try_bond_mixnode(
mix_node.clone(),
cost_params.clone(),
"alice".to_string(),
MessageSignature::from(vec![1, 2, 3]),
Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
@@ -704,7 +705,7 @@ mod tests {
let err = account.try_bond_mixnode(
mix_node,
cost_params,
"alice".to_string(),
MessageSignature::from(vec![1, 2, 3]),
Coin {
amount: Uint128::new(10_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
@@ -738,7 +739,7 @@ mod tests {
// Try delegating too much
let err = account.try_bond_gateway(
gateway.clone(),
"alice".to_string(),
MessageSignature::from(vec![1, 2, 3]),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
@@ -750,7 +751,7 @@ mod tests {
let ok = account.try_bond_gateway(
gateway.clone(),
"alice".to_string(),
MessageSignature::from(vec![1, 2, 3]),
Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
@@ -766,7 +767,7 @@ mod tests {
// Try delegating too much again
let err = account.try_bond_gateway(
gateway,
"alice".to_string(),
MessageSignature::from(vec![1, 2, 3]),
Coin {
amount: Uint128::new(500_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
+1 -1
View File
@@ -17,7 +17,7 @@ rust-version = "1.56"
[dependencies]
anyhow = "1.0.53"
async-trait = { workspace = true }
bip39 = "1.0.1"
bip39 = { workspace = true }
bs58 = "0.4.0"
clap = { version = "4.0", features = ["cargo", "derive"] }
colored = "2.0"
+41 -4
View File
@@ -8,31 +8,37 @@ use crate::{
commands::ensure_config_version_compatibility,
config::persistence::pathfinder::GatewayPathfinder,
};
use anyhow::{anyhow, Result};
use anyhow::{bail, Result};
use clap::{ArgGroup, Args};
use nym_crypto::asymmetric::identity;
use std::error::Error;
use validator_client::nyxd;
#[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 {
/// The id of the mixnode you want to sign with
#[clap(long)]
id: String,
/// 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>,
/// Signs an arbitrary piece of text with your identity key
#[clap(long)]
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 {
Text(String),
Address(nyxd::AccountId),
ContractMsg(String),
}
impl TryFrom<Sign> for SignedTarget {
@@ -43,11 +49,13 @@ impl TryFrom<Sign> for SignedTarget {
Ok(SignedTarget::Text(text))
} else if let Some(address) = args.wallet_address {
Ok(SignedTarget::Address(address))
} else if let Some(msg) = args.contract_msg {
Ok(SignedTarget::ContractMsg(msg))
} else {
// This is unreachable, and hopefully clap will support it explicitly by outputting an
// enum from the ArgGroup in the future.
// 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>> {
let config = build_config(args.id.clone(), OverrideConfig::default())?;
ensure_config_version_compatibility(&config)?;
@@ -99,6 +133,9 @@ pub fn execute(args: Sign) -> Result<(), Box<dyn Error + Send + Sync>> {
match signed_target {
SignedTarget::Text(text) => print_signed_text(identity_keypair.private_key(), &text),
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(())
+1
View File
@@ -291,6 +291,7 @@ impl Config {
&self.gateway.version
}
#[allow(unused)]
pub fn get_wallet_address(&self) -> Option<nyxd::AccountId> {
self.gateway.wallet_address.clone()
}
+1 -22
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use self::storage::PersistentStorage;
use crate::commands::ensure_correct_bech32_prefix;
use crate::config::persistence::pathfinder::GatewayPathfinder;
use crate::config::Config;
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::statistics::collector::GatewayStatisticsCollector;
use crate::node::storage::Storage;
use crate::{commands::sign::load_identity_keys, OutputFormat};
use colored::Colorize;
use crate::OutputFormat;
use log::*;
use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder};
use nym_crypto::asymmetric::{encryption, identity};
@@ -24,7 +22,6 @@ use rand::seq::SliceRandom;
use rand::thread_rng;
use std::error::Error;
use std::net::SocketAddr;
use std::process;
use std::sync::Arc;
use validator_client::Client;
@@ -109,28 +106,10 @@ where
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> {
let node_details = nym_types::gateway::GatewayNodeDetailsResponse {
identity_key: self.identity_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(),
bind_address: self.config.get_listening_address().to_string(),
version: self.config.get_version().to_string(),
+1
View File
@@ -41,6 +41,7 @@ atty = "0.2"
## internal
nym-config = { path="../common/config" }
nym-crypto = { path="../common/crypto" }
nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" }
mixnet-client = { path="../common/client-libs/mixnet-client" }
mixnode-common = { path="../common/mixnode-common" }
nym-nonexhaustive-delayqueue = { path="../common/nonexhaustive-delayqueue" }
+40 -5
View File
@@ -6,7 +6,7 @@ use std::convert::TryFrom;
use crate::commands::validate_bech32_address_or_exit;
use crate::config::{persistence::pathfinder::MixNodePathfinder, Config};
use crate::node::MixNode;
use anyhow::{anyhow, Result};
use anyhow::{bail, Result};
use clap::{ArgGroup, Args};
use log::error;
use nym_config::NymConfig;
@@ -16,7 +16,7 @@ use validator_client::nyxd;
use super::version_check;
#[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 {
/// The id of the mixnode you want to sign with
#[clap(long)]
@@ -30,11 +30,16 @@ pub(crate) struct Sign {
/// Signs an arbitrary piece of text with your identity key
#[clap(long)]
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 {
Text(String),
Address(nyxd::AccountId),
ContractMsg(String),
}
impl TryFrom<Sign> for SignedTarget {
@@ -45,11 +50,13 @@ impl TryFrom<Sign> for SignedTarget {
Ok(SignedTarget::Text(text))
} else if let Some(address) = args.wallet_address {
Ok(SignedTarget::Address(address))
} else if let Some(msg) = args.contract_msg {
Ok(SignedTarget::ContractMsg(msg))
} else {
// This is unreachable, and hopefully clap will support it explicitly by outputting an
// enum from the ArgGroup in the future.
// 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}");
}
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) {
let config = match Config::load_from_file(&args.id) {
Ok(cfg) => cfg,
Err(err) => {
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,
err,
);
return;
}
@@ -101,5 +133,8 @@ pub(crate) fn execute(args: &Sign) {
match signed_target {
SignedTarget::Text(text) => print_signed_text(identity_keypair.private_key(), &text),
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)
}
}
}
-20
View File
@@ -1,7 +1,6 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::validate_bech32_address_or_exit;
use crate::config::persistence::pathfinder::MixNodePathfinder;
use crate::config::Config;
use crate::node::http::{
@@ -18,7 +17,6 @@ use crate::node::node_description::NodeDescription;
use crate::node::node_statistics::SharedNodeStats;
use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSender};
use crate::OutputFormat;
use colored::Colorize;
use log::{error, info, warn};
use mixnode_common::verloc::{self, AtomicVerlocResult, VerlocMeasurer};
use nym_bin_common::version_checker::parse_version;
@@ -84,29 +82,11 @@ impl MixNode {
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
pub(crate) fn print_node_details(&self, output: OutputFormat) {
let node_details = nym_types::mixnode::MixnodeNodeDetailsResponse {
identity_key: self.identity_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(),
bind_address: self.config.get_listening_address().to_string(),
version: self.config.get_version().to_string(),
+1 -1
View File
@@ -17,7 +17,7 @@ rust-version = "1.56"
[dependencies]
async-trait = { workspace = true }
bs58 = {version = "0.4.0" }
bip39 = "1"
bip39 = { workspace = true }
cfg-if = "1.0"
clap = { version = "4.0", features = ["cargo", "derive"] }
console-subscriber = { version = "0.1.1", optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable"
+68 -49
View File
@@ -84,6 +84,18 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
})
}
async fn dump_persistent_state(&self) {
if !self.state.coconut_keypair_is_some().await {
// Delete the files just in case the process is killed before the new keys are generated
std::fs::remove_file(&self.secret_key_path).ok();
std::fs::remove_file(&self.verification_key_path).ok();
}
let persistent_state = PersistentState::from(&self.state);
if let Err(err) = persistent_state.save_to_file(self.state.persistent_state_path()) {
warn!("Could not backup the state for this iteration: {err}");
}
}
pub(crate) async fn handle_epoch_state(&mut self) {
match self.dkg_client.get_current_epoch().await {
Err(err) => warn!("Could not get current epoch state {err}"),
@@ -99,57 +111,64 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
return;
}
if let Err(err) = self.state.is_consistent(epoch.state).await {
error!("Epoch state is corrupted - {err}, the process should be terminated");
return;
}
let ret = match epoch.state {
EpochState::PublicKeySubmission { resharing } => {
public_key_submission(&self.dkg_client, &mut self.state, resharing).await
}
EpochState::DealingExchange { resharing } => {
dealing_exchange(
&self.dkg_client,
&mut self.state,
self.rng.clone(),
resharing,
)
.await
}
EpochState::VerificationKeySubmission { resharing } => {
let keypair_path = nym_pemstore::KeyPairPath::new(
self.secret_key_path.clone(),
self.verification_key_path.clone(),
);
verification_key_submission(
&self.dkg_client,
&mut self.state,
&keypair_path,
resharing,
)
.await
}
EpochState::VerificationKeyValidation { resharing } => {
verification_key_validation(&self.dkg_client, &mut self.state, resharing)
debug!("Epoch state is corrupted - {err}. Awaiting for a DKG restart.");
} else {
let ret = match epoch.state {
EpochState::PublicKeySubmission { resharing } => {
public_key_submission(&self.dkg_client, &mut self.state, resharing)
.await
}
EpochState::DealingExchange { resharing } => {
dealing_exchange(
&self.dkg_client,
&mut self.state,
self.rng.clone(),
resharing,
)
.await
}
EpochState::VerificationKeyFinalization { resharing } => {
verification_key_finalization(&self.dkg_client, &mut self.state, resharing)
}
EpochState::VerificationKeySubmission { resharing } => {
let keypair_path = nym_pemstore::KeyPairPath::new(
self.secret_key_path.clone(),
self.verification_key_path.clone(),
);
verification_key_submission(
&self.dkg_client,
&mut self.state,
&keypair_path,
resharing,
)
.await
}
// Just wait, in case we need to redo dkg at some point
EpochState::InProgress => {
self.state.set_was_in_progress();
Ok(())
}
};
if let Err(err) = ret {
warn!("Could not handle this iteration for the epoch state: {err}");
} else if epoch.state != EpochState::InProgress {
let persistent_state = PersistentState::from(&self.state);
if let Err(err) =
persistent_state.save_to_file(self.state.persistent_state_path())
{
warn!("Could not backup the state for this iteration: {err}");
}
EpochState::VerificationKeyValidation { resharing } => {
verification_key_validation(
&self.dkg_client,
&mut self.state,
resharing,
)
.await
}
EpochState::VerificationKeyFinalization { resharing } => {
verification_key_finalization(
&self.dkg_client,
&mut self.state,
resharing,
)
.await
}
// Just wait, in case we need to redo dkg at some point
EpochState::InProgress => {
self.state.set_was_in_progress();
// We're dumping state here so that we don't do it uselessly during the
// long InProgress state
self.dump_persistent_state().await;
Ok(())
}
};
if let Err(err) = ret {
warn!("Could not handle this iteration for the epoch state: {err}");
} else if epoch.state != EpochState::InProgress {
self.dump_persistent_state().await;
}
}
if let Ok(current_timestamp) =
+24 -11
View File
@@ -4,6 +4,7 @@
use crate::coconut::dkg::client::DkgClient;
use crate::coconut::dkg::state::{ConsistentState, State};
use crate::coconut::error::CoconutError;
use log::debug;
use nym_coconut_dkg_common::types::TOTAL_DEALINGS;
use nym_contracts_common::dealings::ContractSafeBytes;
use nym_dkg::bte::setup;
@@ -18,6 +19,7 @@ pub(crate) async fn dealing_exchange(
resharing: bool,
) -> Result<(), CoconutError> {
if state.receiver_index().is_some() {
debug!("Receiver index was set previously, nothing to do");
return Ok(());
}
@@ -45,6 +47,7 @@ pub(crate) async fn dealing_exchange(
return Err(CoconutError::CorruptedCoconutKeyPair);
}
// We can now erase the keypair from memory
debug!("Removing coconut keypair from memory");
state.set_coconut_keypair(None).await;
scalars.push(x);
scalars
@@ -59,6 +62,11 @@ pub(crate) async fn dealing_exchange(
if !resharing || initial_dealers.iter().any(|d| *d == own_address) {
let params = setup();
for _ in 0..TOTAL_DEALINGS {
debug!(
"Submitting dealing for indexes {:?} with resharing: {}",
receivers.keys().collect::<Vec<_>>(),
prior_resharing_secrets.front().is_some()
);
let (dealing, _) = Dealing::create(
rng.clone(),
&params,
@@ -71,9 +79,11 @@ pub(crate) async fn dealing_exchange(
.submit_dealing(ContractSafeBytes::from(&dealing), resharing)
.await?;
}
} else {
debug!("Nothing to do, waiting for initial dealers to submit dealings");
}
info!("DKG: Finished submitting dealing");
info!("DKG: Finished dealing exchange");
state.set_receiver_index(receiver_index);
Ok(())
@@ -109,7 +119,7 @@ pub(crate) mod tests {
fn insert_dealers(
params: &Params,
dealer_details_db: &Arc<RwLock<HashMap<String, DealerDetails>>>,
dealer_details_db: &Arc<RwLock<HashMap<String, (DealerDetails, bool)>>>,
) -> Vec<DkgKeyPair> {
let mut keypairs = vec![];
for (idx, addr) in TEST_VALIDATORS_ADDRESS.iter().enumerate() {
@@ -119,12 +129,15 @@ pub(crate) mod tests {
keypairs.push(keypair);
dealer_details_db.write().unwrap().insert(
addr.to_string(),
DealerDetails {
address: Addr::unchecked(*addr),
bte_public_key_with_proof,
announce_address: format!("localhost:80{}", idx),
assigned_index: (idx + 1) as u64,
},
(
DealerDetails {
address: Addr::unchecked(*addr),
bte_public_key_with_proof,
announce_address: format!("localhost:80{}", idx),
assigned_index: (idx + 1) as u64,
},
true,
),
);
}
keypairs
@@ -216,7 +229,7 @@ pub(crate) mod tests {
.unwrap()
.entry(TEST_VALIDATORS_ADDRESS[1].to_string())
.and_modify(|details| {
let mut bytes = bs58::decode(details.bte_public_key_with_proof.clone())
let mut bytes = bs58::decode(details.0.bte_public_key_with_proof.clone())
.into_vec()
.unwrap();
// Find another value for last byte that still deserializes to a public key with proof
@@ -231,7 +244,7 @@ pub(crate) mod tests {
break;
}
}
details.bte_public_key_with_proof = bs58::encode(&bytes).into_string();
details.0.bte_public_key_with_proof = bs58::encode(&bytes).into_string();
});
dealing_exchange(&dkg_client, &mut state, OsRng, false)
@@ -257,7 +270,7 @@ pub(crate) mod tests {
let threshold_db = Arc::new(RwLock::new(Some(3)));
let initial_dealers_db = Arc::new(RwLock::new(Some(InitialReplacementData {
initial_dealers: vec![Addr::unchecked(TEST_VALIDATORS_ADDRESS[0])],
initial_height: Some(100),
initial_height: 100,
})));
let dkg_client = DkgClient::new(
DummyClient::new(
+16 -1
View File
@@ -4,6 +4,7 @@
use crate::coconut::dkg::client::DkgClient;
use crate::coconut::dkg::state::State;
use crate::coconut::error::CoconutError;
use log::debug;
use nym_coconut_dkg_common::dealer::DealerType;
pub(crate) async fn public_key_submission(
@@ -12,9 +13,21 @@ pub(crate) async fn public_key_submission(
resharing: bool,
) -> Result<(), CoconutError> {
if state.was_in_progress() {
state.reset_persistent(resharing).await;
let own_address = dkg_client.get_address().await.as_ref().to_string();
let is_initial_dealer = dkg_client
.get_initial_dealers()
.await?
.map(|data| data.initial_dealers.iter().any(|d| *d == own_address))
.unwrap_or(false);
let reset_coconut_keypair = !resharing || !is_initial_dealer;
debug!(
"Resetting state, with coconut keypair reset: {}",
reset_coconut_keypair
);
state.reset_persistent(reset_coconut_keypair).await;
}
if state.node_index().is_some() {
debug!("Node index was set previously, nothing to do");
return Ok(());
}
@@ -23,12 +36,14 @@ pub(crate) async fn public_key_submission(
let index = if let Some(details) = dealer_details.details {
if dealer_details.dealer_type == DealerType::Past {
// If it was a dealer in a previous epoch, re-register it for this epoch
debug!("Registering for the current DKG round, with keys from a previous epoch");
dkg_client
.register_dealer(bte_key, state.announce_address().to_string(), resharing)
.await?;
}
details.assigned_index
} else {
debug!("Registering for the first time to be a dealer");
// First time registration
dkg_client
.register_dealer(bte_key, state.announce_address().to_string(), resharing)
+8 -3
View File
@@ -5,6 +5,7 @@ use crate::coconut::dkg::complaints::ComplaintReason;
use crate::coconut::error::CoconutError;
use crate::coconut::keypair::KeyPair as CoconutKeyPair;
use cosmwasm_std::Addr;
use log::debug;
use nym_coconut_dkg_common::dealer::DealerDetails;
use nym_coconut_dkg_common::types::EpochState;
use nym_dkg::bte::{keys::KeyPair as DkgKeyPair, PublicKey, PublicKeyWithProof};
@@ -133,7 +134,7 @@ impl ConsistentState for State {
fn proposal_id_value(&self) -> Result<u64, CoconutError> {
self.proposal_id.ok_or(CoconutError::UnrecoverableState {
reason: String::from("Proposal id should have benn set"),
reason: String::from("Proposal id should have been set"),
})
}
}
@@ -242,8 +243,8 @@ impl State {
}
}
pub async fn reset_persistent(&mut self, resharing: bool) {
if !resharing {
pub async fn reset_persistent(&mut self, reset_coconut_keypair: bool) {
if reset_coconut_keypair {
self.coconut_keypair.set(None).await;
}
self.node_index = Default::default();
@@ -360,6 +361,10 @@ impl State {
.iter_mut()
.find(|(addr, _)| *addr == dealer_addr)
{
debug!(
"Dealer {} misbehaved: {:?}. It will be marked locally as bad dealer and ignored",
dealer_addr, reason
);
*value = Err(reason);
}
}
+258 -18
View File
@@ -8,6 +8,7 @@ use crate::coconut::error::CoconutError;
use crate::coconut::helpers::accepted_vote_err;
use cosmwasm_std::Addr;
use cw3::{ProposalResponse, Status};
use log::debug;
use nym_coconut_dkg_common::event_attributes::DKG_PROPOSAL_ID;
use nym_coconut_dkg_common::types::{NodeIndex, TOTAL_DEALINGS};
use nym_coconut_dkg_common::verification_key::owner_from_cosmos_msgs;
@@ -116,6 +117,11 @@ fn derive_partial_keypair(
}
})
.unzip();
debug!(
"Recovering verification keys from dealings of dealers {:?} with receivers {:?}",
filtered_dealers,
filtered_receivers_by_idx.keys().collect::<Vec<_>>()
);
let recovered = try_recover_verification_keys(
&filtered_dealings,
threshold,
@@ -123,10 +129,12 @@ fn derive_partial_keypair(
)?;
recovered_vks.push(recovered);
debug!("Decrypting shares");
let shares = filtered_dealings
.iter()
.map(|dealing| decrypt_share(dk, node_index_value, &dealing.ciphertexts, None))
.collect::<Result<_, _>>()?;
debug!("Combining shares into one secret");
let scalar = combine_shares(shares, &filtered_dealers)?;
scalars.push(scalar);
}
@@ -152,13 +160,19 @@ pub(crate) async fn verification_key_submission(
resharing: bool,
) -> Result<(), CoconutError> {
if state.coconut_keypair_is_some().await {
debug!("Coconut keypair was set previously, nothing to do");
return Ok(());
}
let threshold = state.threshold()?;
let dealings_maps =
deterministic_filter_dealers(dkg_client, state, threshold, resharing).await?;
debug!(
"Filtered dealers to {:?}",
dealings_maps[0].keys().collect::<Vec<_>>()
);
let coconut_keypair = derive_partial_keypair(state, threshold, dealings_maps)?;
debug!("Derived own coconut keypair");
let vk_share = coconut_keypair.verification_key().to_bs58();
nym_pemstore::store_keypair(&coconut_keypair, keypair_path)?;
let res = dkg_client
@@ -173,6 +187,10 @@ pub(crate) async fn verification_key_submission(
.map_err(|_| CoconutError::ProposalIdError {
reason: String::from("proposal id could not be parsed to u64"),
})?;
debug!(
"Submitted own verification key share, proposal id {} is attached to it",
proposal_id
);
state.set_proposal_id(proposal_id);
state.set_coconut_keypair(Some(coconut_keypair)).await;
info!("DKG: Submitted own verification key");
@@ -195,6 +213,7 @@ pub(crate) async fn verification_key_validation(
_resharing: bool,
) -> Result<(), CoconutError> {
if state.voted_vks() {
debug!("Already voted on the verification keys, nothing to do");
return Ok(());
}
@@ -225,10 +244,15 @@ pub(crate) async fn verification_key_validation(
.position(|node_index| contract_share.node_index == *node_index)
{
let ret = if !check_vk_pairing(&params, &recovered_partials[idx], &vk) {
debug!(
"Voting NO to proposal {} because of failed VK pairing",
proposal_id
);
dkg_client
.vote_verification_key_share(proposal_id, false)
.await
} else {
debug!("Voting YES to proposal {}", proposal_id);
dkg_client
.vote_verification_key_share(proposal_id, true)
.await
@@ -237,6 +261,10 @@ pub(crate) async fn verification_key_validation(
}
}
Err(_) => {
debug!(
"Voting NO to proposal {} because of failed base 58 deserialization",
proposal_id
);
let ret = dkg_client
.vote_verification_key_share(proposal_id, false)
.await;
@@ -256,6 +284,7 @@ pub(crate) async fn verification_key_finalization(
_resharing: bool,
) -> Result<(), CoconutError> {
if state.executed_proposal() {
debug!("Already executed the proposal, nothing to do");
return Ok(());
}
@@ -294,7 +323,7 @@ pub(crate) mod tests {
use validator_client::nyxd::AccountId;
struct MockContractDb {
dealer_details_db: Arc<RwLock<HashMap<String, DealerDetails>>>,
dealer_details_db: Arc<RwLock<HashMap<String, (DealerDetails, bool)>>>,
dealings_db: Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
proposal_db: Arc<RwLock<HashMap<u64, ProposalResponse>>>,
verification_share_db: Arc<RwLock<HashMap<String, ContractVKShare>>>,
@@ -315,10 +344,11 @@ pub(crate) mod tests {
}
}
const TEST_VALIDATORS_ADDRESS: [&str; 3] = [
const TEST_VALIDATORS_ADDRESS: [&str; 4] = [
"n1aq9kakfgwqcufr23lsv644apavcntrsqsk4yus",
"n1s9l3xr4g0rglvk4yctktmck3h4eq0gp6z2e20v",
"n19kl4py32vsk297dm93ezem992cdyzdy4zuc2x6",
"n1jfrs6cmw9t7dv0x8cgny6geunzjh56n2s89fkv",
];
async fn prepare_clients_and_states(db: &MockContractDb) -> Vec<(DkgClient, State)> {
@@ -418,7 +448,7 @@ pub(crate) mod tests {
.unwrap();
assert_eq!(filtered.len(), TOTAL_DEALINGS);
for mapping in filtered.iter() {
assert_eq!(mapping.len(), 3);
assert_eq!(mapping.len(), 4);
}
}
}
@@ -471,7 +501,7 @@ pub(crate) mod tests {
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
initial_dealers: vec![Addr::unchecked(TEST_VALIDATORS_ADDRESS[0])],
initial_height: None,
initial_height: 1,
});
let filtered = deterministic_filter_dealers(dkg_client, state, 2, true)
.await
@@ -504,7 +534,7 @@ pub(crate) mod tests {
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
initial_dealers: vec![],
initial_height: None,
initial_height: 1,
});
let filtered = deterministic_filter_dealers(dkg_client, state, 2, true)
.await
@@ -542,7 +572,7 @@ pub(crate) mod tests {
.unwrap();
assert_eq!(filtered.len(), TOTAL_DEALINGS);
for mapping in filtered.iter() {
assert_eq!(mapping.len(), 2);
assert_eq!(mapping.len(), 3);
}
let corrupted_status = state
.all_dealers()
@@ -803,6 +833,9 @@ pub(crate) mod tests {
async fn reshare_preserves_keys() {
let db = MockContractDb::new();
let mut clients_and_states = prepare_clients_and_states_with_finalization(&db).await;
for (_, state) in clients_and_states.iter_mut() {
state.set_was_in_progress();
}
let params = Parameters::new(4).unwrap();
let mut vks = vec![];
@@ -839,23 +872,22 @@ pub(crate) mod tests {
KeyPair::new(),
);
let removed_dealer = clients_and_states.first().unwrap().0.get_address().await;
db.dealer_details_db
.write()
.unwrap()
.remove(removed_dealer.as_ref());
for (_, active) in db.dealer_details_db.write().unwrap().values_mut() {
*active = false;
}
*db.dealings_db.write().unwrap() = Default::default();
*db.verification_share_db.write().unwrap() = Default::default();
let mut initial_dealers = vec![];
for (dkg_client, _) in clients_and_states.iter() {
let client_address = Addr::unchecked(dkg_client.get_address().await.as_ref());
initial_dealers.push(client_address);
}
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
initial_dealers: vec![
Addr::unchecked(clients_and_states[1].0.get_address().await.as_ref()),
Addr::unchecked(clients_and_states[2].0.get_address().await.as_ref()),
],
initial_height: None,
initial_dealers,
initial_height: 1,
});
*clients_and_states.first_mut().unwrap() = (new_dkg_client, state);
clients_and_states[1].1.set_was_in_progress();
clients_and_states[2].1.set_was_in_progress();
for (dkg_client, state) in clients_and_states.iter_mut() {
public_key_submission(dkg_client, state, true)
@@ -880,6 +912,214 @@ pub(crate) mod tests {
std::fs::remove_file(private_key_path).unwrap();
std::fs::remove_file(public_key_path).unwrap();
}
for (dkg_client, state) in clients_and_states.iter_mut() {
verification_key_validation(dkg_client, state, true)
.await
.unwrap();
}
for (dkg_client, state) in clients_and_states.iter_mut() {
verification_key_finalization(dkg_client, state, true)
.await
.unwrap();
}
assert!(db
.proposal_db
.read()
.unwrap()
.values()
.all(|proposal| { proposal.status == Status::Executed }));
let mut vks = vec![];
let mut indices = vec![];
for (_, state) in clients_and_states.iter() {
let vk = state
.coconut_secret_key()
.await
.unwrap()
.verification_key(&params);
let index = state.node_index().unwrap();
vks.push(vk);
indices.push(index);
}
let reshared_master_vk = aggregate_verification_keys(&vks, Some(&indices)).unwrap();
assert_eq!(initial_master_vk, reshared_master_vk);
}
#[tokio::test]
#[ignore] // expensive test
async fn reshare_after_reset() {
let db = MockContractDb::new();
let mut clients_and_states = prepare_clients_and_states_with_finalization(&db).await;
for (_, state) in clients_and_states.iter_mut() {
state.set_was_in_progress();
}
let new_dkg_client = DkgClient::new(
DummyClient::new(
AccountId::from_str("n1vxkywf9g4cg0k2dehanzwzz64jw782qm0kuynf").unwrap(),
)
.with_dealer_details(&db.dealer_details_db)
.with_dealings(&db.dealings_db)
.with_proposal_db(&db.proposal_db)
.with_verification_share(&db.verification_share_db)
.with_threshold(&db.threshold_db)
.with_initial_dealers_db(&db.initial_dealers_db),
);
let keypair = DkgKeyPair::new(&setup(), OsRng);
let state = State::new(
PathBuf::default(),
PersistentState::default(),
Url::parse("localhost:8000").unwrap(),
keypair,
KeyPair::new(),
);
let new_dkg_client2 = DkgClient::new(
DummyClient::new(
AccountId::from_str("n1sqkxzh7nl6kgndr4ew9795t2nkwmd8tpql67q7").unwrap(),
)
.with_dealer_details(&db.dealer_details_db)
.with_dealings(&db.dealings_db)
.with_proposal_db(&db.proposal_db)
.with_verification_share(&db.verification_share_db)
.with_threshold(&db.threshold_db)
.with_initial_dealers_db(&db.initial_dealers_db),
);
let keypair = DkgKeyPair::new(&setup(), OsRng);
let state2 = State::new(
PathBuf::default(),
PersistentState::default(),
Url::parse("localhost:8000").unwrap(),
keypair,
KeyPair::new(),
);
for (_, active) in db.dealer_details_db.write().unwrap().values_mut() {
*active = false;
}
*db.dealings_db.write().unwrap() = Default::default();
*db.verification_share_db.write().unwrap() = Default::default();
clients_and_states.pop().unwrap();
let (initial_client2, initial_state2) = clients_and_states.pop().unwrap();
clients_and_states.push((new_dkg_client, state));
clients_and_states.push((new_dkg_client2, state2));
// DKG in reset mode
for (dkg_client, state) in clients_and_states.iter_mut() {
public_key_submission(dkg_client, state, false)
.await
.unwrap();
}
for (dkg_client, state) in clients_and_states.iter_mut() {
dealing_exchange(dkg_client, state, OsRng, false)
.await
.unwrap();
}
for (dkg_client, state) in clients_and_states.iter_mut() {
let random_file: usize = OsRng.gen();
let private_key_path = temp_dir().join(format!("private{}.pem", random_file));
let public_key_path = temp_dir().join(format!("public{}.pem", random_file));
let keypair_path = KeyPairPath::new(private_key_path.clone(), public_key_path.clone());
verification_key_submission(dkg_client, state, &keypair_path, false)
.await
.unwrap();
std::fs::remove_file(private_key_path).unwrap();
std::fs::remove_file(public_key_path).unwrap();
}
for (dkg_client, state) in clients_and_states.iter_mut() {
verification_key_validation(dkg_client, state, false)
.await
.unwrap();
}
for (dkg_client, state) in clients_and_states.iter_mut() {
verification_key_finalization(dkg_client, state, false)
.await
.unwrap();
}
assert!(db
.proposal_db
.read()
.unwrap()
.values()
.all(|proposal| { proposal.status == Status::Executed }));
for (_, state) in clients_and_states.iter_mut() {
state.set_was_in_progress();
}
// DKG in reshare mode
let params = Parameters::new(4).unwrap();
let mut vks = vec![];
let mut indices = vec![];
for (_, state) in clients_and_states.iter() {
let vk = state
.coconut_secret_key()
.await
.unwrap()
.verification_key(&params);
let index = state.node_index().unwrap();
vks.push(vk);
indices.push(index);
}
let initial_master_vk = aggregate_verification_keys(&vks, Some(&indices)).unwrap();
for (_, active) in db.dealer_details_db.write().unwrap().values_mut() {
*active = false;
}
*db.dealings_db.write().unwrap() = Default::default();
*db.verification_share_db.write().unwrap() = Default::default();
let mut initial_dealers = vec![];
for (dkg_client, _) in clients_and_states.iter() {
let client_address = Addr::unchecked(dkg_client.get_address().await.as_ref());
initial_dealers.push(client_address);
}
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
initial_dealers,
initial_height: 1,
});
*clients_and_states.last_mut().unwrap() = (initial_client2, initial_state2);
for (dkg_client, state) in clients_and_states.iter_mut() {
public_key_submission(dkg_client, state, true)
.await
.unwrap();
}
for (dkg_client, state) in clients_and_states.iter_mut() {
dealing_exchange(dkg_client, state, OsRng, true)
.await
.unwrap();
}
for (dkg_client, state) in clients_and_states.iter_mut() {
let random_file: usize = OsRng.gen();
let private_key_path = temp_dir().join(format!("private{}.pem", random_file));
let public_key_path = temp_dir().join(format!("public{}.pem", random_file));
let keypair_path = KeyPairPath::new(private_key_path.clone(), public_key_path.clone());
verification_key_submission(dkg_client, state, &keypair_path, true)
.await
.unwrap();
std::fs::remove_file(private_key_path).unwrap();
std::fs::remove_file(public_key_path).unwrap();
}
for (dkg_client, state) in clients_and_states.iter_mut() {
verification_key_validation(dkg_client, state, true)
.await
.unwrap();
}
for (dkg_client, state) in clients_and_states.iter_mut() {
verification_key_finalization(dkg_client, state, true)
.await
.unwrap();
}
// assert!(db
// .proposal_db
// .read()
// .unwrap()
// .values()
// .all(|proposal| { proposal.status == Status::Executed }));
let mut vks = vec![];
let mut indices = vec![];
for (_, state) in clients_and_states.iter() {
+1 -1
View File
@@ -91,7 +91,7 @@ pub enum CoconutError {
#[error("Failed to recover assigned node index: {reason}")]
NodeIndexRecoveryError { reason: String },
#[error("Unrecoverable state: {reason}. Process should be restarted")]
#[error("Unrecoverable state: {reason}")]
UnrecoverableState { reason: String },
#[error("DKG has not finished yet in order to derive the coconut key")]
+52 -27
View File
@@ -69,7 +69,7 @@ pub(crate) struct DummyClient {
spent_credential_db: Arc<RwLock<HashMap<String, SpendCredentialResponse>>>,
epoch: Arc<RwLock<Epoch>>,
dealer_details: Arc<RwLock<HashMap<String, DealerDetails>>>,
dealer_details: Arc<RwLock<HashMap<String, (DealerDetails, bool)>>>,
threshold: Arc<RwLock<Option<Threshold>>>,
dealings: Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
verification_share: Arc<RwLock<HashMap<String, ContractVKShare>>>,
@@ -122,7 +122,7 @@ impl DummyClient {
pub fn with_dealer_details(
mut self,
dealer_details: &Arc<RwLock<HashMap<String, DealerDetails>>>,
dealer_details: &Arc<RwLock<HashMap<String, (DealerDetails, bool)>>>,
) -> Self {
self.dealer_details = Arc::clone(dealer_details);
self
@@ -233,14 +233,25 @@ impl super::client::Client for DummyClient {
}
async fn get_self_registered_dealer_details(&self) -> Result<DealerDetailsResponse> {
let (details, dealer_type) = if let Some((details, current)) = self
.dealer_details
.read()
.unwrap()
.get(self.validator_address.as_ref())
.cloned()
{
let dealer_type = if current {
DealerType::Current
} else {
DealerType::Past
};
(Some(details), dealer_type)
} else {
(None, DealerType::Unknown)
};
Ok(DealerDetailsResponse {
details: self
.dealer_details
.read()
.unwrap()
.get(self.validator_address.as_ref())
.cloned(),
dealer_type: DealerType::Current,
details,
dealer_type,
})
}
@@ -251,6 +262,7 @@ impl super::client::Client for DummyClient {
.unwrap()
.values()
.cloned()
.filter_map(|(d, current)| if current { Some(d) } else { None })
.collect())
}
@@ -287,13 +299,11 @@ impl super::client::Client for DummyClient {
_fee: Option<Fee>,
) -> Result<()> {
if let Some(proposal) = self.proposal_db.write().unwrap().get_mut(&proposal_id) {
// for now, just suppose that first vote is honest
if proposal.status == cw3::Status::Open {
if vote_yes {
proposal.status = cw3::Status::Passed;
} else {
proposal.status = cw3::Status::Rejected;
}
// for now, just suppose that every vote is honest
if !vote_yes {
proposal.status = cw3::Status::Rejected;
} else if vote_yes && proposal.status == cw3::Status::Open {
proposal.status = cw3::Status::Passed;
}
}
Ok(())
@@ -323,22 +333,33 @@ impl super::client::Client for DummyClient {
_resharing: bool,
) -> Result<ExecuteResult> {
let mut dealer_details = self.dealer_details.write().unwrap();
let assigned_index =
if let Some(details) = dealer_details.get(self.validator_address.as_ref()) {
details.assigned_index
} else {
let assigned_index = OsRng.gen();
dealer_details.insert(
self.validator_address.to_string(),
let assigned_index = if let Some((details, active)) =
dealer_details.get_mut(self.validator_address.as_ref())
{
*active = true;
details.assigned_index
} else {
// let assigned_index = OsRng.gen();
let assigned_index = dealer_details
.values()
.map(|(d, _)| d.assigned_index)
.max()
.unwrap_or(0)
+ 1;
dealer_details.insert(
self.validator_address.to_string(),
(
DealerDetails {
address: Addr::unchecked(self.validator_address.to_string()),
bte_public_key_with_proof,
announce_address,
assigned_index,
},
);
assigned_index
};
true,
),
);
assigned_index
};
Ok(ExecuteResult {
logs: vec![Log {
msg_index: 0,
@@ -380,13 +401,17 @@ impl super::client::Client for DummyClient {
share: VerificationKeyShare,
resharing: bool,
) -> Result<ExecuteResult> {
let dealer_details = self
let (dealer_details, active) = self
.dealer_details
.read()
.unwrap()
.get(self.validator_address.as_ref())
.unwrap()
.clone();
if !active {
// Just throw some error, not really the correct one
return Err(CoconutError::DepositEncrKeyNotFound);
}
self.verification_share.write().unwrap().insert(
self.validator_address.to_string(),
ContractVKShare {
+1 -1
View File
@@ -20,7 +20,7 @@ tauri-macros = "^1.2.1"
[dependencies]
anyhow = "1.0"
bip39 = "1.0"
bip39 = { version = "2.0.0", features = ["zeroize"] }
dirs = "4.0"
eyre = "0.6.5"
fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs", branch = "release"}
+2
View File
@@ -54,6 +54,8 @@ yarn webpack:prod
WRY_ANDROID_PACKAGE=net.nymtech.nym_connect WRY_ANDROID_LIBRARY=nym_connect cargo tauri android build --debug --apk
```
**NOTE**: Production build without the `--debug` flag requires a signed build.
# Storybook
Run storybook with:
+49 -182
View File
@@ -186,15 +186,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "autocfg"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
dependencies = [
"autocfg 1.1.0",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@@ -245,15 +236,16 @@ dependencies = [
[[package]]
name = "bip39"
version = "1.1.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5b9d9748b5770d1539657653dc5ac3cd9353549e74238dc0d96c22919128b94"
checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f"
dependencies = [
"bitcoin_hashes",
"rand 0.6.5",
"rand_core 0.4.2",
"rand 0.8.5",
"rand_core 0.6.4",
"serde",
"unicode-normalization",
"zeroize",
]
[[package]]
@@ -501,7 +493,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa"
dependencies = [
"smallvec 1.10.0",
"smallvec",
]
[[package]]
@@ -649,15 +641,6 @@ dependencies = [
"wasm-utils",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "cocoa"
version = "0.24.1"
@@ -1005,7 +988,7 @@ dependencies = [
"phf 0.8.0",
"proc-macro2",
"quote",
"smallvec 1.10.0",
"smallvec",
"syn",
]
@@ -1687,12 +1670,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "funty"
version = "2.0.0"
@@ -2028,7 +2005,7 @@ dependencies = [
"libc",
"once_cell",
"pin-project-lite",
"smallvec 1.10.0",
"smallvec",
"thiserror",
]
@@ -2076,7 +2053,7 @@ dependencies = [
"gobject-sys",
"libc",
"once_cell",
"smallvec 1.10.0",
"smallvec",
"thiserror",
]
@@ -2659,7 +2636,7 @@ version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"hashbrown 0.12.3",
"serde",
]
@@ -3009,7 +2986,7 @@ version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"scopeguard",
]
@@ -3095,12 +3072,6 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.5.0"
@@ -3113,7 +3084,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg 1.1.0",
"autocfg",
]
[[package]]
@@ -3273,7 +3244,7 @@ version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"num-traits",
]
@@ -3283,7 +3254,7 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"num-integer",
"num-traits",
]
@@ -3294,7 +3265,7 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"libm",
]
@@ -4002,7 +3973,7 @@ version = "0.9.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"cc",
"libc",
"openssl-src",
@@ -4097,7 +4068,7 @@ dependencies = [
"instant",
"libc",
"redox_syscall",
"smallvec 1.10.0",
"smallvec",
"winapi",
]
@@ -4110,7 +4081,7 @@ dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec 1.10.0",
"smallvec",
"windows-sys 0.45.0",
]
@@ -4545,25 +4516,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
dependencies = [
"autocfg 0.1.8",
"libc",
"rand_chacha 0.1.1",
"rand_core 0.4.2",
"rand_hc 0.1.0",
"rand_isaac",
"rand_jitter",
"rand_os",
"rand_pcg 0.1.2",
"rand_xorshift",
"winapi",
]
[[package]]
name = "rand"
version = "0.7.3"
@@ -4574,8 +4526,8 @@ dependencies = [
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
"rand_pcg 0.2.1",
"rand_hc",
"rand_pcg",
]
[[package]]
@@ -4589,16 +4541,6 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
dependencies = [
"autocfg 0.1.8",
"rand_core 0.3.1",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
@@ -4619,21 +4561,6 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.5.1"
@@ -4662,15 +4589,6 @@ dependencies = [
"rand 0.7.3",
]
[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
@@ -4680,50 +4598,6 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_jitter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
dependencies = [
"libc",
"rand_core 0.4.2",
"winapi",
]
[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
dependencies = [
"cloudabi",
"fuchsia-cprng",
"libc",
"rand_core 0.4.2",
"rdrand",
"winapi",
]
[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
dependencies = [
"autocfg 0.1.8",
"rand_core 0.4.2",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
@@ -4733,15 +4607,6 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "raw-window-handle"
version = "0.5.0"
@@ -4751,15 +4616,6 @@ dependencies = [
"cty",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@@ -5177,7 +5033,7 @@ dependencies = [
"phf_codegen",
"precomputed-hash",
"servo_arc",
"smallvec 1.10.0",
"smallvec",
"thin-slice",
]
@@ -5445,16 +5301,7 @@ version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
dependencies = [
"autocfg 1.1.0",
]
[[package]]
name = "smallvec"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
dependencies = [
"maybe-uninit",
"autocfg",
]
[[package]]
@@ -5626,7 +5473,7 @@ dependencies = [
"percent-encoding",
"rustls 0.19.1",
"sha2 0.10.6",
"smallvec 1.10.0",
"smallvec",
"sqlformat 0.1.8",
"sqlx-rt 0.5.13",
"stringprep",
@@ -5673,7 +5520,7 @@ dependencies = [
"rustls 0.20.8",
"rustls-pemfile",
"sha2 0.10.6",
"smallvec 1.10.0",
"smallvec",
"sqlformat 0.2.1",
"sqlx-rt 0.6.2",
"stringprep",
@@ -6363,13 +6210,28 @@ dependencies = [
"time-core",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"bytes",
"libc",
"memchr",
@@ -6564,7 +6426,7 @@ dependencies = [
"once_cell",
"regex",
"sharded-slab",
"smallvec 1.10.0",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
@@ -6667,11 +6529,11 @@ checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-normalization"
version = "0.1.9"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"smallvec 0.6.14",
"tinyvec",
]
[[package]]
@@ -7438,3 +7300,8 @@ dependencies = [
"syn",
"synstructure",
]
[[patch.unused]]
name = "tauri-mobile"
version = "0.2.4"
source = "git+https://github.com/tauri-apps/tauri-mobile?branch=dev#442f0d2c7328930db61058a55706d22e6a401c16"
+5 -1
View File
@@ -19,9 +19,13 @@ crate-type = ["staticlib", "cdylib", "rlib"]
tauri-build = { version = "2.0.0-alpha.1", features = [] }
# tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = [] }
# TODO untill new tauri version includes https://github.com/tauri-apps/tauri-mobile/pull/111
[patch.crates-io]
tauri-mobile = { git = "https://github.com/tauri-apps/tauri-mobile", branch = "dev" }
[dependencies]
anyhow = "1.0"
bip39 = "1.0"
bip39 = { version = "2.0.0", features = ["zeroize"] }
chrono = "0.4"
dirs = "4.0"
eyre = "0.6.5"
+259 -213
View File
@@ -20,7 +20,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
dependencies = [
"generic-array",
"generic-array 0.14.6",
]
[[package]]
@@ -32,7 +32,8 @@ dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"opaque-debug",
"ctr",
"opaque-debug 0.3.0",
]
[[package]]
@@ -46,7 +47,7 @@ dependencies = [
"cipher",
"ctr",
"ghash",
"subtle",
"subtle 2.4.1",
]
[[package]]
@@ -97,7 +98,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25df3c03f1040d0069fcd3907e24e36d59f9b6fa07ba49be0eb25a794f036ba7"
dependencies = [
"base64ct",
"blake2",
"blake2 0.10.6",
"password-hash 0.3.2",
]
@@ -108,10 +109,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a27e27b63e4a34caee411ade944981136fdfa535522dc9944d6700196cbd899f"
dependencies = [
"base64ct",
"blake2",
"blake2 0.10.6",
"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]]
name = "async-trait"
version = "0.1.64"
@@ -174,15 +181,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "autocfg"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
dependencies = [
"autocfg 1.1.0",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@@ -227,28 +225,29 @@ dependencies = [
"rand_core 0.6.4",
"ripemd160",
"sha2 0.9.9",
"subtle",
"subtle 2.4.1",
"zeroize",
]
[[package]]
name = "bip39"
version = "1.0.1"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e89470017230c38e52b82b3ee3f530db1856ba1d434e3a67a3456a8a8dec5f"
checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f"
dependencies = [
"bitcoin_hashes",
"rand 0.6.5",
"rand_core 0.4.2",
"rand 0.8.5",
"rand_core 0.6.4",
"serde",
"unicode-normalization",
"zeroize",
]
[[package]]
name = "bitcoin_hashes"
version = "0.9.7"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1"
checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4"
[[package]]
name = "bitflags"
@@ -268,6 +267,18 @@ dependencies = [
"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]]
name = "blake2"
version = "0.10.6"
@@ -290,7 +301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"block-padding",
"generic-array",
"generic-array 0.14.6",
]
[[package]]
@@ -299,7 +310,7 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
dependencies = [
"generic-array",
"generic-array 0.14.6",
]
[[package]]
@@ -318,7 +329,7 @@ dependencies = [
"group",
"pairing",
"rand_core 0.6.4",
"subtle",
"subtle 2.4.1",
"zeroize",
]
@@ -368,6 +379,12 @@ version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "bytemuck"
version = "1.13.0"
@@ -451,7 +468,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7"
dependencies = [
"smallvec 1.10.0",
"smallvec",
]
[[package]]
@@ -460,7 +477,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa"
dependencies = [
"smallvec 1.10.0",
"smallvec",
]
[[package]]
@@ -469,13 +486,23 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "cipher"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
dependencies = [
"generic-array",
"generic-array 0.14.6",
]
[[package]]
@@ -534,15 +561,6 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "cocoa"
version = "0.24.1"
@@ -785,9 +803,9 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21"
dependencies = [
"generic-array",
"generic-array 0.14.6",
"rand_core 0.6.4",
"subtle",
"subtle 2.4.1",
"zeroize",
]
@@ -797,18 +815,28 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"generic-array 0.14.6",
"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]]
name = "crypto-mac"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
dependencies = [
"generic-array",
"subtle",
"generic-array 0.14.6",
"subtle 2.4.1",
]
[[package]]
@@ -824,7 +852,7 @@ dependencies = [
"phf 0.8.0",
"proc-macro2",
"quote",
"smallvec 1.10.0",
"smallvec",
"syn",
]
@@ -881,7 +909,7 @@ dependencies = [
"byteorder",
"digest 0.9.0",
"rand_core 0.5.1",
"subtle",
"subtle 2.4.1",
"zeroize",
]
@@ -1017,13 +1045,22 @@ dependencies = [
"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]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array",
"generic-array 0.14.6",
]
[[package]]
@@ -1034,7 +1071,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer 0.10.3",
"crypto-common",
"subtle",
"subtle 2.4.1",
]
[[package]]
@@ -1146,6 +1183,8 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand 0.7.3",
"serde",
"sha2 0.9.9",
"zeroize",
]
@@ -1181,11 +1220,11 @@ dependencies = [
"crypto-bigint",
"der",
"ff",
"generic-array",
"generic-array 0.14.6",
"group",
"rand_core 0.6.4",
"sec1",
"subtle",
"subtle 2.4.1",
"zeroize",
]
@@ -1294,7 +1333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924"
dependencies = [
"rand_core 0.6.4",
"subtle",
"subtle 2.4.1",
]
[[package]]
@@ -1375,12 +1414,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "funty"
version = "2.0.0"
@@ -1580,6 +1613,15 @@ dependencies = [
"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]]
name = "generic-array"
version = "0.14.6"
@@ -1597,8 +1639,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
@@ -1632,7 +1676,7 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
dependencies = [
"opaque-debug",
"opaque-debug 0.3.0",
"polyval",
]
@@ -1695,7 +1739,7 @@ dependencies = [
"gobject-sys",
"libc",
"once_cell",
"smallvec 1.10.0",
"smallvec",
"thiserror",
]
@@ -1763,7 +1807,7 @@ dependencies = [
"byteorder",
"ff",
"rand_core 0.6.4",
"subtle",
"subtle 2.4.1",
]
[[package]]
@@ -1939,13 +1983,23 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "hmac"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
dependencies = [
"crypto-mac",
"crypto-mac 0.11.1",
"digest 0.9.0",
]
@@ -2171,7 +2225,7 @@ version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"hashbrown",
"serde",
]
@@ -2338,6 +2392,12 @@ dependencies = [
"cpufeatures",
]
[[package]]
name = "keystream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28"
[[package]]
name = "kuchiki"
version = "0.8.1"
@@ -2374,6 +2434,12 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "libm"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
[[package]]
name = "libz-sys"
version = "1.1.8"
@@ -2401,13 +2467,25 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"scopeguard",
]
@@ -2480,12 +2558,6 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.5.0"
@@ -2498,7 +2570,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg 1.1.0",
"autocfg",
]
[[package]]
@@ -2628,7 +2700,7 @@ version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"num-traits",
]
@@ -2638,7 +2710,7 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"num-integer",
"num-traits",
]
@@ -2649,7 +2721,8 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"libm",
]
[[package]]
@@ -2776,6 +2849,20 @@ dependencies = [
"thiserror",
]
[[package]]
name = "nym-crypto"
version = "0.2.0"
dependencies = [
"bs58",
"ed25519-dalek",
"nym-pemstore",
"nym-sphinx-types",
"rand 0.7.3",
"subtle-encoding",
"thiserror",
"x25519-dalek",
]
[[package]]
name = "nym-dkg"
version = "0.1.0"
@@ -2866,6 +2953,13 @@ dependencies = [
"pem",
]
[[package]]
name = "nym-sphinx-types"
version = "0.2.0"
dependencies = [
"sphinx-packet",
]
[[package]]
name = "nym-types"
version = "1.0.0"
@@ -2961,6 +3055,7 @@ version = "1.1.11"
dependencies = [
"aes-gcm",
"argon2 0.3.4",
"async-trait",
"base64 0.13.1",
"bip39",
"cfg-if",
@@ -2977,6 +3072,8 @@ dependencies = [
"log",
"nym-coconut-interface",
"nym-config",
"nym-contracts-common",
"nym-crypto",
"nym-mixnet-contract-common",
"nym-types",
"nym-vesting-contract",
@@ -2984,7 +3081,7 @@ dependencies = [
"nym-wallet-types",
"once_cell",
"pretty_env_logger",
"rand 0.6.5",
"rand_chacha 0.2.2",
"reqwest",
"serde",
"serde_json",
@@ -3071,6 +3168,12 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "opaque-debug"
version = "0.3.0"
@@ -3125,7 +3228,7 @@ version = "0.9.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"cc",
"libc",
"pkg-config",
@@ -3197,7 +3300,7 @@ dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec 1.10.0",
"smallvec",
"windows-sys 0.45.0",
]
@@ -3209,7 +3312,7 @@ checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8"
dependencies = [
"base64ct",
"rand_core 0.6.4",
"subtle",
"subtle 2.4.1",
]
[[package]]
@@ -3220,7 +3323,7 @@ checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
dependencies = [
"base64ct",
"rand_core 0.6.4",
"subtle",
"subtle 2.4.1",
]
[[package]]
@@ -3241,7 +3344,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739"
dependencies = [
"crypto-mac",
"crypto-mac 0.11.1",
]
[[package]]
@@ -3513,7 +3616,7 @@ checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
dependencies = [
"cfg-if",
"cpufeatures",
"opaque-debug",
"opaque-debug 0.3.0",
"universal-hash",
]
@@ -3657,25 +3760,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
dependencies = [
"autocfg 0.1.8",
"libc",
"rand_chacha 0.1.1",
"rand_core 0.4.2",
"rand_hc 0.1.0",
"rand_isaac",
"rand_jitter",
"rand_os",
"rand_pcg 0.1.2",
"rand_xorshift",
"winapi",
]
[[package]]
name = "rand"
version = "0.7.3"
@@ -3686,8 +3770,8 @@ dependencies = [
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
"rand_pcg 0.2.1",
"rand_hc",
"rand_pcg",
]
[[package]]
@@ -3701,16 +3785,6 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
dependencies = [
"autocfg 0.1.8",
"rand_core 0.3.1",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
@@ -3731,21 +3805,6 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.5.1"
@@ -3765,12 +3824,13 @@ dependencies = [
]
[[package]]
name = "rand_hc"
version = "0.1.0"
name = "rand_distr"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
checksum = "c9e9532ada3929fb8b2e9dbe28d1e06c9b2cc65813f074fcb6bd5fbefeff9d56"
dependencies = [
"rand_core 0.3.1",
"num-traits",
"rand 0.7.3",
]
[[package]]
@@ -3782,50 +3842,6 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_jitter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
dependencies = [
"libc",
"rand_core 0.4.2",
"winapi",
]
[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
dependencies = [
"cloudabi",
"fuchsia-cprng",
"libc",
"rand_core 0.4.2",
"rdrand",
"winapi",
]
[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
dependencies = [
"autocfg 0.1.8",
"rand_core 0.4.2",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
@@ -3835,15 +3851,6 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "raw-window-handle"
version = "0.5.0"
@@ -3853,15 +3860,6 @@ dependencies = [
"cty",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@@ -4012,7 +4010,7 @@ checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251"
dependencies = [
"block-buffer 0.9.0",
"digest 0.9.0",
"opaque-debug",
"opaque-debug 0.3.0",
]
[[package]]
@@ -4162,9 +4160,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1"
dependencies = [
"der",
"generic-array",
"generic-array 0.14.6",
"pkcs8",
"subtle",
"subtle 2.4.1",
"zeroize",
]
@@ -4207,7 +4205,7 @@ dependencies = [
"phf_codegen",
"precomputed-hash",
"servo_arc",
"smallvec 1.10.0",
"smallvec",
"thin-slice",
]
@@ -4396,7 +4394,7 @@ dependencies = [
"cfg-if",
"cpufeatures",
"digest 0.9.0",
"opaque-debug",
"opaque-debug 0.3.0",
]
[[package]]
@@ -4419,7 +4417,7 @@ dependencies = [
"block-buffer 0.9.0",
"digest 0.9.0",
"keccak",
"opaque-debug",
"opaque-debug 0.3.0",
]
[[package]]
@@ -4462,16 +4460,7 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
dependencies = [
"autocfg 1.1.0",
]
[[package]]
name = "smallvec"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
dependencies = [
"maybe-uninit",
"autocfg",
]
[[package]]
@@ -4518,6 +4507,30 @@ dependencies = [
"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]]
name = "spin"
version = "0.5.2"
@@ -4609,6 +4622,12 @@ dependencies = [
"syn",
]
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
[[package]]
name = "subtle"
version = "2.4.1"
@@ -4951,7 +4970,7 @@ dependencies = [
"serde_repr",
"sha2 0.9.9",
"signature",
"subtle",
"subtle 2.4.1",
"subtle-encoding",
"tendermint-proto",
"time",
@@ -5108,13 +5127,28 @@ dependencies = [
"time-core",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"bytes",
"libc",
"memchr",
@@ -5261,7 +5295,7 @@ dependencies = [
"once_cell",
"regex",
"sharded-slab",
"smallvec 1.10.0",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
@@ -5344,11 +5378,11 @@ checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-normalization"
version = "0.1.9"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"smallvec 0.6.14",
"tinyvec",
]
[[package]]
@@ -5369,8 +5403,8 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
"generic-array",
"subtle",
"generic-array 0.14.6",
"subtle 2.4.1",
]
[[package]]
@@ -6029,6 +6063,17 @@ dependencies = [
"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]]
name = "xattr"
version = "0.2.3"
@@ -6044,6 +6089,7 @@ version = "1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
dependencies = [
"serde",
"zeroize_derive",
]
@@ -10,7 +10,7 @@ aes-gcm = "0.9"
anyhow = "1.0"
argon2 = "0.4"
base64 = "0.13"
bip39 = "1.0"
bip39 = { version = "2.0.0", features = ["zeroize"] }
clap = { version = "4.0", features = ["derive"] }
log = "0.4"
pretty_env_logger = "0.4"
+1 -1
View File
@@ -11,7 +11,7 @@ serde_json = "1.0"
strum = { version = "0.23", features = ["derive"] }
ts-rs = "6.1.2"
cosmwasm-std = "1.0.0-beta8"
cosmwasm-std = "1.0.0"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
nym-config = { path = "../../common/config" }
+7 -3
View File
@@ -19,7 +19,8 @@ tauri-codegen = "=1.2.1"
tauri-macros = "=1.2.1"
[dependencies]
bip39 = "1.0"
async-trait = "0.1.64"
bip39 = { version = "2.0.0", features = ["zeroize", "rand"] }
cfg-if = "1.0.0"
colored = "2.0"
dirs = "4.0"
@@ -31,7 +32,6 @@ itertools = "0.10"
log = { version = "0.4", features = ["serde"] }
once_cell = "1.7.2"
pretty_env_logger = "0.4"
rand = "0.6.5"
reqwest = {version = "0.11.9", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
@@ -49,7 +49,7 @@ k256 = { version = "0.10", features = ["ecdsa", "sha256"] }
aes-gcm = "0.9.4"
argon2 = { version = "0.3.2", features = ["std"] }
base64 = "0.13"
zeroize = "1.4.3"
zeroize = { version = "1.5", features = ["zeroize_derive", "serde"] }
cosmwasm-std = "1.0.0"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
@@ -57,6 +57,8 @@ cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegr
validator-client = { path = "../../common/client-libs/validator-client", features = [
"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-vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" }
nym-config = { path = "../../common/config" }
@@ -67,6 +69,8 @@ nym-types = { path = "../../common/types" }
nym-wallet-types = { path = "../nym-wallet-types" }
[dev-dependencies]
nym-crypto = { path = "../../common/crypto", features = ["rand"] }
rand_chacha = "0.2"
tempfile = "3.3.0"
ts-rs = "6.1.2"
+10
View File
@@ -1,3 +1,5 @@
use nym_contracts_common::signing::SigningAlgorithm;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_types::error::TypesError;
use nym_wallet_types::network::Network;
use serde::{Serialize, Serializer};
@@ -122,6 +124,14 @@ pub enum BackendError {
#[error("Unable to open a new window")]
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.")]
RemovedCommand { name: String, alternative: String },
}
+4
View File
@@ -169,6 +169,10 @@ fn main() {
simulate::mixnet::simulate_claim_operator_reward,
signatures::sign::sign,
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,
])
.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,15 +2,14 @@ use crate::config::{Config, CUSTOM_SIMULATED_GAS_MULTIPLIER};
use crate::error::BackendError;
use crate::network_config;
use crate::state::{WalletAccountIds, WalletState};
use crate::utils::{SensitiveStringWrapper, ZeroizeMnemonicWrapper};
use crate::wallet_storage::{self, UserPassword, DEFAULT_LOGIN_ID};
use bip39::rand::{self, seq::SliceRandom};
use bip39::{Language, Mnemonic};
use cosmrs::bip32::DerivationPath;
use itertools::Itertools;
use nym_config::defaults::{NymNetworkDetails, COSMOS_DERIVATION_PATH};
use nym_types::account::{Account, AccountEntry, Balance};
use nym_wallet_types::network::Network as WalletNetwork;
use rand::seq::SliceRandom;
use std::collections::HashMap;
use strum::IntoEnumIterator;
use url::Url;
@@ -19,10 +18,9 @@ use validator_client::{nyxd::SigningNyxdClient, Client};
#[tauri::command]
pub async fn connect_with_mnemonic(
mnemonic: SensitiveStringWrapper,
mnemonic: Mnemonic,
state: tauri::State<'_, WalletState>,
) -> Result<Account, BackendError> {
let mnemonic = ZeroizeMnemonicWrapper::try_from_string(mnemonic)?;
_connect_with_mnemonic(mnemonic, state).await
}
@@ -48,13 +46,13 @@ pub async fn get_balance(state: tauri::State<'_, WalletState>) -> Result<Balance
}
#[tauri::command]
pub fn create_new_mnemonic() -> SensitiveStringWrapper {
random_mnemonic().into_string()
pub fn create_new_mnemonic() -> Mnemonic {
random_mnemonic()
}
#[tauri::command]
pub fn validate_mnemonic(mnemonic: SensitiveStringWrapper) -> bool {
ZeroizeMnemonicWrapper::try_from_string(mnemonic).is_ok()
pub fn validate_mnemonic(_mnemonic: Mnemonic) -> bool {
true
}
#[tauri::command]
@@ -82,15 +80,13 @@ pub async fn logout(state: tauri::State<'_, WalletState>) -> Result<(), BackendE
Ok(())
}
fn random_mnemonic() -> ZeroizeMnemonicWrapper {
fn random_mnemonic() -> Mnemonic {
let mut rng = rand::thread_rng();
Mnemonic::generate_in_with(&mut rng, Language::English, 24)
.unwrap()
.into()
Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap()
}
async fn _connect_with_mnemonic(
mnemonic: ZeroizeMnemonicWrapper,
mnemonic: Mnemonic,
state: tauri::State<'_, WalletState>,
) -> Result<Account, BackendError> {
{
@@ -141,7 +137,7 @@ async fn _connect_with_mnemonic(
&default_nyxd_urls,
&default_api_urls,
&config,
mnemonic.as_ref(),
&mnemonic,
)?;
// Set the default account
@@ -298,16 +294,12 @@ pub fn does_password_file_exist() -> Result<bool, BackendError> {
}
#[tauri::command]
pub fn create_password(
mnemonic: SensitiveStringWrapper,
password: UserPassword,
) -> Result<(), BackendError> {
pub fn create_password(mnemonic: Mnemonic, password: UserPassword) -> Result<(), BackendError> {
if does_password_file_exist()? {
return Err(BackendError::WalletFileAlreadyExists);
}
log::info!("Creating password");
let mnemonic = ZeroizeMnemonicWrapper::try_from_string(mnemonic)?;
let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap();
// Currently we only support a single, default, login id in the wallet
let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string());
@@ -330,7 +322,7 @@ pub async fn sign_in_with_password(
set_state_with_all_accounts(stored_login, first_login_id_when_converting, state.clone())
.await?;
_connect_with_mnemonic(mnemonic.into(), state).await
_connect_with_mnemonic(mnemonic, state).await
}
fn extract_first_mnemonic(
@@ -370,7 +362,7 @@ pub async fn sign_in_with_password_and_account_id(
set_state_with_all_accounts(stored_login, first_login_id_when_converting, state.clone())
.await?;
_connect_with_mnemonic(mnemonic.into(), state).await
_connect_with_mnemonic(mnemonic, state).await
}
fn extract_mnemonic(
@@ -404,13 +396,12 @@ pub fn archive_wallet_file() -> Result<(), BackendError> {
#[tauri::command]
pub async fn add_account_for_password(
mnemonic: SensitiveStringWrapper,
mnemonic: Mnemonic,
password: UserPassword,
account_id: &str,
state: tauri::State<'_, WalletState>,
) -> Result<AccountEntry, BackendError> {
log::info!("Adding account for the current password: {account_id}");
let mnemonic = ZeroizeMnemonicWrapper::try_from_string(mnemonic)?;
let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap();
// Currently we only support a single, default, login id in the wallet
let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string());
@@ -427,13 +418,7 @@ pub async fn add_account_for_password(
let address = {
let state = state.read().await;
let network: NymNetworkDetails = state.current_network().into();
// safety: the call to `clone_inner` is fine here as the raw mnemonic will get immediately
// passed to `DirectSecp256k1HdWallet` which will zeroize it on drop
derive_address(
mnemonic.into_cloned_inner(),
&network.chain_details.bech32_account_prefix,
)?
.to_string()
derive_address(mnemonic, &network.chain_details.bech32_account_prefix)?.to_string()
};
// Re-read all the acccounts from the wallet to reset the state, rather than updating it
@@ -553,19 +538,19 @@ pub async fn list_accounts(
pub fn show_mnemonic_for_account_in_password(
account_id: String,
password: UserPassword,
) -> Result<SensitiveStringWrapper, BackendError> {
) -> Result<Mnemonic, BackendError> {
log::info!("Getting mnemonic for: {account_id}");
let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string());
let account_id = wallet_storage::AccountId::new(account_id);
let mnemonic = _show_mnemonic_for_account_in_password(&login_id, &account_id, &password)?;
Ok(mnemonic.into_string())
Ok(mnemonic)
}
fn _show_mnemonic_for_account_in_password(
login_id: &wallet_storage::LoginId,
account_id: &wallet_storage::AccountId,
password: &wallet_storage::UserPassword,
) -> Result<ZeroizeMnemonicWrapper, BackendError> {
) -> Result<Mnemonic, BackendError> {
let stored_account = wallet_storage::load_existing_login(login_id, password)?;
let mnemonic = match stored_account {
wallet_storage::StoredLogin::Mnemonic(ref account) => account.mnemonic().clone(),
@@ -575,7 +560,7 @@ fn _show_mnemonic_for_account_in_password(
.mnemonic()
.clone(),
};
Ok(mnemonic.into())
Ok(mnemonic)
}
#[cfg(test)]
@@ -590,8 +575,8 @@ mod tests {
use super::*;
// This decryptes a stored wallet file using the same procedure as when signing in. Most tests
// related to the encryped wallet storage is in `wallet_storage`.
// This decrypts a stored wallet file using the same procedure as when signing in. Most tests
// related to the encrypted wallet storage is in `wallet_storage`.
#[test]
fn decrypt_stored_wallet_for_sign_in() {
const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json";
@@ -2,8 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::BackendError;
use crate::operations::helpers::{
verify_gateway_bonding_sign_payload, verify_mixnode_bonding_sign_payload,
};
use crate::state::WalletState;
use crate::{nyxd_client, Gateway, MixNode};
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::{MixId, MixNodeConfigUpdate};
use nym_types::currency::DecCoin;
use nym_types::gateway::GatewayBond;
@@ -26,7 +30,7 @@ pub struct NodeDescription {
pub async fn bond_gateway(
gateway: Gateway,
pledge: DecCoin,
owner_signature: String,
msg_signature: MessageSignature,
fee: Option<Fee>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
@@ -41,10 +45,20 @@ pub async fn bond_gateway(
pledge_base,
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
.bond_gateway(gateway, owner_signature, pledge_base, fee)
.bond_gateway(gateway, msg_signature, pledge_base, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
@@ -73,7 +87,7 @@ pub async fn unbond_gateway(
pub async fn bond_mixnode(
mixnode: MixNode,
cost_params: MixNodeCostParams,
owner_signature: String,
msg_signature: MessageSignature,
pledge: DecCoin,
fee: Option<Fee>,
state: tauri::State<'_, WalletState>,
@@ -90,10 +104,26 @@ pub async fn bond_mixnode(
pledge_base,
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
.bond_mixnode(mixnode, cost_params, owner_signature, pledge_base, fee)
.bond_mixnode(mixnode, cost_params, msg_signature, pledge_base, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
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(crate) mod helpers;
pub mod mixnet;
pub mod nym_api;
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
}

Some files were not shown because too many files have changed in this diff Show More