Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f3279ab3da | |||
| 8f5e6992c1 | |||
| 76a53c8ac9 | |||
| 7ff0becf72 | |||
| 4c2967a733 | |||
| 2b80e5d1c9 | |||
| 4e7ff53214 | |||
| 9ec36e49b7 | |||
| dd13073037 | |||
| 1010df1077 | |||
| 9eaf9cf491 | |||
| 8e96318478 | |||
| 54287666e8 | |||
| 6de829163d | |||
| adb5ed7c30 | |||
| 2b019e57df | |||
| 30c07712e3 | |||
| 82c92501d9 | |||
| c2a871a1a7 | |||
| dfd7bd5889 | |||
| 7ff043d8df | |||
| c904d245d2 |
@@ -80,7 +80,7 @@ jobs:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install --version 0.110.0 wasm-opt
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Build release contracts
|
||||
run: make wasm
|
||||
@@ -99,14 +99,9 @@ jobs:
|
||||
cp target/release/nym-network-statistics $OUTPUT_DIR
|
||||
cp target/release/nym-cli $OUTPUT_DIR
|
||||
cp target/release/credential $OUTPUT_DIR
|
||||
cp target/release/explorer-api $OUTPUT_DIR
|
||||
|
||||
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_bandwidth.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
|
||||
|
||||
- name: Deploy branch to CI www
|
||||
continue-on-error: true
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install --version 0.110.0 wasm-opt
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Build release contracts
|
||||
run: make wasm
|
||||
|
||||
@@ -39,5 +39,7 @@ validator-api-config.toml
|
||||
dist
|
||||
storybook-static
|
||||
envs/qwerty.env
|
||||
Cargo.lock
|
||||
nym-connect/Cargo.lock
|
||||
.parcel-cache
|
||||
**/.DS_Store
|
||||
|
||||
@@ -4,26 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v1.1.13] (2023-03-15)
|
||||
|
||||
- NE - instead of throwing a "Mixnode/Gateway not found" error for blacklisted nodes due to bad performance, show their history but tag them as "Having poor performance" ([#2979])
|
||||
- NE - Upgrade Sandbox and make below changes: ([#2332])
|
||||
- Explorer - Updates ([#3168])
|
||||
- Fix contracts and nym-api audit findings ([#3026])
|
||||
- Website v2 - deploy infrastructure for strapi and CI ([#2213])
|
||||
- add blockstream green to sp list ([#3180])
|
||||
- mock-nym-api: fix .storybook lint error ([#3178])
|
||||
- Validating new interval config parameters to prevent division by zero ([#3153])
|
||||
|
||||
[#2979]: https://github.com/nymtech/nym/issues/2979
|
||||
[#2332]: https://github.com/nymtech/nym/issues/2332
|
||||
[#3168]: https://github.com/nymtech/nym/issues/3168
|
||||
[#3026]: https://github.com/nymtech/nym/issues/3026
|
||||
[#2213]: https://github.com/nymtech/nym/issues/2213
|
||||
[#3180]: https://github.com/nymtech/nym/pull/3180
|
||||
[#3178]: https://github.com/nymtech/nym/pull/3178
|
||||
[#3153]: https://github.com/nymtech/nym/pull/3153
|
||||
|
||||
## [v1.1.12] (2023-03-07)
|
||||
|
||||
- Fix generated docs for mixnet and vesting contract on docs.rs ([#3093])
|
||||
|
||||
Generated
+50
-56
@@ -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"
|
||||
@@ -646,7 +647,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "client-core"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"dashmap 5.4.0",
|
||||
@@ -855,9 +856,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-derive"
|
||||
version = "1.0.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4"
|
||||
checksum = "d5abeeb891e6d0098402e4d3d042f90451db52651d2fe14b170e69a1dd3e4115"
|
||||
dependencies = [
|
||||
"syn",
|
||||
]
|
||||
@@ -1612,7 +1613,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.1.4",
|
||||
@@ -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"
|
||||
@@ -3062,7 +3057,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.14"
|
||||
version = "1.1.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3140,17 +3135,9 @@ dependencies = [
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-bandwidth-claim-contract"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-bin-common"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"clap 4.1.4",
|
||||
"clap_complete",
|
||||
@@ -3179,7 +3166,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
@@ -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",
|
||||
@@ -3237,7 +3225,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
dependencies = [
|
||||
"clap 4.1.4",
|
||||
"client-core",
|
||||
@@ -3319,7 +3307,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -3412,7 +3400,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3479,7 +3467,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -3489,7 +3477,7 @@ dependencies = [
|
||||
"rand_chacha 0.3.1",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde-json-wasm",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"time 0.3.17",
|
||||
@@ -3498,7 +3486,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.14"
|
||||
version = "1.1.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atty",
|
||||
@@ -3515,6 +3503,7 @@ dependencies = [
|
||||
"mixnode-common",
|
||||
"nym-bin-common",
|
||||
"nym-config",
|
||||
"nym-contracts-common",
|
||||
"nym-crypto",
|
||||
"nym-nonexhaustive-delayqueue",
|
||||
"nym-pemstore",
|
||||
@@ -3566,7 +3555,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"clap 4.1.4",
|
||||
@@ -3604,7 +3593,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-statistics"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"log",
|
||||
@@ -3704,7 +3693,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
dependencies = [
|
||||
"clap 4.1.4",
|
||||
"client-core",
|
||||
@@ -3967,7 +3956,6 @@ dependencies = [
|
||||
name = "nym-vesting-contract"
|
||||
version = "1.2.0-pre.1"
|
||||
dependencies = [
|
||||
"cosmwasm-derive",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw2",
|
||||
@@ -3983,7 +3971,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"nym-contracts-common",
|
||||
@@ -4178,7 +4166,7 @@ dependencies = [
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec 1.10.0",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -4191,7 +4179,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec 1.10.0",
|
||||
"smallvec",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
@@ -5064,7 +5052,7 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"ref-cast",
|
||||
"serde",
|
||||
"smallvec 1.10.0",
|
||||
"smallvec",
|
||||
"stable-pattern",
|
||||
"state",
|
||||
"time 0.3.17",
|
||||
@@ -5559,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"
|
||||
@@ -5732,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",
|
||||
@@ -5780,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",
|
||||
@@ -6205,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"
|
||||
@@ -6502,7 +6496,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"regex",
|
||||
"sharded-slab",
|
||||
"smallvec 1.10.0",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
@@ -6668,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
-11
@@ -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,17 +105,8 @@ license = "Apache-2.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
async-trait = "0.1.64"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
cfg-if = "1.0.0"
|
||||
cosmwasm-derive = "=1.0.0"
|
||||
cosmwasm-schema = "=1.0.0"
|
||||
cosmwasm-std = "=1.0.0"
|
||||
cosmwasm-storage = "=1.0.0"
|
||||
cw-utils = "=0.13.4"
|
||||
cw-storage-plus = "=0.13.4"
|
||||
cw2 = { version = "=0.13.4" }
|
||||
cw3 = { version = "=0.13.4" }
|
||||
cw3-fixed-multisig = { version = "=0.13.4" }
|
||||
cw4 = { version = "=0.13.4" }
|
||||
dotenvy = "0.15.6"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "client-core"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.66"
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.13"
|
||||
version = "1.1.12"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-bin-common"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
description = "Common code for nym binaries"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
|
||||
@@ -37,17 +37,17 @@ 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 = { workspace = true, optional = true }
|
||||
cw4 = { workspace = true, optional = true }
|
||||
cw3 = { version = "0.13.4", optional = true }
|
||||
cw4 = { version = "0.13.4", optional = true }
|
||||
prost = { version = "0.10", default-features = false, optional = true }
|
||||
flate2 = { version = "1.0.20", optional = true }
|
||||
sha2 = { version = "0.9.5", optional = true }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
cosmwasm-std = { workspace = true, optional = true }
|
||||
zeroize = { version = "1.5.7", optional = true }
|
||||
cosmwasm-std = { version = "1.0.0", optional = true }
|
||||
zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
ts-rs = "6.1.2"
|
||||
|
||||
@@ -749,12 +749,6 @@ where
|
||||
Ok(self.nym_api.get_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_mixnodes_detailed_unfiltered().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
|
||||
@@ -144,21 +144,6 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
routes::MIXNODES,
|
||||
routes::DETAILED_UNFILTERED,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
.await
|
||||
|
||||
@@ -8,7 +8,6 @@ pub const MIXNODES: &str = "mixnodes";
|
||||
pub const GATEWAYS: &str = "gateways";
|
||||
|
||||
pub const DETAILED: &str = "detailed";
|
||||
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
|
||||
pub const ACTIVE: &str = "active";
|
||||
pub const REWARDED: &str = "rewarded";
|
||||
pub const COCONUT_ROUTES: &str = "coconut";
|
||||
|
||||
@@ -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,8 @@ 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::gateway::GatewayConfigUpdate;
|
||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
|
||||
use nym_mixnet_contract_common::{
|
||||
@@ -290,7 +292,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 +313,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 +444,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 +463,7 @@ pub trait MixnetSigningClient {
|
||||
&self,
|
||||
owner: AccountId,
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
@@ -497,6 +499,36 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_gateway_config(
|
||||
&self,
|
||||
new_config: GatewayConfigUpdate,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateGatewayConfig { new_config },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_gateway_config_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
new_config: GatewayConfigUpdate,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateGatewayConfigOnBehalf {
|
||||
new_config,
|
||||
owner: owner.to_string(),
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// delegation-related:
|
||||
|
||||
async fn delegate_to_mixnode(
|
||||
|
||||
@@ -6,6 +6,8 @@ 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::gateway::GatewayConfigUpdate;
|
||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
|
||||
use nym_vesting_contract_common::messages::{
|
||||
@@ -34,6 +36,12 @@ pub trait VestingSigningClient {
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
|
||||
async fn vesting_update_gateway_config(
|
||||
&self,
|
||||
new_config: GatewayConfigUpdate,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
|
||||
async fn update_mixnet_address(
|
||||
&self,
|
||||
address: &str,
|
||||
@@ -43,7 +51,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 +69,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>;
|
||||
@@ -184,6 +192,19 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_update_gateway_config(
|
||||
&self,
|
||||
new_config: GatewayConfigUpdate,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_vesting_contract(
|
||||
fee,
|
||||
VestingExecuteMsg::UpdateGatewayConfig { new_config },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_mixnet_address(
|
||||
&self,
|
||||
address: &str,
|
||||
@@ -208,14 +229,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 +293,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 +302,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)
|
||||
|
||||
@@ -6,12 +6,12 @@ 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"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
cw-utils = { workspace = true }
|
||||
cw-utils = { version = "0.13.4" }
|
||||
handlebars = "3.0.1"
|
||||
humantime-serde = "1.0"
|
||||
k256 = { version = "0.10", features = ["ecdsa", "sha256"] }
|
||||
@@ -26,10 +26,11 @@ url = "2.2"
|
||||
tap = "1"
|
||||
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
cosmwasm-std = { workspace = true }
|
||||
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" }
|
||||
|
||||
@@ -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,
|
||||
|
||||
+81
@@ -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,8 @@
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod bond_gateway;
|
||||
pub mod gateway_bonding_sign_payload;
|
||||
pub mod settings;
|
||||
pub mod unbond_gateway;
|
||||
pub mod vesting_bond_gateway;
|
||||
pub mod vesting_unbond_gateway;
|
||||
@@ -17,12 +19,16 @@ pub struct MixnetOperatorsGateway {
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsGatewayCommands {
|
||||
/// Manage your gateway settings stored in the directory
|
||||
Settings(settings::MixnetOperatorsGatewaySettings),
|
||||
/// 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),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod update_config;
|
||||
pub mod vesting_update_config;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
pub struct MixnetOperatorsGatewaySettings {
|
||||
#[clap(subcommand)]
|
||||
pub command: MixnetOperatorsGatewaySettingsCommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsGatewaySettingsCommands {
|
||||
/// Update gateway configuration
|
||||
UpdateConfig(update_config::Args),
|
||||
/// Update gateway configuration for a gateway bonded with locked tokens
|
||||
VestingUpdateConfig(vesting_update_config::Args),
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2023 - 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::GatewayConfigUpdate;
|
||||
use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub host: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub clients_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub location: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn update_config(args: Args, client: SigningClient) {
|
||||
info!("Update gateway config!");
|
||||
|
||||
let current_details = match client
|
||||
.get_owned_gateway(client.address())
|
||||
.await
|
||||
.expect("failed to query the chain for gateway details")
|
||||
.gateway
|
||||
{
|
||||
Some(details) => details,
|
||||
None => {
|
||||
log::warn!("this operator does not own a gateway to update");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let update = GatewayConfigUpdate {
|
||||
host: args.host.unwrap_or(current_details.gateway.host),
|
||||
mix_port: args.mix_port.unwrap_or(current_details.gateway.mix_port),
|
||||
clients_port: args
|
||||
.clients_port
|
||||
.unwrap_or(current_details.gateway.clients_port),
|
||||
location: args.location.unwrap_or(current_details.gateway.location),
|
||||
version: args.version.unwrap_or(current_details.gateway.version),
|
||||
};
|
||||
|
||||
let res = client
|
||||
.update_gateway_config(update, None)
|
||||
.await
|
||||
.expect("updating gateway config");
|
||||
|
||||
info!("gateway config updated: {:?}", res)
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright 2023 - 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::GatewayConfigUpdate;
|
||||
use validator_client::nyxd::traits::MixnetQueryClient;
|
||||
use validator_client::nyxd::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub host: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub clients_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub location: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn vesting_update_config(client: SigningClient, args: Args) {
|
||||
info!("Update vesting gateway config!");
|
||||
|
||||
let current_details = match client
|
||||
.get_owned_gateway(client.address())
|
||||
.await
|
||||
.expect("failed to query the chain for gateway details")
|
||||
.gateway
|
||||
{
|
||||
Some(details) => details,
|
||||
None => {
|
||||
log::warn!("this operator does not own a gateway to update");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let update = GatewayConfigUpdate {
|
||||
host: args.host.unwrap_or(current_details.gateway.host),
|
||||
mix_port: args.mix_port.unwrap_or(current_details.gateway.mix_port),
|
||||
clients_port: args
|
||||
.clients_port
|
||||
.unwrap_or(current_details.gateway.clients_port),
|
||||
location: args.location.unwrap_or(current_details.gateway.location),
|
||||
version: args.version.unwrap_or(current_details.gateway.version),
|
||||
};
|
||||
|
||||
let res = client
|
||||
.vesting_update_gateway_config(update, None)
|
||||
.await
|
||||
.expect("updating vesting gateway config");
|
||||
|
||||
info!("gateway config updated: {:?}", 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, 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>,
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use crate::utils::account_id_to_cw_addr;
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::{Coin, Uint128};
|
||||
use nym_contracts_common::Percent;
|
||||
use nym_mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNodeCostParams};
|
||||
use nym_network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
};
|
||||
use validator_client::nyxd::traits::MixnetQueryClient;
|
||||
use validator_client::nyxd::CosmWasmCoin;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub host: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub verloc_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub http_api_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub sphinx_key: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub version: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub interval_operating_cost: Option<u128>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub amount: u128,
|
||||
|
||||
/// Indicates whether the mixnode is going to get bonded via a vesting account
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
}
|
||||
|
||||
pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
let mixnode = nym_mixnet_contract_common::MixNode {
|
||||
host: args.host,
|
||||
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
|
||||
verloc_port: args.verloc_port.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT),
|
||||
http_api_port: args
|
||||
.http_api_port
|
||||
.unwrap_or(DEFAULT_HTTP_API_LISTENING_PORT),
|
||||
sphinx_key: args.sphinx_key,
|
||||
identity_key: args.identity_key,
|
||||
version: args.version,
|
||||
};
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(
|
||||
args.profit_margin_percent.unwrap_or(10) as u64,
|
||||
)
|
||||
.unwrap(),
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
|
||||
},
|
||||
};
|
||||
|
||||
let nonce = match client.get_signing_nonce(client.address()).await {
|
||||
Ok(nonce) => nonce,
|
||||
Err(err) => {
|
||||
eprint!(
|
||||
"failed to query for the signing nonce of {}: {err}",
|
||||
client.address()
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let address = account_id_to_cw_addr(client.address());
|
||||
let proxy = if args.with_vesting_account {
|
||||
Some(account_id_to_cw_addr(client.vesting_contract_address()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload =
|
||||
construct_mixnode_bonding_sign_payload(nonce, address, proxy, coin, mixnode, cost_params);
|
||||
println!("{}", payload.to_base58_string().unwrap())
|
||||
}
|
||||
@@ -5,6 +5,7 @@ use clap::{Args, Subcommand};
|
||||
|
||||
pub mod bond_mixnode;
|
||||
pub mod 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!");
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-std = "1.0.0"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
nym-multisig-contract-common = { path = "../multisig-contract" }
|
||||
|
||||
@@ -6,8 +6,8 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
cosmwasm-std = "1.0.0"
|
||||
cw-utils = "0.13.4"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
description = "Common library for Nym cosmwasm contracts"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
@@ -9,7 +9,7 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
bs58 = "0.4.0"
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-std = "1.0.0"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
|
||||
@@ -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)?)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,6 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cw4 = { workspace = true }
|
||||
cw4 = { version = "0.13.4" }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
description = "Common library for the Nym mixnet contract"
|
||||
rust-version = "1.62"
|
||||
edition = { workspace = true }
|
||||
@@ -10,13 +10,15 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
bs58 = "0.4.0"
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-std = "=1.0.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_repr = "0.1"
|
||||
schemars = "0.8"
|
||||
thiserror = "1.0"
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.3.0" }
|
||||
serde_json = "1.0.0"
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.2.0" }
|
||||
# use 0.4.1 as that's the version used by cosmwasm-std 1.0.0
|
||||
# (and ideally we don't want to pull the same dependency twice)
|
||||
serde-json-wasm = "=0.4.1"
|
||||
humantime-serde = "1.1.1"
|
||||
|
||||
# TO CHECK WHETHER STILL NEEDED:
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::gateway::GatewayConfigUpdate;
|
||||
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use crate::reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate};
|
||||
use crate::rewarding::RewardDistribution;
|
||||
@@ -42,6 +43,7 @@ pub enum MixnetEventType {
|
||||
ReconcilePendingEvents,
|
||||
PendingIntervalConfigUpdate,
|
||||
IntervalConfigUpdate,
|
||||
GatewayConfigUpdate,
|
||||
}
|
||||
|
||||
impl From<MixnetEventType> for String {
|
||||
@@ -86,6 +88,7 @@ impl ToString for MixnetEventType {
|
||||
MixnetEventType::PendingIntervalConfigUpdate => "pending_interval_config_update",
|
||||
MixnetEventType::IntervalConfigUpdate => "interval_config_update",
|
||||
MixnetEventType::DelegationOnUnbonding => "delegation_on_unbonding_node",
|
||||
MixnetEventType::GatewayConfigUpdate => "gateway_config_update",
|
||||
};
|
||||
|
||||
format!("{EVENT_VERSION_PREFIX}{event_name}")
|
||||
@@ -122,6 +125,7 @@ pub const OLD_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "old_rewarding_validator_a
|
||||
pub const NEW_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "new_rewarding_validator_address";
|
||||
|
||||
pub const UPDATED_MIXNODE_CONFIG_KEY: &str = "updated_mixnode_config";
|
||||
pub const UPDATED_GATEWAY_CONFIG_KEY: &str = "updated_gateway_config";
|
||||
pub const UPDATED_MIXNODE_COST_PARAMS_KEY: &str = "updated_mixnode_cost_params";
|
||||
|
||||
// rewarding
|
||||
@@ -382,6 +386,17 @@ pub fn new_mixnode_config_update_event(
|
||||
.add_attribute(UPDATED_MIXNODE_CONFIG_KEY, update.to_inline_json())
|
||||
}
|
||||
|
||||
pub fn new_gateway_config_update_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
update: &GatewayConfigUpdate,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::GatewayConfigUpdate)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
|
||||
.add_attribute(UPDATED_GATEWAY_CONFIG_KEY, update.to_inline_json())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_pending_cost_params_update_event(
|
||||
mix_id: MixId,
|
||||
owner: &Addr,
|
||||
|
||||
@@ -112,6 +112,26 @@ impl Display for GatewayBond {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/GatewayConfigUpdate.ts")
|
||||
)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct GatewayConfigUpdate {
|
||||
pub host: String,
|
||||
pub mix_port: u16,
|
||||
pub clients_port: u16,
|
||||
pub location: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl GatewayConfigUpdate {
|
||||
pub fn to_inline_json(&self) -> String {
|
||||
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedGatewayResponse {
|
||||
pub nodes: Vec<GatewayBond>,
|
||||
|
||||
@@ -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::*;
|
||||
@@ -26,7 +27,8 @@ pub use delegation::{
|
||||
PagedMixNodeDelegationsResponse,
|
||||
};
|
||||
pub use gateway::{
|
||||
Gateway, GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, PagedGatewayResponse,
|
||||
Gateway, GatewayBond, GatewayBondResponse, GatewayConfigUpdate, GatewayOwnershipResponse,
|
||||
PagedGatewayResponse,
|
||||
};
|
||||
pub use interval::{
|
||||
CurrentIntervalResponse, EpochState, EpochStatus, Interval, NumberOfPendingEventsResponse,
|
||||
@@ -43,4 +45,5 @@ pub use pending_events::{
|
||||
PendingIntervalEventData, PendingIntervalEventKind,
|
||||
};
|
||||
pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams};
|
||||
pub use signing_types::*;
|
||||
pub use types::*;
|
||||
|
||||
@@ -542,7 +542,7 @@ pub struct MixNodeCostParams {
|
||||
|
||||
impl MixNodeCostParams {
|
||||
pub fn to_inline_json(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,7 +636,7 @@ pub struct MixNodeConfigUpdate {
|
||||
|
||||
impl MixNodeConfigUpdate {
|
||||
pub fn to_inline_json(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::delegation::OwnerProxySubKey;
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::gateway::GatewayConfigUpdate;
|
||||
use crate::helpers::IntoBaseDecimal;
|
||||
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use crate::reward_params::{
|
||||
@@ -10,6 +11,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 +155,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,17 +189,24 @@ 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 {
|
||||
owner: String,
|
||||
},
|
||||
UpdateGatewayConfig {
|
||||
new_config: GatewayConfigUpdate,
|
||||
},
|
||||
UpdateGatewayConfigOnBehalf {
|
||||
new_config: GatewayConfigUpdate,
|
||||
owner: String,
|
||||
},
|
||||
|
||||
// delegation-related:
|
||||
DelegateToMixnode {
|
||||
@@ -312,6 +321,10 @@ impl ExecuteMsg {
|
||||
}
|
||||
ExecuteMsg::UnbondGateway { .. } => "unbonding gateway".into(),
|
||||
ExecuteMsg::UnbondGatewayOnBehalf { .. } => "unbonding gateway on behalf".into(),
|
||||
ExecuteMsg::UpdateGatewayConfig { .. } => "updating gateway configuration".into(),
|
||||
ExecuteMsg::UpdateGatewayConfigOnBehalf { .. } => {
|
||||
"updating gateway configuration on behalf".into()
|
||||
}
|
||||
ExecuteMsg::DelegateToMixnode { mix_id } => format!("delegating to mixnode {mix_id}"),
|
||||
ExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, .. } => {
|
||||
format!("delegating to mixnode {mix_id} on behalf")
|
||||
@@ -500,6 +513,11 @@ pub enum QueryMsg {
|
||||
start_after: Option<u32>,
|
||||
},
|
||||
GetNumberOfPendingEvents {},
|
||||
|
||||
// signing-related
|
||||
GetSigningNonce {
|
||||
address: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
|
||||
@@ -70,7 +70,7 @@ pub struct IntervalRewardParams {
|
||||
|
||||
impl IntervalRewardParams {
|
||||
pub fn to_inline_json(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,6 +282,6 @@ impl IntervalRewardingParamsUpdate {
|
||||
}
|
||||
|
||||
pub fn to_inline_json(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -6,11 +6,11 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cw-utils = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw3-fixed-multisig = { workspace = true, features = ["library"] }
|
||||
cw4 = { workspace= true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cw-utils = { version = "0.13.4" }
|
||||
cw3 = { version = "0.13.4" }
|
||||
cw3-fixed-multisig = { version = "0.13.4", features = ["library"] }
|
||||
cw4 = { version = "0.13.4" }
|
||||
cosmwasm-std = "1.0.0"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
description = "Common library for the Nym vesting contract"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
@@ -8,9 +8,9 @@ license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { workspace = true }
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.3.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.3.0" }
|
||||
cosmwasm-std = "1.0.0"
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.2.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.2.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
schemars = "0.8"
|
||||
ts-rs = {version = "6.1.2", optional = true}
|
||||
|
||||
@@ -17,6 +17,7 @@ pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding";
|
||||
pub const VESTING_PLEDGE_MORE_EVENT_TYPE: &str = "vesting_pledge_more";
|
||||
pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding";
|
||||
pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixnode_config";
|
||||
pub const VESTING_UPDATE_GATEWAY_CONFIG_EVENT_TYPE: &str = "vesting_update_gateway_config";
|
||||
pub const VESTING_UPDATE_MIXNODE_COST_PARAMS_EVENT_TYPE: &str =
|
||||
"vesting_update_mixnode_cost_params";
|
||||
|
||||
@@ -121,6 +122,10 @@ pub fn new_vesting_update_mixnode_config_event() -> Event {
|
||||
Event::new(VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_update_gateway_config_event() -> Event {
|
||||
Event::new(VESTING_UPDATE_GATEWAY_CONFIG_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_update_mixnode_cost_params_event() -> Event {
|
||||
Event::new(VESTING_UPDATE_MIXNODE_COST_PARAMS_EVENT_TYPE)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use contracts_common::signing::MessageSignature;
|
||||
use cosmwasm_std::{Coin, Timestamp};
|
||||
use mixnet_contract_common::{
|
||||
gateway::GatewayConfigUpdate,
|
||||
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
|
||||
Gateway, IdentityKey, MixId, MixNode,
|
||||
};
|
||||
@@ -118,7 +120,7 @@ pub enum ExecuteMsg {
|
||||
BondMixnode {
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
amount: Coin,
|
||||
},
|
||||
PledgeMore {
|
||||
@@ -131,7 +133,7 @@ pub enum ExecuteMsg {
|
||||
},
|
||||
BondGateway {
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
amount: Coin,
|
||||
},
|
||||
UnbondGateway {},
|
||||
@@ -139,6 +141,9 @@ pub enum ExecuteMsg {
|
||||
owner: String,
|
||||
amount: Coin,
|
||||
},
|
||||
UpdateGatewayConfig {
|
||||
new_config: GatewayConfigUpdate,
|
||||
},
|
||||
TransferOwnership {
|
||||
to_address: String,
|
||||
},
|
||||
@@ -178,6 +183,7 @@ impl ExecuteMsg {
|
||||
ExecuteMsg::BondGateway { .. } => "VestingExecuteMsg::BondGateway",
|
||||
ExecuteMsg::UnbondGateway { .. } => "VestingExecuteMsg::UnbondGateway",
|
||||
ExecuteMsg::TrackUnbondGateway { .. } => "VestingExecuteMsg::TrackUnbondGateway",
|
||||
ExecuteMsg::UpdateGatewayConfig { .. } => "VestingExecuteMsg::UpdateGatewayConfig",
|
||||
ExecuteMsg::TransferOwnership { .. } => "VestingExecuteMsg::TransferOwnership",
|
||||
ExecuteMsg::UpdateStakingAddress { .. } => "VestingExecuteMsg::UpdateStakingAddress",
|
||||
ExecuteMsg::UpdateLockedPledgeCap { .. } => "VestingExecuteMsg::UpdateLockedPledgeCap",
|
||||
|
||||
@@ -19,7 +19,7 @@ thiserror = "1.0"
|
||||
url = "2.2"
|
||||
ts-rs = "6.1.2"
|
||||
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = [
|
||||
|
||||
@@ -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: {})",
|
||||
|
||||
@@ -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: {})",
|
||||
|
||||
Generated
-2037
File diff suppressed because it is too large
Load Diff
@@ -27,18 +27,3 @@ codegen-units = 1
|
||||
panic = 'abort'
|
||||
incremental = false
|
||||
overflow-checks = true
|
||||
|
||||
[workspace.dependencies]
|
||||
cosmwasm-crypto = "=1.0.0"
|
||||
cosmwasm-derive = "=1.0.0"
|
||||
cosmwasm-schema = "=1.0.0"
|
||||
cosmwasm-std = "=1.0.0"
|
||||
cosmwasm-storage = "=1.0.0"
|
||||
cw-controllers = "=0.13.4"
|
||||
cw-multi-test = "=0.13.4"
|
||||
cw-storage-plus = "=0.13.4"
|
||||
cw-utils = "=0.13.4"
|
||||
cw2 = "=0.13.4"
|
||||
cw3 = "=0.13.4"
|
||||
cw3-fixed-multisig = "=0.13.4"
|
||||
cw4 = "=0.13.4"
|
||||
|
||||
@@ -9,14 +9,13 @@ 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" }
|
||||
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
cw-storage-plus = "0.13.4"
|
||||
cw-controllers = "0.13.4"
|
||||
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -11,18 +11,18 @@ crate-type = ["cdylib", "rlib"]
|
||||
[dependencies]
|
||||
nym-coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut-dkg" }
|
||||
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
cw-storage-plus = "0.13.4"
|
||||
cw-controllers = "0.13.4"
|
||||
cw4 = { version = "0.13.4" }
|
||||
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.23"
|
||||
|
||||
[dev-dependencies]
|
||||
cw-multi-test = { workspace = true }
|
||||
cw-multi-test = { version = "0.13.4" }
|
||||
cw4-group = { path = "../multisig/cw4-group" }
|
||||
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
|
||||
lazy_static = "1.4"
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -7,19 +7,18 @@ 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" }
|
||||
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
|
||||
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
cw3 = "0.13.4"
|
||||
cw4 = "0.13.4"
|
||||
cw-storage-plus = "0.13.4"
|
||||
cw-controllers = "0.13.4"
|
||||
cw-utils = "0.13.4"
|
||||
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
@@ -27,7 +26,7 @@ thiserror = "1.0.23"
|
||||
|
||||
nym-coconut-bandwidth = { path = "../coconut-bandwidth" }
|
||||
nym-coconut-dkg = { path = "../coconut-dkg" }
|
||||
cw-multi-test = { workspace = true }
|
||||
cw-multi-test = { version = "0.13.4" }
|
||||
cw3-flex-multisig = { path = "../multisig/cw3-flex-multisig" }
|
||||
cw4-group = { path = "../multisig/cw4-group" }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-mixnet-contract"
|
||||
version = "1.2.0"
|
||||
version = "1.2.0-pre.0"
|
||||
description = "Nym mixnet contract"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
@@ -22,15 +22,14 @@ name = "mixnet_contract"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.3.0" }
|
||||
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.3.0" }
|
||||
#nym-config = { path = "../../common/config"}
|
||||
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-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.2.0" }
|
||||
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
cosmwasm-derive = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
cw2 = { version = "0.13.4" }
|
||||
cw-storage-plus = "0.13.4"
|
||||
|
||||
bs58 = "0.4.0"
|
||||
schemars = "0.8"
|
||||
@@ -40,9 +39,8 @@ time = { version = "0.3", features = ["macros"] }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cosmwasm-schema = "1.0.0"
|
||||
rand_chacha = "0.2"
|
||||
#rand = "0.7"
|
||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -318,6 +318,14 @@ pub fn execute(
|
||||
ExecuteMsg::UnbondGatewayOnBehalf { owner } => {
|
||||
crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner)
|
||||
}
|
||||
ExecuteMsg::UpdateGatewayConfig { new_config } => {
|
||||
crate::gateways::transactions::try_update_gateway_config(deps, info, new_config)
|
||||
}
|
||||
ExecuteMsg::UpdateGatewayConfigOnBehalf { new_config, owner } => {
|
||||
crate::gateways::transactions::try_update_gateway_config_on_behalf(
|
||||
deps, info, new_config, owner,
|
||||
)
|
||||
}
|
||||
|
||||
// delegation-related:
|
||||
ExecuteMsg::DelegateToMixnode { mix_id } => {
|
||||
@@ -581,6 +589,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?)
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::storage;
|
||||
use cosmwasm_std::{Addr, Storage};
|
||||
use mixnet_contract_common::{error::MixnetContractError, GatewayBond};
|
||||
|
||||
pub(crate) fn must_get_gateway_bond_by_owner(
|
||||
store: &dyn Storage,
|
||||
owner: &Addr,
|
||||
) -> Result<GatewayBond, MixnetContractError> {
|
||||
Ok(storage::gateways()
|
||||
.idx
|
||||
.owner
|
||||
.item(store, owner.clone())?
|
||||
.ok_or(MixnetContractError::NoAssociatedGatewayBond {
|
||||
owner: owner.clone(),
|
||||
})?
|
||||
.1)
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod helpers;
|
||||
pub mod queries;
|
||||
pub mod signature_helpers;
|
||||
pub mod storage;
|
||||
pub mod transactions;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::helpers::must_get_gateway_bond_by_owner;
|
||||
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_proxy_match, 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::events::{
|
||||
new_gateway_bonding_event, new_gateway_config_update_event, new_gateway_unbonding_event,
|
||||
};
|
||||
use mixnet_contract_common::gateway::GatewayConfigUpdate;
|
||||
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 +24,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 +43,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 +60,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 +90,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(),
|
||||
@@ -180,41 +193,95 @@ pub(crate) fn _try_remove_gateway(
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) fn try_update_gateway_config(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
new_config: GatewayConfigUpdate,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
let owner = info.sender;
|
||||
_try_update_gateway_config(deps, new_config, owner, None)
|
||||
}
|
||||
|
||||
pub(crate) fn try_update_gateway_config_on_behalf(
|
||||
deps: DepsMut,
|
||||
info: MessageInfo,
|
||||
new_config: GatewayConfigUpdate,
|
||||
owner: String,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
ensure_sent_by_vesting_contract(&info, deps.storage)?;
|
||||
|
||||
let owner = deps.api.addr_validate(&owner)?;
|
||||
let proxy = info.sender;
|
||||
_try_update_gateway_config(deps, new_config, owner, Some(proxy))
|
||||
}
|
||||
|
||||
pub(crate) fn _try_update_gateway_config(
|
||||
deps: DepsMut,
|
||||
new_config: GatewayConfigUpdate,
|
||||
owner: Addr,
|
||||
proxy: Option<Addr>,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
let existing_bond = must_get_gateway_bond_by_owner(deps.storage, &owner)?;
|
||||
ensure_proxy_match(&proxy, &existing_bond.proxy)?;
|
||||
|
||||
let cfg_update_event = new_gateway_config_update_event(&owner, &proxy, &new_config);
|
||||
|
||||
let mut updated_bond = existing_bond.clone();
|
||||
updated_bond.gateway.host = new_config.host;
|
||||
updated_bond.gateway.mix_port = new_config.mix_port;
|
||||
updated_bond.gateway.clients_port = new_config.clients_port;
|
||||
updated_bond.gateway.location = new_config.location;
|
||||
updated_bond.gateway.version = new_config.version;
|
||||
|
||||
storage::gateways().replace(
|
||||
deps.storage,
|
||||
existing_bond.identity(),
|
||||
Some(&updated_bond),
|
||||
Some(&existing_bond),
|
||||
)?;
|
||||
|
||||
Ok(Response::new().add_event(cfg_update_event))
|
||||
}
|
||||
|
||||
#[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,
|
||||
try_update_gateway_config, try_update_gateway_config_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::gateway::GatewayConfigUpdate;
|
||||
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 +300,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 +325,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 +412,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 +435,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 +452,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 +466,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 +508,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]
|
||||
@@ -432,4 +541,94 @@ pub mod tests {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_gateway_config() {
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
let owner = "alice";
|
||||
let info = mock_info(owner, &[]);
|
||||
let update = GatewayConfigUpdate {
|
||||
host: "1.1.1.1:1234".to_string(),
|
||||
mix_port: 1234,
|
||||
clients_port: 1235,
|
||||
location: "home".to_string(),
|
||||
version: "v1.2.3".to_string(),
|
||||
};
|
||||
|
||||
// try updating a non existing gateway bond
|
||||
let res = try_update_gateway_config(test.deps_mut(), info.clone(), update.clone());
|
||||
assert_eq!(
|
||||
res,
|
||||
Err(MixnetContractError::NoAssociatedGatewayBond {
|
||||
owner: Addr::unchecked(owner)
|
||||
})
|
||||
);
|
||||
|
||||
test.add_dummy_gateway(owner, None);
|
||||
let vesting_contract = test.vesting_contract();
|
||||
|
||||
// attempted to remove on behalf with invalid proxy (current is `None`)
|
||||
let res = try_update_gateway_config_on_behalf(
|
||||
test.deps_mut(),
|
||||
mock_info(vesting_contract.as_ref(), &[]),
|
||||
update.clone(),
|
||||
owner.to_string(),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
Err(MixnetContractError::ProxyMismatch {
|
||||
existing: "None".to_string(),
|
||||
incoming: vesting_contract.into_string()
|
||||
})
|
||||
);
|
||||
|
||||
// "normal" update succeeds
|
||||
let res = try_update_gateway_config(test.deps_mut(), info, update.clone());
|
||||
assert!(res.is_ok());
|
||||
|
||||
// and the config has actually been updated
|
||||
let bond =
|
||||
must_get_gateway_bond_by_owner(test.deps().storage, &Addr::unchecked(owner)).unwrap();
|
||||
assert_eq!(bond.gateway.host, update.host);
|
||||
assert_eq!(bond.gateway.mix_port, update.mix_port);
|
||||
assert_eq!(bond.gateway.clients_port, update.clients_port);
|
||||
assert_eq!(bond.gateway.location, update.location);
|
||||
assert_eq!(bond.gateway.version, update.version);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn updating_gateway_config_with_illegal_proxy() {
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
let illegal_proxy = Addr::unchecked("not-vesting-contract");
|
||||
let vesting_contract = test.vesting_contract();
|
||||
|
||||
let owner = "alice";
|
||||
|
||||
test.add_dummy_gateway_with_illegal_proxy(owner, None, illegal_proxy.clone());
|
||||
let update = GatewayConfigUpdate {
|
||||
host: "1.1.1.1:1234".to_string(),
|
||||
mix_port: 1234,
|
||||
clients_port: 1235,
|
||||
location: "at home".to_string(),
|
||||
version: "v1.2.3".to_string(),
|
||||
};
|
||||
|
||||
let res = try_update_gateway_config_on_behalf(
|
||||
test.deps_mut(),
|
||||
mock_info(illegal_proxy.as_ref(), &[]),
|
||||
update,
|
||||
owner.to_string(),
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
res,
|
||||
MixnetContractError::SenderIsNotVestingContract {
|
||||
received: illegal_proxy,
|
||||
vesting_contract
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,14 +327,6 @@ pub(crate) fn try_update_interval_config(
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
ensure_is_owner(info.sender, deps.storage)?;
|
||||
|
||||
if epochs_in_interval == 0 {
|
||||
return Err(MixnetContractError::EpochsInIntervalZero);
|
||||
}
|
||||
|
||||
if epoch_duration_secs == 0 {
|
||||
return Err(MixnetContractError::EpochDurationZero);
|
||||
}
|
||||
|
||||
let interval = storage::current_interval(deps.storage)?;
|
||||
if force_immediately || interval.is_current_interval_over(&env) {
|
||||
change_interval_config(
|
||||
|
||||
@@ -14,6 +14,7 @@ mod mixnet_contract_settings;
|
||||
mod mixnodes;
|
||||
mod queued_migrations;
|
||||
mod rewards;
|
||||
mod signing;
|
||||
mod support;
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
|
||||
pub mod helpers;
|
||||
pub mod queries;
|
||||
pub mod signature_helpers;
|
||||
pub mod storage;
|
||||
pub mod transactions;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
@@ -284,7 +295,7 @@ pub(crate) fn try_update_mixnode_config(
|
||||
}
|
||||
|
||||
pub(crate) fn try_update_mixnode_config_on_behalf(
|
||||
deps: DepsMut,
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
new_config: MixNodeConfigUpdate,
|
||||
owner: String,
|
||||
@@ -297,7 +308,7 @@ pub(crate) fn try_update_mixnode_config_on_behalf(
|
||||
}
|
||||
|
||||
pub(crate) fn _try_update_mixnode_config(
|
||||
deps: DepsMut,
|
||||
deps: DepsMut<'_>,
|
||||
new_config: MixNodeConfigUpdate,
|
||||
owner: Addr,
|
||||
proxy: Option<Addr>,
|
||||
@@ -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)]
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod queries;
|
||||
pub mod storage;
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::signing::storage::get_signing_nonce;
|
||||
use cosmwasm_std::{Deps, StdResult};
|
||||
use nym_contracts_common::signing::Nonce;
|
||||
|
||||
pub fn query_current_signing_nonce(deps: Deps<'_>, address: String) -> StdResult<Nonce> {
|
||||
let address = deps.api.addr_validate(&address)?;
|
||||
get_signing_nonce(deps.storage, address)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::constants::SIGNING_NONCES_NAMESPACE;
|
||||
use cosmwasm_std::{Addr, StdResult, Storage};
|
||||
use cw_storage_plus::Map;
|
||||
use nym_contracts_common::signing::Nonce;
|
||||
|
||||
pub const NONCES: Map<'_, Addr, Nonce> = Map::new(SIGNING_NONCES_NAMESPACE);
|
||||
|
||||
pub fn get_signing_nonce(storage: &dyn Storage, address: Addr) -> StdResult<Nonce> {
|
||||
let nonce = NONCES.may_load(storage, address)?.unwrap_or(0);
|
||||
Ok(nonce)
|
||||
}
|
||||
|
||||
pub fn update_signing_nonce(
|
||||
storage: &mut dyn Storage,
|
||||
address: Addr,
|
||||
value: Nonce,
|
||||
) -> StdResult<()> {
|
||||
NONCES.save(storage, address, &value)
|
||||
}
|
||||
|
||||
pub fn increment_signing_nonce(storage: &mut dyn Storage, address: Addr) -> StdResult<()> {
|
||||
// get the current nonce
|
||||
let nonce = get_signing_nonce(storage, address.clone())?;
|
||||
|
||||
// increment it for the next use
|
||||
update_signing_nonce(storage, address, nonce + 1)
|
||||
}
|
||||
@@ -377,7 +377,7 @@ pub fn validate_node_identity_signature(
|
||||
signature: &str,
|
||||
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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
pub mod fixtures;
|
||||
#[cfg(test)]
|
||||
pub mod messages;
|
||||
#[cfg(test)]
|
||||
pub mod queries;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test_helpers {
|
||||
@@ -22,9 +20,10 @@ pub mod test_helpers {
|
||||
perform_pending_epoch_actions, perform_pending_interval_actions, try_begin_epoch_transition,
|
||||
};
|
||||
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::{self 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
|
||||
}
|
||||
@@ -17,13 +17,13 @@ crate-type = ["cdylib", "rlib"]
|
||||
library = []
|
||||
|
||||
[dependencies]
|
||||
cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw3-fixed-multisig = { workspace = true, features = ["library"] }
|
||||
cw4 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cw-utils = { version = "0.13.4" }
|
||||
cw2 = { version = "0.13.4" }
|
||||
cw3 = { version = "0.13.4" }
|
||||
cw3-fixed-multisig = { version = "0.13.4", features = ["library"] }
|
||||
cw4 = { version = "0.13.4" }
|
||||
cw-storage-plus = { version = "0.13.4" }
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -33,4 +33,4 @@ nym-multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = { version = "1.0.0" }
|
||||
cw4-group = { path = "../cw4-group", version = "0.13.4" }
|
||||
cw-multi-test = { workspace = true }
|
||||
cw-multi-test = { version = "0.13.4" }
|
||||
|
||||
@@ -26,15 +26,15 @@ library = []
|
||||
[dependencies]
|
||||
nym-group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" }
|
||||
|
||||
cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cw-utils = { version = "0.13.4" }
|
||||
cw2 = { version = "0.13.4" }
|
||||
cw4 = { version = "0.13.4" }
|
||||
cw-controllers = { version = "0.13.4" }
|
||||
cw-storage-plus = { version = "0.13.4" }
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cosmwasm-schema = { version = "1.0.0" }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user