Compare commits

..

56 Commits

Author SHA1 Message Date
Simon Wicky edfb75b9ef remove test client 2023-03-28 16:12:21 +02:00
Simon Wicky 298591b9e0 Merge commit '009b2131f61dc447ad08b5f1df1e2eef44cde1d2' into simon/instrumented 2023-03-28 13:45:37 +00:00
Simon Wicky 009b2131f6 instrumentation 2023-03-28 13:24:45 +00:00
Simon Wicky e5af0f5d5e Merge commit '3cad2dbb341c23592d22856ced5c8020a7ea20b7' into simon/instrumented 2023-03-28 13:21:46 +00:00
Simon Wicky 3cad2dbb34 Merge remote-tracking branch 'origin/simon/instrumented' into simon/instrumented 2023-03-28 13:18:53 +00:00
Simon Wicky e95e33cd70 Merge commit '6d44fe818ea4c74f476cc6d79434cc8619b45c0c' into simon/instrumented 2023-03-28 13:17:09 +00:00
Simon Wicky 5281895d5b mixnode instrumentation 2023-03-28 12:39:06 +00:00
Simon Wicky 263db0dbc3 Merge commit '6d44fe818ea4c74f476cc6d79434cc8619b45c0c' into Simon/1.1.4 2023-03-28 11:55:06 +00:00
Simon Wicky 6d44fe818e Merge commit '2878e9be9d0c397a746a8c942b818ac1168dff6f' into simon 2023-03-28 11:50:17 +02:00
Simon Wicky b8ec48cf07 small changes to warmup phase 2023-03-28 09:41:35 +00:00
Simon Wicky 99227e837c disable some epoch operations 2023-03-23 07:52:39 +00:00
farbanas 2878e9be9d Merge branch 'release/v1.1.13' 2023-03-22 10:15:32 +01:00
farbanas 7b419c2b12 bump version of contracts 2023-03-22 10:12:33 +01:00
Fran Arbanas 0049126a91 Merge pull request #3200 from nymtech/jon/chore/explicit-cosmwasm-versions-across-workspaces
Set cosmwasm versions on workspace and strictly use 1.0.0
2023-03-22 10:06:18 +01:00
Simon Wicky 34cb142595 packet size the end 2023-03-22 08:40:33 +00:00
Jon Häggblad 80c5194d8b Add explicit cosmwasm-crypto 1.0.0 dev-dependency 2023-03-21 16:34:06 +01:00
Jon Häggblad 27a6b99453 Even more workspace version missed earlier 2023-03-21 16:34:06 +01:00
Jon Häggblad 61982de511 A few more workspace versions 2023-03-21 16:34:06 +01:00
Jon Häggblad efd9883197 Use workspace deps for coconut contracts 2023-03-21 16:34:06 +01:00
Jon Häggblad ce4ae8d90c Set cosmwasm versions on workspace and strictly use 1.0.0 2023-03-21 16:34:06 +01:00
farbanas 1d2722f994 update workflows with specific wasm-opt version 2023-03-21 16:30:40 +01:00
farbanas f7a0b305df update common crate version in contracts 2023-03-21 11:10:55 +01:00
farbanas 746ec71a0d update package versions 2023-03-21 10:54:18 +01:00
farbanas cdfa5ee540 chore: update versions for release/v1.1.13 2023-03-15 15:23:55 +01:00
farbanas 71853f69f3 chore: update changelogs for release/v1.1.13 2023-03-15 15:23:15 +01:00
Tommy Verrall bedff1f258 Merge pull request #3153 from nymtech/oak-14
Validating new interval config parameters to prevent division by zero
2023-03-14 12:38:43 +02:00
Fouad 71a10a9a8b add blockstream green to sp list (#3180) 2023-03-13 17:05:21 +01:00
Jon Häggblad 605aed6f20 mock-nym-api: fix .storybook lint error (#3178) 2023-03-13 12:23:17 +01:00
Tommy Verrall 5aee4b1660 Merge pull request #3167 from nymtech/feature/wallet-3132
Feature/wallet 3132
2023-03-10 17:56:09 +02:00
Tommy Verrall 68a7bb67de Merge pull request #3169 from nymtech/feature/explorer-3168
Feature/explorer 3168
2023-03-10 17:32:22 +02:00
Simon Wicky 2988ae4459 packet size in config 2023-03-10 14:16:53 +00:00
Simon Wicky e653b632ba packet sizes support 2023-03-10 13:18:19 +00:00
Simon Wicky eb216a06b3 redirect mixnodes/active to mixnodes 2023-03-10 13:18:15 +00:00
fmtabbara 31e93428cf use new locked rewards and locked coins endpoints 2023-03-09 16:48:05 +00:00
fmtabbara 5f56b3eeea add bond % tooltip 2023-03-09 16:34:22 +00:00
fmtabbara e69b05693a switch avg and routing score table positions 2023-03-09 16:30:58 +00:00
fmtabbara 8ae2451340 add y-axis label 2023-03-09 16:28:30 +00:00
fmtabbara b3b3279345 fix gateway click thorough from gateway version field 2023-03-09 14:45:45 +00:00
farbanas 94a451c79b fix: updated build-and-upload-binaries-ci workflow with explorer-api and the new contracts 2023-03-09 13:41:22 +01:00
Jędrzej Stuczyński ec7b959028 removed source of future clippy complaints 2023-03-09 11:30:30 +00:00
Jędrzej Stuczyński 7061beea6e exposing tauri operations for spendable vested and reward coins 2023-03-09 11:28:50 +00:00
Fran Arbanas e408162e26 Merge pull request #2747 from nymtech/339-net-switch-bttn
339 net switch bttn
2023-03-09 10:52:00 +01:00
Gala 25ae3895cb updating branch 2023-03-09 10:39:30 +01:00
fmtabbara 60296f2a41 update vesting schedule ui 2023-03-08 17:51:30 +00:00
Fouad 3b97844310 Feature/explorer 2979 (#3147)
* additional unfiltered endpoints for nym-api

* add poor performance UI

* display appropriate UI when node is blacklisted

* update explorer api with blacklisted nodes

* add new unfiltered endpoint

add new unfiltered endpoint

* show blacklisted detail even when node description is unavailable

remove console.log

---------

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
2023-03-08 16:15:32 +01:00
Jon Häggblad 8e6d3c34e2 Merge branch 'jon/fix/docs-rs-build' 2023-03-08 09:37:31 +01:00
Jędrzej Stuczyński b1fb8bb18c Validating new interval config parameters to prevent division by zero 2023-03-07 09:51:18 +00:00
Simon Wicky 921f01a789 Merge commit 'f4d0a120bb7f552591c415ece31d9f75bd6ec33e' into Simon/1.1.5 2023-01-31 09:31:24 +00:00
Simon Wicky 27bf8b2e00 tracing first part 2023-01-31 09:25:15 +00:00
Simon Wicky e82a669cd3 merge 1.1.5 2023-01-11 15:17:15 +00:00
Simon Wicky 2015443a90 set default packetsize back to 2 2023-01-10 10:23:51 +00:00
Gala 4f59678ded center layout 2022-12-22 15:31:35 +01:00
Gala 8a1d2af3cf adding changes 2022-12-22 14:44:58 +01:00
Simon Wicky 2c2823f9e1 test instrumentation 2022-12-21 08:31:16 +00:00
Simon Wicky 52b41a5697 add packet sizes support 2022-12-21 08:06:23 +00:00
Simon Wicky 9ee6ae44e2 remove override config, and active nodes 2022-12-21 08:06:13 +00:00
259 changed files with 5516 additions and 8285 deletions
@@ -80,7 +80,7 @@ jobs:
components: rustfmt, clippy
- name: Install wasm-opt
run: cargo install wasm-opt
run: cargo install --version 0.110.0 wasm-opt
- name: Build release contracts
run: make wasm
@@ -99,9 +99,14 @@ 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
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
components: rustfmt, clippy
- name: Install wasm-opt
run: cargo install wasm-opt
run: cargo install --version 0.110.0 wasm-opt
- name: Build release contracts
run: make wasm
-3
View File
@@ -39,8 +39,5 @@ validator-api-config.toml
dist
storybook-static
envs/qwerty.env
Cargo.lock
nym-connect/Cargo.lock
.parcel-cache
**/.DS_Store
cpu-cycles/libcpucycles/build
+20
View File
@@ -4,6 +4,26 @@ 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
+755 -490
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -22,6 +22,7 @@ members = [
"clients/native",
"clients/native/websocket-requests",
"clients/socks5",
"common/bandwidth-claim-contract",
"common/bin-common",
"common/client-libs/gateway-client",
"common/client-libs/mixnet-client",
@@ -106,6 +107,16 @@ license = "Apache-2.0"
[workspace.dependencies]
async-trait = "0.1.64"
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 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "client-core"
version = "1.1.12"
version = "1.1.13"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.66"
@@ -333,7 +333,7 @@ where
fn poll_poisson(&mut self, cx: &mut Context<'_>) -> Poll<Option<StreamMessage>> {
// The average delay could change depending on if backpressure in the downstream channel
// (mix_tx) was detected.
self.adjust_current_average_message_sending_delay();
//self.adjust_current_average_message_sending_delay();
let avg_delay = self.current_average_message_sending_delay();
// Start by checking if we have any incoming messages about closed connections
+22
View File
@@ -697,6 +697,17 @@ pub enum ExtendedPacketSize {
Extended8,
Extended16,
Extended32,
Extended10,
Extended15,
Extended20,
Extended25,
Extended50,
Extended100,
Extended150,
Extended200,
Extended250,
Extended500,
}
impl Default for DebugConfig {
@@ -734,6 +745,17 @@ impl From<ExtendedPacketSize> for PacketSize {
ExtendedPacketSize::Extended8 => PacketSize::ExtendedPacket8,
ExtendedPacketSize::Extended16 => PacketSize::ExtendedPacket16,
ExtendedPacketSize::Extended32 => PacketSize::ExtendedPacket32,
ExtendedPacketSize::Extended10 => PacketSize::ExtendedPacket10,
ExtendedPacketSize::Extended15 => PacketSize::ExtendedPacket15,
ExtendedPacketSize::Extended20 => PacketSize::ExtendedPacket20,
ExtendedPacketSize::Extended25 => PacketSize::ExtendedPacket25,
ExtendedPacketSize::Extended50 => PacketSize::ExtendedPacket50,
ExtendedPacketSize::Extended100 => PacketSize::ExtendedPacket100,
ExtendedPacketSize::Extended150 => PacketSize::ExtendedPacket150,
ExtendedPacketSize::Extended200 => PacketSize::ExtendedPacket200,
ExtendedPacketSize::Extended250 => PacketSize::ExtendedPacket250,
ExtendedPacketSize::Extended500 => PacketSize::ExtendedPacket500,
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.12"
version = "1.1.13"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.12"
version = "1.1.13"
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"
@@ -0,0 +1,10 @@
[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"] }
@@ -0,0 +1,45 @@
// 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
}
}
@@ -0,0 +1,3 @@
pub mod keys;
pub mod msg;
pub mod payment;
@@ -0,0 +1,30 @@
// 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 {}
@@ -0,0 +1,73 @@
// 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 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-bin-common"
version = "0.2.0"
version = "0.3.0"
description = "Common code for nym binaries"
edition = { workspace = true }
authors = { workspace = true }
@@ -74,7 +74,7 @@ impl PacketRouter {
received_messages.push(received_packet);
} else {
// this can happen if other clients are not padding their messages
warn!("Received message of unexpected size. Probably from an outdated client... len: {}", received_packet.len());
//warn!("Received message of unexpected size. Probably from an outdated client... len: {}", received_packet.len());
received_messages.push(received_packet);
}
}
@@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
futures = "0.3"
tracing = "0.1.37"
log = { workspace = true }
tokio = { version = "1.24.1", features = ["time", "net", "rt"] }
tokio-util = { version = "0.7.4", features = ["codec"] }
@@ -3,11 +3,12 @@
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use tracing::*;
use nym_sphinx::framing::codec::SphinxCodec;
use nym_sphinx::framing::packet::FramedSphinxPacket;
use nym_sphinx::params::PacketMode;
use nym_sphinx::{addressing::nodes::NymNodeRoutingAddress, SphinxPacket};
use nym_sphinx::params::packet_sizes::PacketSize;
use std::collections::HashMap;
use std::io;
use std::net::SocketAddr;
@@ -197,6 +198,7 @@ impl Client {
}
impl SendWithoutResponse for Client {
#[instrument(level="info", skip(self, packet), "Sending packet to mixnet", fields(packet_size))]
fn send_without_response(
&mut self,
address: NymNodeRoutingAddress,
@@ -204,13 +206,15 @@ impl SendWithoutResponse for Client {
packet_mode: PacketMode,
) -> io::Result<()> {
trace!("Sending packet to {:?}", address);
let packet_size = PacketSize::get_type(packet.len()).unwrap();
Span::current().record("packet_size", field::debug(packet_size));
let framed_packet =
FramedSphinxPacket::new(packet, packet_mode, self.config.use_legacy_version);
if let Some(sender) = self.conn_new.get_mut(&address) {
if let Err(err) = sender.channel.try_send(framed_packet) {
if err.is_full() {
debug!("Connection to {} seems to not be able to handle all the traffic - dropping the current packet", address);
info!("Connection to {} seems to not be able to handle all the traffic - dropping the current packet", address);
// it's not a 'big' error, but we did not manage to send the packet
// if the queue is full, we can't really do anything but to drop the packet
Err(io::Error::new(
@@ -4,8 +4,8 @@
use crate::client::{Client, Config, SendWithoutResponse};
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use nym_sphinx::forwarding::packet::MixPacket;
use tracing::*;
use std::time::Duration;
pub type MixForwardingSender = mpsc::UnboundedSender<MixPacket>;
@@ -53,10 +53,10 @@ impl PacketForwarder {
tokio::select! {
biased;
_ = self.shutdown.recv() => {
log::trace!("PacketForwarder: Received shutdown");
trace!("PacketForwarder: Received shutdown");
}
Some(mix_packet) = self.packet_receiver.next() => {
trace!("Going to forward packet to {:?}", mix_packet.next_hop());
trace!("Going to forward packet to {:?}", mix_packet.next_hop());
let next_hop = mix_packet.next_hop();
let packet_mode = mix_packet.packet_mode();
@@ -40,13 +40,13 @@ async-trait = { workspace = true, optional = true }
bip39 = { version = "1", features = ["rand"], optional = true }
nym-config = { path = "../../config", optional = true }
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true}
cw3 = { version = "0.13.4", optional = true }
cw4 = { version = "0.13.4", optional = true }
cw3 = { workspace = true, optional = true }
cw4 = { workspace = true, 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 = { version = "1.0.0", optional = true }
cosmwasm-std = { workspace = true, optional = true }
zeroize = { version = "1.5.7", optional = true }
[dev-dependencies]
@@ -749,6 +749,12 @@ 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,6 +144,21 @@ 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,6 +8,7 @@ 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,7 +43,6 @@ 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;
@@ -370,15 +369,6 @@ 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,7 +6,6 @@ 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::{
@@ -391,13 +390,6 @@ 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,7 +8,6 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient};
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
use nym_mixnet_contract_common::{
@@ -291,7 +290,7 @@ pub trait MixnetSigningClient {
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: String,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -312,7 +311,7 @@ pub trait MixnetSigningClient {
owner: AccountId,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: String,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -443,7 +442,7 @@ pub trait MixnetSigningClient {
async fn bond_gateway(
&self,
gateway: Gateway,
owner_signature: MessageSignature,
owner_signature: String,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -462,7 +461,7 @@ pub trait MixnetSigningClient {
&self,
owner: AccountId,
gateway: Gateway,
owner_signature: MessageSignature,
owner_signature: String,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -6,7 +6,6 @@ use crate::nyxd::cosmwasm_client::types::ExecuteResult;
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Coin, Fee, NyxdClient};
use async_trait::async_trait;
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
use nym_vesting_contract_common::messages::{
@@ -44,7 +43,7 @@ pub trait VestingSigningClient {
async fn vesting_bond_gateway(
&self,
gateway: Gateway,
owner_signature: MessageSignature,
owner_signature: &str,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError>;
@@ -62,7 +61,7 @@ pub trait VestingSigningClient {
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: &str,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError>;
@@ -209,14 +208,14 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
async fn vesting_bond_gateway(
&self,
gateway: Gateway,
owner_signature: MessageSignature,
owner_signature: &str,
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: owner_signature.to_string(),
amount: pledge.into(),
};
self.client
@@ -273,7 +272,7 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: &str,
pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -282,7 +281,7 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
VestingExecuteMsg::BondMixnode {
mix_node,
cost_params,
owner_signature,
owner_signature: owner_signature.to_string(),
amount: pledge.into(),
},
vec![],
+2 -3
View File
@@ -11,7 +11,7 @@ bs58 = "0.4"
comfy-table = "6.0.0"
cfg-if = "1.0.0"
clap = { version = "4.0", features = ["derive"] }
cw-utils = { version = "0.13.4" }
cw-utils = { workspace = true }
handlebars = "3.0.1"
humantime-serde = "1.0"
k256 = { version = "0.10", features = ["ecdsa", "sha256"] }
@@ -26,11 +26,10 @@ url = "2.2"
tap = "1"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
cosmwasm-std = { version = "1.0.0" }
cosmwasm-std = { workspace = true }
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" }
+3 -10
View File
@@ -1,19 +1,12 @@
// 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 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())
}
use cosmwasm_std::{Coin as CosmWasmCoin, Decimal};
use log::error;
use validator_client::nyxd::Coin;
pub fn pretty_coin(coin: &Coin) -> String {
let amount = Decimal::from_ratio(coin.amount, 1_000_000u128);
@@ -4,7 +4,6 @@
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;
@@ -15,7 +14,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: MessageSignature,
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -24,7 +23,7 @@ pub struct Args {
pub clients_port: Option<u16>,
#[clap(long)]
pub location: String,
pub location: Option<String>,
#[clap(long)]
pub sphinx_key: String,
@@ -60,7 +59,9 @@ 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,
location: args
.location
.unwrap_or_else(|| "secret gateway location".to_owned()),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
@@ -1,81 +0,0 @@
// 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,7 +4,6 @@
use clap::{Args, Subcommand};
pub mod bond_gateway;
pub mod gateway_bonding_sign_payload;
pub mod unbond_gateway;
pub mod vesting_bond_gateway;
pub mod vesting_unbond_gateway;
@@ -26,6 +25,4 @@ pub enum MixnetOperatorsGatewayCommands {
VestingBond(vesting_bond_gateway::Args),
/// Unbound from a gateway (when originally using locked tokens)
VestingUnbound(vesting_unbond_gateway::Args),
/// Create base58-encoded payload required for producing valid bonding signature.
CreateGatewayBondingSignPayload(gateway_bonding_sign_payload::Args),
}
@@ -4,7 +4,6 @@
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;
@@ -15,7 +14,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: MessageSignature,
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -24,7 +23,7 @@ pub struct Args {
pub clients_port: Option<u16>,
#[clap(long)]
pub location: String,
pub location: Option<String>,
#[clap(long)]
pub sphinx_key: String,
@@ -58,7 +57,9 @@ 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,
location: args
.location
.unwrap_or_else(|| "secret gateway location".to_owned()),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
@@ -67,7 +68,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,7 +5,6 @@ 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,
@@ -21,7 +20,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: MessageSignature,
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -1,108 +0,0 @@
// 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,7 +5,6 @@ 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;
@@ -35,6 +34,4 @@ pub enum MixnetOperatorsMixnodeCommands {
BondVesting(vesting_bond_mixnode::Args),
/// Unbound from a mixnode (when originally using locked tokens)
UnboundVesting(vesting_unbond_mixnode::Args),
/// Create base58-encoded payload required for producing valid bonding signature.
CreateMixnodeBondingSignPayload(mixnode_bonding_sign_payload::Args),
}
@@ -1,11 +1,10 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - 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::{
@@ -19,7 +18,7 @@ pub struct Args {
pub host: String,
#[clap(long)]
pub signature: MessageSignature,
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
@@ -96,7 +95,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 = "1.0.0"
cosmwasm-std = { workspace = true }
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 = "1.0.0"
cw-utils = "0.13.4"
cosmwasm-std = { workspace = true }
cw-utils = { workspace = true }
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
@@ -1,6 +1,6 @@
[package]
name = "nym-contracts-common"
version = "0.2.0"
version = "0.3.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 = "1.0.0"
cosmwasm-std = { workspace = true }
schemars = "0.8"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1"
@@ -6,7 +6,6 @@
pub mod dealings;
pub mod events;
pub mod signing;
pub mod types;
pub use types::*;
@@ -1,259 +0,0 @@
// 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,
}
}
}
@@ -1,81 +0,0 @@
// 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 = { version = "0.13.4" }
cw4 = { workspace = true }
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.2.0"
version = "0.3.0"
description = "Common library for the Nym mixnet contract"
rust-version = "1.62"
edition = { workspace = true }
@@ -10,12 +10,12 @@ repository = { workspace = true }
[dependencies]
bs58 = "0.4.0"
cosmwasm-std = "1.0.0"
cosmwasm-std = { workspace = true }
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.2.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.3.0" }
serde_json = "1.0.0"
humantime-serde = "1.1.1"
@@ -2,7 +2,6 @@
// 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;
@@ -219,10 +218,4 @@ pub enum MixnetContractError {
value: String,
error_message: String,
},
#[error("failed to verify message signature: {source}")]
SignatureVerificationFailure {
#[from]
source: ApiVerifierError,
},
}
@@ -17,7 +17,6 @@ mod msg;
pub mod pending_events;
pub mod reward_params;
pub mod rewarding;
pub mod signing_types;
mod types;
pub use contracts_common::types::*;
@@ -44,5 +43,4 @@ pub use pending_events::{
PendingIntervalEventData, PendingIntervalEventKind,
};
pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams};
pub use signing_types::*;
pub use types::*;
@@ -10,7 +10,6 @@ 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};
@@ -154,12 +153,12 @@ pub enum ExecuteMsg {
BondMixnode {
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: String,
},
BondMixnodeOnBehalf {
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: String,
owner: String,
},
PledgeMore {},
@@ -188,12 +187,12 @@ pub enum ExecuteMsg {
// gateway-related:
BondGateway {
gateway: Gateway,
owner_signature: MessageSignature,
owner_signature: String,
},
BondGatewayOnBehalf {
gateway: Gateway,
owner: String,
owner_signature: MessageSignature,
owner_signature: String,
},
UnbondGateway {},
UnbondGatewayOnBehalf {
@@ -501,11 +500,6 @@ pub enum QueryMsg {
start_after: Option<u32>,
},
GetNumberOfPendingEvents {},
// signing-related
GetSigningNonce {
address: String,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
@@ -1,151 +0,0 @@
// 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 = { 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"
cw-utils = { workspace = true }
cw3 = { workspace = true }
cw3-fixed-multisig = { workspace = true, features = ["library"] }
cw4 = { workspace= true }
cosmwasm-std = { workspace = true }
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.2.0"
version = "0.3.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 = "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" }
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" }
serde = { version = "1.0", features = ["derive"] }
schemars = "0.8"
ts-rs = {version = "6.1.2", optional = true}
@@ -1,4 +1,3 @@
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{Coin, Timestamp};
use mixnet_contract_common::{
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
@@ -119,7 +118,7 @@ pub enum ExecuteMsg {
BondMixnode {
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: String,
amount: Coin,
},
PledgeMore {
@@ -132,7 +131,7 @@ pub enum ExecuteMsg {
},
BondGateway {
gateway: Gateway,
owner_signature: MessageSignature,
owner_signature: String,
amount: Coin,
},
UnbondGateway {},
+1
View File
@@ -16,6 +16,7 @@ serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.24.1", features = ["time", "macros", "rt", "net", "io-util"] }
tokio-util = { version = "0.7.4", features = ["codec"] }
url = "2.2"
tracing = "0.1.37"
thiserror = "1.0.37"
nym-crypto = { path = "../crypto" }
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::packet_processor::error::MixProcessingError;
use log::*;
use tracing::*;
use nym_sphinx_acknowledgements::surb_ack::SurbAck;
use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx_forwarding::packet::MixPacket;
@@ -57,6 +57,7 @@ impl SphinxPacketProcessor {
}
/// Takes the received framed packet and tries to unwrap it from the sphinx encryption.
#[instrument(level="debug", skip_all)]
fn perform_initial_unwrapping(
&self,
received: FramedSphinxPacket,
@@ -73,6 +74,7 @@ impl SphinxPacketProcessor {
/// Processed received forward hop packet - tries to extract next hop address, sets delay
/// and packs all the data in a way that can be easily sent to the next hop.
#[instrument(level="debug", skip_all)]
fn process_forward_hop(
&self,
packet: SphinxPacket,
@@ -88,6 +90,7 @@ impl SphinxPacketProcessor {
/// Split data extracted from the final hop sphinx packet into a SURBAck and message
/// that should get delivered to a client.
#[instrument(level="debug", skip_all)]
fn split_hop_data_into_ack_and_message(
&self,
mut extracted_data: Vec<u8>,
@@ -105,6 +108,7 @@ impl SphinxPacketProcessor {
/// Tries to extract a SURBAck that could be sent back into the mix network and message
/// that should get delivered to a client from received Sphinx packet.
#[instrument(level="debug", skip_all)]
fn split_into_ack_and_message(
&self,
data: Vec<u8>,
@@ -116,10 +120,7 @@ impl SphinxPacketProcessor {
trace!("received an ack packet!");
Ok((None, data))
}
PacketSize::RegularPacket
| PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32 => {
_ => {
trace!("received a normal packet!");
let (ack_data, message) = self.split_hop_data_into_ack_and_message(data)?;
let (ack_first_hop, ack_packet) = SurbAck::try_recover_first_hop_packet(&ack_data)?;
@@ -132,6 +133,7 @@ impl SphinxPacketProcessor {
/// Processed received final hop packet - tries to extract SURBAck out of it (assuming the
/// packet itself is not an ACK) and splits it from the message that should get delivered
/// to the destination.
#[instrument(level="debug", skip_all)]
fn process_final_hop(
&self,
destination: DestinationAddressBytes,
@@ -153,6 +155,7 @@ impl SphinxPacketProcessor {
/// Performs final processing for the unwrapped packet based on whether it was a forward hop
/// or a final hop.
#[instrument(level="debug", skip_all)]
fn perform_final_processing(
&self,
packet: ProcessedPacket,
@@ -170,7 +173,7 @@ impl SphinxPacketProcessor {
}
}
}
#[instrument(level="debug", skip_all, fields(packet_size=?received.packet_size()))]
pub fn process_received(
&self,
received: FramedSphinxPacket,
+83 -1
View File
@@ -20,6 +20,16 @@ const ACK_PACKET_SIZE: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + ACK_IV_SIZE
const EXTENDED_PACKET_SIZE_8: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 8 * 1024;
const EXTENDED_PACKET_SIZE_16: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 16 * 1024;
const EXTENDED_PACKET_SIZE_32: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 32 * 1024;
const EXTENDED_PACKET_SIZE_10: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 10 * 1024;
const EXTENDED_PACKET_SIZE_15: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 15 * 1024;
const EXTENDED_PACKET_SIZE_20: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 20 * 1024;
const EXTENDED_PACKET_SIZE_25: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 25 * 1024;
const EXTENDED_PACKET_SIZE_50: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 50 * 1024;
const EXTENDED_PACKET_SIZE_100: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 100 * 1024;
const EXTENDED_PACKET_SIZE_150: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 150 * 1024;
const EXTENDED_PACKET_SIZE_200: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 200 * 1024;
const EXTENDED_PACKET_SIZE_250: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 250 * 1024;
const EXTENDED_PACKET_SIZE_500: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 500 * 1024;
#[derive(Debug, Error)]
pub enum InvalidPacketSize {
@@ -51,6 +61,18 @@ pub enum PacketSize {
// for example for streaming fast and furious in compressed XviD quality
ExtendedPacket16 = 5,
ExtendedPacket10 = 6,
ExtendedPacket15 = 7,
ExtendedPacket20 = 8,
ExtendedPacket25 = 9,
ExtendedPacket50 = 10,
ExtendedPacket100 = 11,
ExtendedPacket150 = 12,
ExtendedPacket200 = 13,
ExtendedPacket250 = 14,
ExtendedPacket500 = 15,
}
impl FromStr for PacketSize {
@@ -63,6 +85,16 @@ impl FromStr for PacketSize {
"extended8" => Ok(Self::ExtendedPacket8),
"extended16" => Ok(Self::ExtendedPacket16),
"extended32" => Ok(Self::ExtendedPacket32),
"extended10" => Ok(Self::ExtendedPacket10),
"extended15" => Ok(Self::ExtendedPacket15),
"extended20" => Ok(Self::ExtendedPacket20),
"extended25" => Ok(Self::ExtendedPacket25),
"extended50" => Ok(Self::ExtendedPacket50),
"extended100" => Ok(Self::ExtendedPacket100),
"extended150" => Ok(Self::ExtendedPacket150),
"extended200" => Ok(Self::ExtendedPacket200),
"extended250" => Ok(Self::ExtendedPacket250),
"extended500" => Ok(Self::ExtendedPacket500),
s => Err(InvalidPacketSize::UnknownExtendedPacketVariant {
received: s.to_string(),
}),
@@ -80,6 +112,16 @@ impl TryFrom<u8> for PacketSize {
_ if value == (PacketSize::ExtendedPacket8 as u8) => Ok(Self::ExtendedPacket8),
_ if value == (PacketSize::ExtendedPacket16 as u8) => Ok(Self::ExtendedPacket16),
_ if value == (PacketSize::ExtendedPacket32 as u8) => Ok(Self::ExtendedPacket32),
_ if value == (PacketSize::ExtendedPacket10 as u8) => Ok(Self::ExtendedPacket10),
_ if value == (PacketSize::ExtendedPacket15 as u8) => Ok(Self::ExtendedPacket15),
_ if value == (PacketSize::ExtendedPacket20 as u8) => Ok(Self::ExtendedPacket20),
_ if value == (PacketSize::ExtendedPacket25 as u8) => Ok(Self::ExtendedPacket25),
_ if value == (PacketSize::ExtendedPacket50 as u8) => Ok(Self::ExtendedPacket50),
_ if value == (PacketSize::ExtendedPacket100 as u8) => Ok(Self::ExtendedPacket100),
_ if value == (PacketSize::ExtendedPacket150 as u8) => Ok(Self::ExtendedPacket150),
_ if value == (PacketSize::ExtendedPacket200 as u8) => Ok(Self::ExtendedPacket200),
_ if value == (PacketSize::ExtendedPacket250 as u8) => Ok(Self::ExtendedPacket250),
_ if value == (PacketSize::ExtendedPacket500 as u8) => Ok(Self::ExtendedPacket500),
v => Err(InvalidPacketSize::UnknownPacketTag { received: v }),
}
}
@@ -93,6 +135,16 @@ impl PacketSize {
PacketSize::ExtendedPacket8 => EXTENDED_PACKET_SIZE_8,
PacketSize::ExtendedPacket16 => EXTENDED_PACKET_SIZE_16,
PacketSize::ExtendedPacket32 => EXTENDED_PACKET_SIZE_32,
PacketSize::ExtendedPacket10 => EXTENDED_PACKET_SIZE_10,
PacketSize::ExtendedPacket15 => EXTENDED_PACKET_SIZE_15,
PacketSize::ExtendedPacket20 => EXTENDED_PACKET_SIZE_20,
PacketSize::ExtendedPacket25 => EXTENDED_PACKET_SIZE_25,
PacketSize::ExtendedPacket50 => EXTENDED_PACKET_SIZE_50,
PacketSize::ExtendedPacket100 => EXTENDED_PACKET_SIZE_100,
PacketSize::ExtendedPacket150 => EXTENDED_PACKET_SIZE_150,
PacketSize::ExtendedPacket200 => EXTENDED_PACKET_SIZE_200,
PacketSize::ExtendedPacket250 => EXTENDED_PACKET_SIZE_250,
PacketSize::ExtendedPacket500 => EXTENDED_PACKET_SIZE_500,
}
}
@@ -115,6 +167,26 @@ impl PacketSize {
Ok(PacketSize::ExtendedPacket16)
} else if PacketSize::ExtendedPacket32.size() == size {
Ok(PacketSize::ExtendedPacket32)
} else if PacketSize::ExtendedPacket10.size() == size {
Ok(PacketSize::ExtendedPacket10)
} else if PacketSize::ExtendedPacket15.size() == size {
Ok(PacketSize::ExtendedPacket15)
} else if PacketSize::ExtendedPacket20.size() == size {
Ok(PacketSize::ExtendedPacket20)
} else if PacketSize::ExtendedPacket25.size() == size {
Ok(PacketSize::ExtendedPacket25)
} else if PacketSize::ExtendedPacket50.size() == size {
Ok(PacketSize::ExtendedPacket50)
} else if PacketSize::ExtendedPacket100.size() == size {
Ok(PacketSize::ExtendedPacket100)
} else if PacketSize::ExtendedPacket150.size() == size {
Ok(PacketSize::ExtendedPacket150)
} else if PacketSize::ExtendedPacket200.size() == size {
Ok(PacketSize::ExtendedPacket200)
} else if PacketSize::ExtendedPacket250.size() == size {
Ok(PacketSize::ExtendedPacket250)
} else if PacketSize::ExtendedPacket500.size() == size {
Ok(PacketSize::ExtendedPacket500)
} else {
Err(InvalidPacketSize::UnknownPacketSize { received: size })
}
@@ -125,7 +197,17 @@ impl PacketSize {
PacketSize::RegularPacket | PacketSize::AckPacket => false,
PacketSize::ExtendedPacket8
| PacketSize::ExtendedPacket16
| PacketSize::ExtendedPacket32 => true,
| PacketSize::ExtendedPacket32
| PacketSize::ExtendedPacket10
| PacketSize::ExtendedPacket15
| PacketSize::ExtendedPacket20
| PacketSize::ExtendedPacket25
| PacketSize::ExtendedPacket50
| PacketSize::ExtendedPacket100
| PacketSize::ExtendedPacket150
| PacketSize::ExtendedPacket200
| PacketSize::ExtendedPacket250
| PacketSize::ExtendedPacket500 => true,
}
}
@@ -47,6 +47,16 @@ impl From<u8> for PacketVersion {
n if n == PacketSize::ExtendedPacket8 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket16 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket32 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket10 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket15 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket20 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket25 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket50 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket100 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket150 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket200 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket250 as u8 => PacketVersion::Legacy,
n if n == PacketSize::ExtendedPacket500 as u8 => PacketVersion::Legacy,
n => PacketVersion::Versioned(n),
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ thiserror = "1.0"
url = "2.2"
ts-rs = "6.1.2"
cosmwasm-std = "1.0.0"
cosmwasm-std = { workspace = true }
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
validator-client = { path = "../../common/client-libs/validator-client", features = [
+2
View File
@@ -81,6 +81,7 @@ 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,
@@ -93,6 +94,7 @@ impl fmt::Display for GatewayNodeDetailsResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Identity Key: {}", self.identity_key)?;
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
writeln!(
f,
"Host: {} (bind address: {})",
+2
View File
@@ -167,6 +167,7 @@ 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,
@@ -181,6 +182,7 @@ impl fmt::Display for MixnodeNodeDetailsResponse {
let wallet_address = self.wallet_address.clone().unwrap_or_default();
writeln!(f, "Identity Key: {}", self.identity_key)?;
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
writeln!(
f,
"Host: {} (bind address: {})",
-1
View File
@@ -1 +0,0 @@
Cargo.lock
+2037
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -27,3 +27,18 @@ 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"
+5 -4
View File
@@ -9,13 +9,14 @@ 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 = "1.0.0"
cosmwasm-storage = "1.0.0"
cw-storage-plus = "0.13.4"
cw-controllers = "0.13.4"
cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
cw-storage-plus = { workspace = true }
cw-controllers = { workspace = true }
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
+6 -6
View File
@@ -11,18 +11,18 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
nym-coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut-dkg" }
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" }
cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
cw-storage-plus = { workspace = true }
cw-controllers = { workspace = true }
cw4 = { workspace = true }
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = "1.0.23"
[dev-dependencies]
cw-multi-test = { version = "0.13.4" }
cw-multi-test = { workspace = true }
cw4-group = { path = "../multisig/cw4-group" }
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
lazy_static = "1.4"
+9 -8
View File
@@ -7,18 +7,19 @@ 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 = "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"
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 }
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
@@ -26,7 +27,7 @@ thiserror = "1.0.23"
nym-coconut-bandwidth = { path = "../coconut-bandwidth" }
nym-coconut-dkg = { path = "../coconut-dkg" }
cw-multi-test = { version = "0.13.4" }
cw-multi-test = { workspace = true }
cw3-flex-multisig = { path = "../multisig/cw3-flex-multisig" }
cw4-group = { path = "../multisig/cw4-group" }
+11 -9
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-mixnet-contract"
version = "1.2.0-pre.0"
version = "1.2.0"
description = "Nym mixnet contract"
edition = { workspace = true }
authors = { workspace = true }
@@ -22,14 +22,15 @@ 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.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" }
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"}
cosmwasm-std = "1.0.0"
cosmwasm-storage = "1.0.0"
cw2 = { version = "0.13.4" }
cw-storage-plus = "0.13.4"
cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
cosmwasm-derive = { workspace = true }
cw2 = { workspace = true }
cw-storage-plus = { workspace = true }
bs58 = "0.4.0"
schemars = "0.8"
@@ -39,8 +40,9 @@ time = { version = "0.3", features = ["macros"] }
semver = { version = "1.0.16", default-features = false }
[dev-dependencies]
cosmwasm-schema = "1.0.0"
cosmwasm-schema = { workspace = true }
rand_chacha = "0.2"
#rand = "0.7"
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
[build-dependencies]
+30 -32
View File
@@ -39,43 +39,41 @@ pub const FAMILIES_DEFAULT_RETRIEVAL_LIMIT: u32 = 10;
pub const FAMILIES_MAX_RETRIEVAL_LIMIT: u32 = 20;
// storage keys
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 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 GATEWAYS_PK_NAMESPACE: &str = "gt";
pub const GATEWAYS_OWNER_IDX_NAMESPACE: &str = "gto";
pub(crate) const GATEWAYS_PK_NAMESPACE: &str = "gt";
pub(crate) const GATEWAYS_OWNER_IDX_NAMESPACE: &str = "gto";
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 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 LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
pub const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
pub(crate) const LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
pub(crate) const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
pub const CONTRACT_STATE_KEY: &str = "state";
pub(crate) const CONTRACT_STATE_KEY: &str = "state";
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 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 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 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 REWARDING_PARAMS_KEY: &str = "rparams";
pub const PENDING_REWARD_POOL_KEY: &str = "prp";
pub const MIXNODES_REWARDING_PK_NAMESPACE: &str = "mnr";
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 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";
pub(crate) const FAMILIES_INDEX_NAMESPACE: &str = "faml2";
pub(crate) const FAMILIES_MAP_NAMESPACE: &str = "fam2";
pub(crate) const MEMBERS_MAP_NAMESPACE: &str = "memb2";
-3
View File
@@ -581,9 +581,6 @@ 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?)
+50 -62
View File
@@ -305,77 +305,71 @@ 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;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
use crate::support::tests::{fixtures, test_helpers};
use cosmwasm_std::testing::{mock_env, mock_info};
#[test]
fn test_family_crud() {
let mut test = TestSetup::new();
let env = test.env();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let head = "alice";
let malicious_head = "timmy";
let member = "bob";
let minimum_pledge = minimum_mixnode_pledge(test.deps().storage).unwrap();
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 cost_params = fixtures::mix_node_cost_params_fixture();
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);
let member = "bob";
let (member_mixnode, member_sig, _) =
test_helpers::mixnode_with_signature(&mut rng, member);
// we are informed that we didn't send enough funds
crate::mixnodes::transactions::try_add_mixnode(
test.deps_mut(),
deps.as_mut(),
env.clone(),
mock_info(head, &[minimum_pledge.clone()]),
head_mixnode.clone(),
cost_params.clone(),
head_bond_sig,
head_sig.clone(),
)
.unwrap();
crate::mixnodes::transactions::try_add_mixnode(
test.deps_mut(),
deps.as_mut(),
env.clone(),
mock_info(malicious_head, &[minimum_pledge.clone()]),
malicious_mixnode,
cost_params.clone(),
malicious_bond_sig,
malicious_sig.clone(),
)
.unwrap();
crate::mixnodes::transactions::try_add_mixnode(
test.deps_mut(),
deps.as_mut(),
env,
mock_info(member, &[minimum_pledge]),
member_mixnode.clone(),
cost_params,
member_bond_sig,
member_sig.clone(),
)
.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();
try_create_family(deps.as_mut(), mock_info(head, &[]), head_sig, "test").unwrap();
let family_head = FamilyHead::new(&head_mixnode.identity_key);
assert!(get_family(&family_head, test.deps().storage).is_ok());
assert!(get_family(&family_head, &deps.storage).is_ok());
let nope = try_create_family(
test.deps_mut(),
deps.as_mut(),
mock_info(malicious_head, &[]),
old_style_malicious_head_sig,
malicious_sig,
"test",
);
@@ -387,11 +381,11 @@ mod test {
},
}
let family = get_family_by_label("test", test.deps().storage).unwrap();
let family = get_family_by_label("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(), test.deps().storage).unwrap();
let family = get_family_by_head(family_head.identity(), &deps.storage).unwrap();
assert_eq!(family.head_identity(), family_head.identity());
let join_signature = head_keypair
@@ -400,47 +394,41 @@ mod test {
.to_base58_string();
try_join_family(
test.deps_mut(),
deps.as_mut(),
mock_info(member, &[]),
Some(old_style_member_sig.clone()),
Some(member_sig.clone()),
join_signature.clone(),
head_mixnode.identity_key.clone(),
)
.unwrap();
let family = get_family(&family_head, test.deps().storage).unwrap();
let family = get_family(&family_head, &deps.storage).unwrap();
assert!(
is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
assert!(is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
try_leave_family(
test.deps_mut(),
deps.as_mut(),
mock_info(member, &[]),
old_style_member_sig.clone(),
member_sig.clone(),
head_mixnode.identity_key.clone(),
)
.unwrap();
let family = get_family(&family_head, test.deps().storage).unwrap();
assert!(
!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
let family = get_family(&family_head, &deps.storage).unwrap();
assert!(!is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
try_join_family(
test.deps_mut(),
deps.as_mut(),
mock_info(member, &[]),
Some(old_style_member_sig),
join_signature,
head_mixnode.identity_key,
Some(member_sig.clone()),
join_signature.clone(),
head_mixnode.identity_key.clone(),
)
.unwrap();
let family = get_family(&family_head, test.deps().storage).unwrap();
let family = get_family(&family_head, &deps.storage).unwrap();
assert!(
is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
assert!(is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
// try_head_kick_member(
// deps.as_mut(),
@@ -450,8 +438,8 @@ mod test {
// )
// .unwrap();
// let family = get_family(&family_head, test.deps().storage).unwrap();
// assert!(!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap());
// let family = get_family(&family_head, &deps.storage).unwrap();
// assert!(!is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
}
#[cfg(test)]
@@ -468,7 +456,7 @@ mod test {
let head = "alice";
let (_, keypair) = test.add_dummy_mixnode_with_proxy_and_keypair(head, None);
let (_, keypair) = test.add_dummy_mixnode_with_keypair(head, None);
let sig = keypair.private_key().sign_text(head);
let res = try_create_family_on_behalf(
@@ -507,7 +495,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_proxy_and_keypair(new_member, None);
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
// TODO: those signatures are WRONG and have to be c hanged
let join_signature = head_keys
@@ -554,7 +542,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_proxy_and_keypair(new_member, None);
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
// TODO: those signatures are WRONG and have to be changed
let join_signature = head_keys
@@ -611,7 +599,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_proxy_and_keypair(new_member, None);
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
// TODO: those signatures are WRONG and have to be c hanged
let join_signature = head_keys
@@ -625,7 +613,7 @@ mod test {
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
new_member.to_string(),
Some(member_sig),
Some(member_sig.clone()),
join_signature,
head_identity,
)
-1
View File
@@ -2,6 +2,5 @@
// SPDX-License-Identifier: Apache-2.0
pub mod queries;
pub mod signature_helpers;
pub mod storage;
pub mod transactions;
+46 -27
View File
@@ -62,7 +62,6 @@ 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]
@@ -74,22 +73,27 @@ pub(crate) mod tests {
#[test]
fn gateways_paged_retrieval_obeys_limits() {
let mut test = TestSetup::new();
test.add_dummy_gateways(1000);
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let limit = 2;
test_helpers::add_dummy_gateways(&mut rng, deps.as_mut(), env, 1000);
let page1 = query_gateways_paged(test.deps(), None, Option::from(limit)).unwrap();
let page1 = query_gateways_paged(deps.as_ref(), None, Option::from(limit)).unwrap();
assert_eq!(limit, page1.nodes.len() as u32);
}
#[test]
fn gateways_paged_retrieval_has_default_limit() {
let mut test = TestSetup::new();
test.add_dummy_gateways(1000);
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);
// query without explicitly setting a limit
let page1 = query_gateways_paged(test.deps(), None, None).unwrap();
let page1 = query_gateways_paged(deps.as_ref(), None, None).unwrap();
assert_eq!(
GATEWAY_BOND_DEFAULT_RETRIEVAL_LIMIT,
@@ -99,12 +103,15 @@ pub(crate) mod tests {
#[test]
fn gateways_paged_retrieval_has_max_limit() {
let mut test = TestSetup::new();
test.add_dummy_gateways(1000);
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);
// 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(test.deps(), None, Option::from(crazy_limit)).unwrap();
let page1 = query_gateways_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap();
// we default to a decent sized upper bound instead
let expected_limit = GATEWAY_BOND_MAX_RETRIEVAL_LIMIT;
@@ -114,27 +121,25 @@ 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,
deps.as_ref(),
stake.clone(),
&sender,
);
let (msg, identity) = tests::messages::valid_bond_gateway_msg(&mut rng, &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(), &stake);
let info = mock_info(
&sender_identities[0].0.clone(),
&tests::fixtures::good_gateway_pledge(),
);
execute(deps.as_mut(), mock_env(), info, messages[0].clone()).unwrap();
let per_page = 2;
@@ -195,29 +200,43 @@ pub(crate) mod tests {
#[test]
fn query_for_gateway_owner_works() {
let mut test = TestSetup::new();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
// "fred" does not own a mixnode if there are no mixnodes
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
assert!(res.gateway.is_none());
// gateway was added to "bob", "fred" still does not own one
test.add_dummy_gateway("bob", 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(),
);
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
assert!(res.gateway.is_none());
// "fred" now owns a gateway!
test.add_dummy_gateway("fred", None);
test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env,
"fred",
tests::fixtures::good_gateway_pledge(),
);
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
let res = query_owned_gateway(deps.as_ref(), "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(test.deps_mut(), mock_info("fred", &[]))
crate::gateways::transactions::try_remove_gateway(deps.as_mut(), mock_info("fred", &[]))
.unwrap();
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
assert!(res.gateway.is_none());
}
}
@@ -1,34 +0,0 @@
// 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)
}
}
+76 -129
View File
@@ -2,17 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
use super::storage;
use crate::gateways::signature_helpers::verify_gateway_bonding_signature;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::signing::storage as signing_storage;
use crate::support::helpers::{
ensure_no_existing_bond, ensure_sent_by_vesting_contract, validate_pledge,
ensure_no_existing_bond, ensure_sent_by_vesting_contract, validate_node_identity_signature,
validate_pledge,
};
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
use mixnet_contract_common::{Gateway, GatewayBond};
use nym_contracts_common::signing::MessageSignature;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
pub fn try_add_gateway(
@@ -20,7 +18,7 @@ pub fn try_add_gateway(
env: Env,
info: MessageInfo,
gateway: Gateway,
owner_signature: MessageSignature,
owner_signature: String,
) -> Result<Response, MixnetContractError> {
_try_add_gateway(
deps,
@@ -39,7 +37,7 @@ pub fn try_add_gateway_on_behalf(
info: MessageInfo,
gateway: Gateway,
owner: String,
owner_signature: MessageSignature,
owner_signature: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
@@ -56,15 +54,13 @@ 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: MessageSignature,
owner_signature: String,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// check if the pledge contains any funds of the appropriate denomination
@@ -86,18 +82,13 @@ pub(crate) fn _try_add_gateway(
}
// check if this sender actually owns the gateway by checking the signature
verify_gateway_bonding_signature(
validate_node_identity_signature(
deps.as_ref(),
owner.clone(),
proxy.clone(),
pledge.clone(),
gateway.clone(),
owner_signature,
&owner,
&owner_signature,
&gateway.identity_key,
)?;
// 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(),
@@ -191,41 +182,39 @@ pub(crate) fn _try_remove_gateway(
#[cfg(test)]
pub mod tests {
use super::*;
use crate::contract::execute;
use crate::gateways::queries;
use crate::gateways::transactions::{
try_add_gateway, try_add_gateway_on_behalf, try_remove_gateway_on_behalf,
};
use crate::interval::pending_events;
use crate::mixnet_contract_settings::storage::minimum_gateway_pledge;
use crate::support::tests;
use crate::support::tests::fixtures;
use crate::support::tests::fixtures::{good_gateway_pledge, good_mixnode_pledge};
use crate::support::tests::fixtures::{good_gateway_pledge, TEST_COIN_DENOM};
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{Addr, BankMsg, Response, Uint128};
use crate::support::tests::{fixtures, test_helpers};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coin, Addr, BankMsg, Response, Uint128};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::new_gateway_unbonding_event;
use mixnet_contract_common::ExecuteMsg;
#[test]
fn gateway_add() {
let mut test = TestSetup::new();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
// if we fail validation (by say not sending enough funds
let sender = "alice";
let minimum_pledge = minimum_gateway_pledge(test.deps().storage).unwrap();
let minimum_pledge = minimum_gateway_pledge(deps.as_ref().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.gateway_with_signature(sender, Some(vec![insufficient_pledge.clone()]));
let (gateway, sig) = test_helpers::gateway_with_signature(&mut rng, sender);
let env = test.env();
let result = try_add_gateway(
test.deps_mut(),
deps.as_mut(),
env.clone(),
info,
gateway.clone(),
@@ -244,23 +233,47 @@ 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.add_dummy_gateway(sender, None);
test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env.clone(),
sender,
fixtures::good_gateway_pledge(),
);
// it fails
let result = try_add_gateway(test.deps_mut(), env.clone(), info, gateway, sig);
let result = try_add_gateway(deps.as_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.add_dummy_mixnode(sender2, None);
let mix_id = test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
sender2,
vec![coin(100_000_000, TEST_COIN_DENOM)],
);
let info = mock_info(sender2, &fixtures::good_gateway_pledge());
let (gateway, sig) = test.gateway_with_signature(sender2, None);
let (gateway, sig) = test_helpers::gateway_with_signature(&mut rng, sender2);
let result = try_add_gateway(
test.deps_mut(),
deps.as_mut(),
env.clone(),
info.clone(),
gateway.clone(),
@@ -269,84 +282,12 @@ pub mod tests {
assert_eq!(Err(MixnetContractError::AlreadyOwnsMixnode), result);
// but after he unbonds it, it's all fine again
pending_events::unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
pending_events::unbond_mixnode(deps.as_mut(), &env, 123, mix_id).unwrap();
let result = try_add_gateway(test.deps_mut(), env, info, gateway, sig);
let result = try_add_gateway(deps.as_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();
@@ -356,7 +297,7 @@ pub mod tests {
let vesting_contract = test.vesting_contract();
let owner = "alice";
let (gateway, sig) = test.gateway_with_signature(owner, None);
let (gateway, sig) = test_helpers::gateway_with_signature(&mut test.rng, owner);
let res = try_add_gateway_on_behalf(
test.deps_mut(),
@@ -379,13 +320,14 @@ pub mod tests {
#[test]
fn gateway_remove() {
let mut test = TestSetup::new();
let env = test.env();
let mut deps = test_helpers::init_contract();
let mut rng = test_helpers::test_rng();
let env = mock_env();
// try unbond when no nodes exist yet
let info = mock_info("anyone", &[]);
let msg = ExecuteMsg::UnbondGateway {};
let result = execute(test.deps_mut(), env.clone(), info, msg);
let result = execute(deps.as_mut(), mock_env(), info, msg);
// we're told that there is no node for our address
assert_eq!(
@@ -396,12 +338,18 @@ pub mod tests {
);
// let's add a node owned by bob
test.add_dummy_gateway("bob", None);
test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env.clone(),
"bob",
fixtures::good_gateway_pledge(),
);
// attempt to unbond fred's node, which doesn't exist
let info = mock_info("fred", &[]);
let msg = ExecuteMsg::UnbondGateway {};
let result = execute(test.deps_mut(), env.clone(), info, msg);
let result = execute(deps.as_mut(), mock_env(), info, msg);
assert_eq!(
result,
Err(MixnetContractError::NoAssociatedGatewayBond {
@@ -410,27 +358,28 @@ pub mod tests {
);
// bob's node is still there
let nodes = queries::query_gateways_paged(test.deps(), None, None)
.unwrap()
.nodes;
let nodes = tests::queries::get_gateways(&mut deps);
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.add_dummy_gateway("fred", None);
let fred_identity = test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env,
"fred",
tests::fixtures::good_gateway_pledge(),
);
// let's make sure we now have 2 nodes:
let nodes = queries::query_gateways_paged(test.deps(), None, None)
.unwrap()
.nodes;
assert_eq!(2, nodes.len());
assert_eq!(2, tests::queries::get_gateways(&mut deps).len());
// unbond fred's node
let info = mock_info("fred", &[]);
let msg = ExecuteMsg::UnbondGateway {};
let remove_fred = execute(test.deps_mut(), env, info.clone(), msg).unwrap();
let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
// we should see a funds transfer from the contract back to fred
let expected_message = BankMsg::Send {
@@ -452,11 +401,9 @@ pub mod tests {
assert_eq!(expected_response, remove_fred);
// only 1 node now exists, owned by bob:
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());
let gateway_bonds = tests::queries::get_gateways(&mut deps);
assert_eq!(1, gateway_bonds.len());
assert_eq!(&Addr::unchecked("bob"), gateway_bonds[0].owner());
}
#[test]
@@ -327,6 +327,14 @@ 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(
-1
View File
@@ -14,7 +14,6 @@ mod mixnet_contract_settings;
mod mixnodes;
mod queued_migrations;
mod rewards;
mod signing;
mod support;
#[cfg(feature = "contract-testing")]
-1
View File
@@ -3,6 +3,5 @@
pub mod helpers;
pub mod queries;
pub mod signature_helpers;
pub mod storage;
pub mod transactions;
+147 -71
View File
@@ -273,24 +273,30 @@ 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 test = TestSetup::new();
test.add_dummy_mixnodes(1000);
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let limit = 2;
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(limit)).unwrap();
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();
assert_eq!(limit, page1.nodes.len() as u32);
}
#[test]
fn has_default_limit() {
let mut test = TestSetup::new();
test.add_dummy_mixnodes(1000);
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);
// query without explicitly setting a limit
let page1 = query_mixnode_bonds_paged(test.deps(), None, None).unwrap();
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, None).unwrap();
assert_eq!(
MIXNODE_BOND_DEFAULT_RETRIEVAL_LIMIT,
@@ -300,12 +306,14 @@ pub(crate) mod tests {
#[test]
fn has_max_limit() {
let mut test = TestSetup::new();
test.add_dummy_mixnodes(1000);
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);
// query with a crazily high limit in an attempt to use too many resources
let crazy_limit = 1000;
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(crazy_limit)).unwrap();
let page1 = query_mixnode_bonds_paged(deps.as_ref(), 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);
@@ -314,43 +322,63 @@ 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 test = TestSetup::new();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
test.add_dummy_mixnode("addr1", None);
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr1",
good_mixnode_pledge(),
);
let per_page = 2;
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(per_page)).unwrap();
// page should have 1 result on it
assert_eq!(1, page1.nodes.len());
// save another
test.add_dummy_mixnode("addr2", None);
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr2",
good_mixnode_pledge(),
);
// page1 should have 2 results on it
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(per_page)).unwrap();
assert_eq!(2, page1.nodes.len());
test.add_dummy_mixnode("addr3", None);
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr3",
good_mixnode_pledge(),
);
// page1 still has the same 2 results
let another_page1 =
query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
query_mixnode_bonds_paged(deps.as_ref(), 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(test.deps(), Some(start_after), Some(per_page)).unwrap();
let page2 = query_mixnode_bonds_paged(deps.as_ref(), Some(start_after), Some(per_page))
.unwrap();
assert_eq!(1, page2.nodes.len());
// save another one
test.add_dummy_mixnode("addr4", None);
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "addr4", good_mixnode_pledge());
let page2 =
query_mixnode_bonds_paged(test.deps(), Some(start_after), Some(per_page)).unwrap();
let page2 = query_mixnode_bonds_paged(deps.as_ref(), 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());
@@ -360,24 +388,29 @@ 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 test = TestSetup::new();
test.add_dummy_mixnodes(1000);
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let limit = 2;
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(limit)).unwrap();
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();
assert_eq!(limit, page1.nodes.len() as u32);
}
#[test]
fn has_default_limit() {
let mut test = TestSetup::new();
test.add_dummy_mixnodes(1000);
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);
// query without explicitly setting a limit
let page1 = query_mixnodes_details_paged(test.deps(), None, None).unwrap();
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, None).unwrap();
assert_eq!(
MIXNODE_DETAILS_DEFAULT_RETRIEVAL_LIMIT,
@@ -387,12 +420,15 @@ pub(crate) mod tests {
#[test]
fn has_max_limit() {
let mut test = TestSetup::new();
test.add_dummy_mixnodes(1000);
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);
// query with a crazily high limit in an attempt to use too many resources
let crazy_limit = 1000;
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(crazy_limit)).unwrap();
let page1 =
query_mixnodes_details_paged(deps.as_ref(), None, Some(crazy_limit)).unwrap();
// we default to a decent sized upper bound instead
assert_eq!(
@@ -404,44 +440,64 @@ 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 test = TestSetup::new();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
test.add_dummy_mixnode("addr1", None);
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr1",
good_mixnode_pledge(),
);
let per_page = 2;
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, Some(per_page)).unwrap();
// page should have 1 result on it
assert_eq!(1, page1.nodes.len());
// save another
test.add_dummy_mixnode("addr2", None);
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr2",
good_mixnode_pledge(),
);
// page1 should have 2 results on it
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, Some(per_page)).unwrap();
assert_eq!(2, page1.nodes.len());
test.add_dummy_mixnode("addr3", None);
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
"addr3",
good_mixnode_pledge(),
);
// page1 still has the same 2 results
let another_page1 =
query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
query_mixnodes_details_paged(deps.as_ref(), 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(test.deps(), Some(start_after), Some(per_page))
query_mixnodes_details_paged(deps.as_ref(), Some(start_after), Some(per_page))
.unwrap();
assert_eq!(1, page2.nodes.len());
// save another one
test.add_dummy_mixnode("addr4", None);
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "addr4", good_mixnode_pledge());
let page2 =
query_mixnodes_details_paged(test.deps(), Some(start_after), Some(per_page))
query_mixnodes_details_paged(deps.as_ref(), Some(start_after), Some(per_page))
.unwrap();
// now we have 2 pages, with 2 results on the second page
@@ -1079,18 +1135,26 @@ pub(crate) mod tests {
#[test]
fn query_for_owned_mixnode() {
let mut test = TestSetup::new();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let address = "mix-owner".to_string();
// when it doesnt exist
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
assert!(res.mixnode_details.is_none());
assert_eq!(address, res.address);
// when it [fully] exists
let id = test.add_dummy_mixnode(&address, None);
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
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 details = res.mixnode_details.unwrap();
assert_eq!(address, details.bond_information.owner);
assert_eq!(
@@ -1107,27 +1171,30 @@ pub(crate) mod tests {
rewarding_details.delegates = Decimal::raw(12345);
rewarding_details.unique_delegations = 1;
rewards_storage::MIXNODE_REWARDING
.save(test.deps_mut().storage, id, &rewarding_details)
.save(deps.as_mut().storage, id, &rewarding_details)
.unwrap();
pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, id).unwrap();
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), 123, id).unwrap();
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
assert!(res.mixnode_details.is_none());
assert_eq!(address, res.address);
}
#[test]
fn query_for_mixnode_details() {
let mut test = TestSetup::new();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
// no node under this id
let res = query_mixnode_details(test.deps(), 42).unwrap();
let res = query_mixnode_details(deps.as_ref(), 42).unwrap();
assert!(res.mixnode_details.is_none());
assert_eq!(42, res.mix_id);
// it exists
let mix_id = test.add_dummy_mixnode("foomp", None);
let res = query_mixnode_details(test.deps(), mix_id).unwrap();
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 details = res.mixnode_details.unwrap();
assert_eq!(mix_id, details.bond_information.mix_id);
assert_eq!(
@@ -1160,15 +1227,18 @@ pub(crate) mod tests {
#[test]
fn query_for_mixnode_rewarding_details() {
let mut test = TestSetup::new();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
// no node under this id
let res = query_mixnode_rewarding_details(test.deps(), 42).unwrap();
let res = query_mixnode_rewarding_details(deps.as_ref(), 42).unwrap();
assert!(res.rewarding_details.is_none());
assert_eq!(42, res.mix_id);
let mix_id = test.add_dummy_mixnode("foomp", None);
let res = query_mixnode_rewarding_details(test.deps(), mix_id).unwrap();
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 details = res.rewarding_details.unwrap();
assert_eq!(
fixtures::mix_node_cost_params_fixture(),
@@ -1179,74 +1249,80 @@ pub(crate) mod tests {
#[test]
fn query_for_unbonded_mixnode() {
let mut test = TestSetup::new();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let sender = "mix-owner";
// no node under this id
let res = query_unbonded_mixnode(test.deps(), 42).unwrap();
let res = query_unbonded_mixnode(deps.as_ref(), 42).unwrap();
assert!(res.unbonded_info.is_none());
assert_eq!(42, res.mix_id);
// add and unbond the mixnode
let mix_id = test.add_dummy_mixnode(sender, None);
pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, mix_id).unwrap();
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 res = query_unbonded_mixnode(test.deps(), mix_id).unwrap();
let res = query_unbonded_mixnode(deps.as_ref(), 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 test = TestSetup::new();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
// no node under this id
let res = query_stake_saturation(test.deps(), 42).unwrap();
let res = query_stake_saturation(deps.as_ref(), 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(test.deps().storage)
.load(deps.as_ref().storage)
.unwrap();
let saturation_point = rewarding_params.interval.stake_saturation_point;
let mix_id = test.add_dummy_mixnode("foomp", None);
let mix_id =
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "foomp", good_mixnode_pledge());
// 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(test.deps(), mix_id).unwrap();
let res = query_stake_saturation(deps.as_ref(), 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(test.deps().storage, mix_id)
.load(deps.as_ref().storage, mix_id)
.unwrap();
mix_rewarding.operator = saturation_point;
rewards_storage::MIXNODE_REWARDING
.save(test.deps_mut().storage, mix_id, &mix_rewarding)
.save(deps.as_mut().storage, mix_id, &mix_rewarding)
.unwrap();
let res = query_stake_saturation(test.deps(), mix_id).unwrap();
let res = query_stake_saturation(deps.as_ref(), 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(test.deps().storage, mix_id)
.load(deps.as_ref().storage, mix_id)
.unwrap();
mix_rewarding.delegates = mix_rewarding.operator * Decimal::percent(150);
rewards_storage::MIXNODE_REWARDING
.save(test.deps_mut().storage, mix_id, &mix_rewarding)
.save(deps.as_mut().storage, mix_id, &mix_rewarding)
.unwrap();
let res = query_stake_saturation(test.deps(), mix_id).unwrap();
let res = query_stake_saturation(deps.as_ref(), 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);
@@ -1,36 +0,0 @@
// 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)
}
}
+82 -142
View File
@@ -9,11 +9,10 @@ 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_pledge,
ensure_proxy_match, ensure_sent_by_vesting_contract, validate_node_identity_signature,
validate_pledge,
};
use cosmwasm_std::{coin, Addr, Coin, DepsMut, Env, MessageInfo, Response, Storage};
use mixnet_contract_common::error::MixnetContractError;
@@ -25,7 +24,6 @@ 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,
@@ -63,7 +61,7 @@ pub fn try_add_mixnode(
info: MessageInfo,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: String,
) -> Result<Response, MixnetContractError> {
_try_add_mixnode(
deps,
@@ -84,7 +82,7 @@ pub fn try_add_mixnode_on_behalf(
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner: String,
owner_signature: MessageSignature,
owner_signature: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
@@ -103,9 +101,6 @@ 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<'_>,
@@ -114,7 +109,7 @@ fn _try_add_mixnode(
cost_params: MixNodeCostParams,
pledge: Vec<Coin>,
owner: Addr,
owner_signature: MessageSignature,
owner_signature: String,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// check if the pledge contains any funds of the appropriate denomination
@@ -131,19 +126,13 @@ 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
verify_mixnode_bonding_signature(
validate_node_identity_signature(
deps.as_ref(),
owner.clone(),
proxy.clone(),
pledge.clone(),
mixnode.clone(),
cost_params.clone(),
owner_signature,
&owner,
&owner_signature,
&mixnode.identity_key,
)?;
// 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,
@@ -404,7 +393,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_info;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{Order, StdResult, Uint128};
use mixnet_contract_common::{
EpochState, EpochStatus, ExecuteMsg, Layer, LayerDistribution, Percent,
@@ -412,23 +401,23 @@ pub mod tests {
#[test]
fn mixnode_add() {
let mut test = TestSetup::new();
let env = test.env();
let mut deps = test_helpers::init_contract();
let env = mock_env();
let mut rng = test_helpers::test_rng();
let sender = "alice";
let minimum_pledge = minimum_mixnode_pledge(test.deps().storage).unwrap();
let minimum_pledge = minimum_mixnode_pledge(deps.as_ref().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.mixnode_with_signature(sender, Some(vec![insufficient_pledge.clone()]));
let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut rng, sender);
let cost_params = fixtures::mix_node_cost_params_fixture();
// we are informed that we didn't send enough funds
let result = try_add_mixnode(
test.deps_mut(),
deps.as_mut(),
env.clone(),
info,
mixnode.clone(),
@@ -446,12 +435,31 @@ 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.add_dummy_mixnode(sender, None);
test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
sender,
fixtures::good_mixnode_pledge(),
);
// it fails
let result = try_add_mixnode(
test.deps_mut(),
deps.as_mut(),
env.clone(),
info,
mixnode,
@@ -463,13 +471,19 @@ pub mod tests {
// the same holds if the user already owns a gateway
let sender2 = "gateway-owner";
test.add_dummy_gateway(sender2, None);
test_helpers::add_gateway(
&mut rng,
deps.as_mut(),
env.clone(),
sender2,
tests::fixtures::good_gateway_pledge(),
);
let info = mock_info(sender2, &tests::fixtures::good_mixnode_pledge());
let (mixnode, sig, _) = test.mixnode_with_signature(sender2, None);
let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut rng, sender2);
let result = try_add_mixnode(
test.deps_mut(),
deps.as_mut(),
env.clone(),
info.clone(),
mixnode.clone(),
@@ -480,14 +494,14 @@ pub mod tests {
// but after he unbonds it, it's all fine again
let msg = ExecuteMsg::UnbondGateway {};
execute(test.deps_mut(), env.clone(), info.clone(), msg).unwrap();
execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
let result = try_add_mixnode(test.deps_mut(), env, info, mixnode, cost_params, sig);
let result = try_add_mixnode(deps.as_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(test.deps().storage, &Addr::unchecked(sender2)).unwrap();
let bond = must_get_mixnode_bond_by_owner(deps.as_ref().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)
@@ -499,84 +513,10 @@ pub mod tests {
layer2: 1,
layer3: 0,
};
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));
assert_eq!(
expected,
storage::LAYERS.load(deps.as_ref().storage).unwrap()
)
}
#[test]
@@ -588,9 +528,10 @@ pub mod tests {
let vesting_contract = test.vesting_contract();
let owner = "alice";
let (mixnode, sig, _) = test.mixnode_with_signature(owner, None);
let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut test.rng, owner);
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,
@@ -979,53 +920,52 @@ pub mod tests {
#[test]
fn adding_mixnode_with_duplicate_sphinx_key_errors_out() {
let mut test = TestSetup::new();
let env = test.env();
let mut deps = test_helpers::init_contract();
let mut rng = test_helpers::test_rng();
let keypair1 = nym_crypto::asymmetric::identity::KeyPair::new(&mut test.rng);
let keypair2 = nym_crypto::asymmetric::identity::KeyPair::new(&mut test.rng);
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 cost_params = fixtures::mix_node_cost_params_fixture();
let mixnode1 = MixNode {
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 {
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 test.rng)
sphinx_key: nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng)
.public_key()
.to_base58_string(),
identity_key: keypair1.public_key().to_base58_string(),
version: "v0.1.2.3".to_string(),
};
// 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());
let cost_params = fixtures::mix_node_cost_params_fixture();
assert!(try_add_mixnode(
test.deps_mut(),
env.clone(),
deps.as_mut(),
mock_env(),
info_alice,
mixnode1,
mixnode.clone(),
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(test.deps_mut(), env, info_bob, mixnode2, cost_params, sig2).is_err()
);
assert!(try_add_mixnode(
deps.as_mut(),
mock_env(),
info_bob,
mixnode,
cost_params,
sig2
)
.is_err());
}
#[cfg(test)]
-5
View File
@@ -1,5 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod queries;
pub mod storage;
-11
View File
@@ -1,11 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::signing::storage::get_signing_nonce;
use cosmwasm_std::{Deps, StdResult};
use nym_contracts_common::signing::Nonce;
pub fn query_current_signing_nonce(deps: Deps<'_>, address: String) -> StdResult<Nonce> {
let address = deps.api.addr_validate(&address)?;
get_signing_nonce(deps.storage, address)
}
-30
View File
@@ -1,30 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::constants::SIGNING_NONCES_NAMESPACE;
use cosmwasm_std::{Addr, StdResult, Storage};
use cw_storage_plus::Map;
use nym_contracts_common::signing::Nonce;
pub const NONCES: Map<'_, Addr, Nonce> = Map::new(SIGNING_NONCES_NAMESPACE);
pub fn get_signing_nonce(storage: &dyn Storage, address: Addr) -> StdResult<Nonce> {
let nonce = NONCES.may_load(storage, address)?.unwrap_or(0);
Ok(nonce)
}
pub fn update_signing_nonce(
storage: &mut dyn Storage,
address: Addr,
value: Nonce,
) -> StdResult<()> {
NONCES.save(storage, address, &value)
}
pub fn increment_signing_nonce(storage: &mut dyn Storage, address: Addr) -> StdResult<()> {
// get the current nonce
let nonce = get_signing_nonce(storage, address.clone())?;
// increment it for the next use
update_signing_nonce(storage, address, nonce + 1)
}
+3 -3
View File
@@ -377,7 +377,7 @@ pub fn validate_node_identity_signature(
signature: &str,
identity: IdentityKeyRef<'_>,
) -> Result<(), MixnetContractError> {
validate_ed25519_signature(deps, owner.as_bytes(), signature, identity)
validate_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_ed25519_signature(deps, family_member.as_bytes(), signature, family_head)
validate_signature(deps, family_member.as_bytes(), signature, family_head)
}
pub(crate) fn validate_ed25519_signature(
pub(crate) fn validate_signature(
deps: Deps<'_>,
signed_bytes: &[u8],
signature: &str,
+9 -18
View File
@@ -1,34 +1,25 @@
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 = 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 keypair = nym_crypto::asymmetric::identity::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();
(
ExecuteMsg::BondGateway {
gateway,
gateway: Gateway {
identity_key: identity_key.clone(),
..tests::fixtures::gateway_fixture()
},
owner_signature,
},
identity_key,
+187 -288
View File
@@ -5,6 +5,8 @@
pub mod fixtures;
#[cfg(test)]
pub mod messages;
#[cfg(test)]
pub mod queries;
#[cfg(test)]
pub mod test_helpers {
@@ -22,8 +24,7 @@ pub mod test_helpers {
use crate::interval::{pending_events, storage as interval_storage};
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnet_contract_settings::storage::{
minimum_gateway_pledge, minimum_mixnode_pledge, rewarding_denom,
rewarding_validator_address,
minimum_mixnode_pledge, rewarding_denom, rewarding_validator_address,
};
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::storage::mixnode_bonds;
@@ -35,11 +36,8 @@ 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::{
good_gateway_pledge, good_mixnode_pledge, TEST_COIN_DENOM,
};
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use cosmwasm_std::testing::mock_dependencies;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::testing::mock_info;
@@ -61,19 +59,13 @@ 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, GatewayBondingPayload, IdentityKey,
InitialRewardingParams, InstantiateMsg, Interval, MixId, MixNode, MixNodeBond,
MixnodeBondingPayload, Percent, RewardedSetNodeStatus, SignableGatewayBondingMsg,
SignableMixNodeBondingMsg,
};
use nym_contracts_common::signing::{
ContractMessageContent, MessageSignature, SignableMessage, SigningAlgorithm, SigningPurpose,
Delegation, EpochState, EpochStatus, Gateway, IdentityKey, InitialRewardingParams,
InstantiateMsg, Interval, MixId, MixNode, MixNodeBond, Percent, RewardedSetNodeStatus,
};
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) {
@@ -198,7 +190,7 @@ pub mod test_helpers {
head: &str,
label: &str,
) -> (MixId, identity::KeyPair) {
let (mix_id, keys) = self.add_dummy_mixnode_with_proxy_and_keypair(head, None);
let (mix_id, keys) = self.add_dummy_mixnode_with_keypair(head, None);
let sig = keys.private_key().sign_text(head);
try_create_family(self.deps_mut(), mock_info(head, &[]), sig, label).unwrap();
@@ -206,57 +198,6 @@ 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();
@@ -264,61 +205,36 @@ pub mod test_helpers {
}
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
};
vec![stake]
let env = self.env();
add_mixnode(&mut self.rng, self.deps.as_mut(), env, owner, vec![stake])
}
pub fn make_gateway_pledge(&self, stake: Option<Uint128>) -> Vec<Coin> {
pub fn add_dummy_mixnode_with_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_gateway_pledge(self.deps.as_ref().storage).unwrap(),
None => minimum_mixnode_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 = self.make_mix_pledge(stake);
let proxy = self.vesting_contract();
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 owner_signature = keypair
.private_key()
.sign(owner.as_bytes())
.to_base58_string();
let mixnode = MixNode {
identity_key,
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
..tests::fixtures::mix_node_fixture()
};
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
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 info = mock_info(proxy.as_str(), &[stake]);
let key = keypair.public_key().to_base58_string();
let current_id_counter = mixnodes_storage::MIXNODE_ID_COUNTER
.may_load(self.deps().storage)
.unwrap()
@@ -329,7 +245,11 @@ pub mod test_helpers {
self.deps_mut(),
env,
info,
mixnode,
MixNode {
identity_key: key,
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
..tests::fixtures::mix_node_fixture()
},
tests::fixtures::mix_node_cost_params_fixture(),
owner.to_string(),
owner_signature,
@@ -345,8 +265,7 @@ pub mod test_helpers {
owner: &str,
stake: Option<Uint128>,
) -> MixId {
self.add_dummy_mixnode_with_proxy_and_keypair(owner, stake)
.0
self.add_dummy_mixnode_with_keypair(owner, stake).0
}
pub fn set_illegal_mixnode_proxy(&mut self, mix_id: MixId, proxy: Addr) {
@@ -393,26 +312,14 @@ 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 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 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 env = self.env();
let info = mock_info(proxy.as_ref(), &[stake]);
@@ -421,7 +328,11 @@ pub mod test_helpers {
self.deps_mut(),
env,
info,
gateway,
Gateway {
identity_key: keypair.public_key().to_base58_string(),
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
..tests::fixtures::gateway_fixture()
},
owner.to_string(),
owner_signature,
)
@@ -440,63 +351,6 @@ 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)
@@ -860,22 +714,6 @@ 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)
@@ -962,54 +800,54 @@ pub mod test_helpers {
perform_pending_interval_actions(deps.branch(), &env, None).unwrap();
}
// 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)
// }
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 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)
// }
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 add_dummy_delegations(mut deps: DepsMut<'_>, env: Env, mix_id: MixId, n: usize) {
for i in 0..n {
@@ -1026,22 +864,39 @@ 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_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_unbonded_mixnodes(
mut rng: impl RngCore + CryptoRng,
@@ -1113,35 +968,79 @@ pub mod test_helpers {
id
}
pub fn mixnode_bonding_sign_payload(
deps: Deps<'_>,
owner: &str,
proxy: Option<Addr>,
mixnode: MixNode,
// 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,
stake: Vec<Coin>,
) -> SignableMixNodeBondingMsg {
let cost_params = tests::fixtures::mix_node_cost_params_fixture();
let nonce =
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
) -> MixId {
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
let owner_signature = keypair
.private_key()
.sign(sender.as_bytes())
.to_base58_string();
let payload = MixnodeBondingPayload::new(mixnode, cost_params);
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
SignableMixNodeBondingMsg::new(nonce, content)
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
}
pub fn gateway_bonding_sign_payload(
deps: Deps<'_>,
owner: &str,
proxy: Option<Addr>,
gateway: Gateway,
// same note as with `add_mixnode`
pub fn add_gateway(
mut rng: impl RngCore + CryptoRng,
deps: DepsMut<'_>,
env: Env,
sender: &str,
stake: Vec<Coin>,
) -> SignableGatewayBondingMsg {
let nonce =
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
) -> String {
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
let owner_signature = keypair
.private_key()
.sign(sender.as_bytes())
.to_base58_string();
let payload = GatewayBondingPayload::new(gateway);
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
SignableGatewayBondingMsg::new(nonce, content)
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
}
fn initial_rewarding_params() -> InitialRewardingParams {
@@ -0,0 +1,39 @@
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 = { 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" }
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 }
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 = { version = "0.13.4" }
cw-multi-test = { workspace = true }
+7 -7
View File
@@ -26,15 +26,15 @@ library = []
[dependencies]
nym-group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" }
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" }
cw-utils = { workspace = true }
cw2 = { workspace = true }
cw4 = { workspace = true }
cw-controllers = { workspace = true }
cw-storage-plus = { workspace = true }
cosmwasm-std = { workspace = true }
schemars = "0.8.1"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" }
[dev-dependencies]
cosmwasm-schema = { version = "1.0.0" }
cosmwasm-schema = { workspace = true }
+9 -7
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-vesting-contract"
version = "1.2.0-pre.1"
version = "1.2.0"
description = "Nym vesting contract"
edition = { workspace = true }
authors = { workspace = true }
@@ -20,13 +20,14 @@ name = "vesting_contract"
crate-type = ["cdylib", "rlib"]
[dependencies]
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.2.0" }
contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", package = "nym-contracts-common", version = "0.2.0" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.2.0" }
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.3.0" }
contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", package = "nym-contracts-common", version = "0.3.0" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.3.0" }
cosmwasm-std = { version = "1.0.0 "}
cw2 = { version = "0.13.4" }
cw-storage-plus = { version = "0.13.4", features = ["iterator"] }
cosmwasm-std = { workspace = true }
cosmwasm-derive = { workspace = true }
cw2 = { workspace = true }
cw-storage-plus = { workspace = true, features = ["iterator"] }
schemars = "0.8"
serde = { version = "1.0", default-features = false, features = ["derive"] }
@@ -38,6 +39,7 @@ rand_chacha = "0.3.1"
base64 = "0.21.0"
hex = "0.4.3"
serde_json = "1.0.66"
cosmwasm-crypto = { workspace = true }
[build-dependencies]
vergen = { version = "=7.4.3", default-features = false, features = ["build", "git", "rustc"] }
+2 -3
View File
@@ -7,7 +7,6 @@ use crate::traits::{
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, NodeFamilies, VestingAccount,
};
use crate::vesting::{populate_vesting_periods, Account};
use contracts_common::signing::MessageSignature;
use contracts_common::ContractBuildInformation;
use cosmwasm_std::{
coin, entry_point, to_binary, Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Order,
@@ -412,7 +411,7 @@ fn try_update_staking_address(
/// Bond a gateway, sends [mixnet_contract_common::ExecuteMsg::BondGatewayOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].
pub fn try_bond_gateway(
gateway: Gateway,
owner_signature: MessageSignature,
owner_signature: String,
amount: Coin,
info: MessageInfo,
env: Env,
@@ -449,7 +448,7 @@ pub fn try_track_unbond_gateway(
pub fn try_bond_mixnode(
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: String,
amount: Coin,
info: MessageInfo,
env: Env,
@@ -1,5 +1,4 @@
use crate::errors::ContractError;
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{Coin, Env, Response, Storage};
use mixnet_contract_common::{
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
@@ -13,7 +12,7 @@ pub trait MixnodeBondingAccount {
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: String,
pledge: Coin,
env: &Env,
storage: &mut dyn Storage,
@@ -51,7 +50,7 @@ pub trait GatewayBondingAccount {
fn try_bond_gateway(
&self,
gateway: Gateway,
owner_signature: MessageSignature,
owner_signature: String,
pledge: Coin,
env: &Env,
storage: &mut dyn Storage,
@@ -2,7 +2,6 @@ use super::PledgeData;
use crate::errors::ContractError;
use crate::storage::MIXNET_CONTRACT_ADDRESS;
use crate::traits::GatewayBondingAccount;
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway};
use vesting_contract_common::events::{
@@ -15,7 +14,7 @@ impl GatewayBondingAccount for Account {
fn try_bond_gateway(
&self,
gateway: Gateway,
owner_signature: MessageSignature,
owner_signature: String,
pledge: Coin,
env: &Env,
storage: &mut dyn Storage,
@@ -5,7 +5,6 @@ use super::Account;
use crate::errors::ContractError;
use crate::storage::MIXNET_CONTRACT_ADDRESS;
use crate::traits::MixnodeBondingAccount;
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::mixnode::MixNodeConfigUpdate;
use mixnet_contract_common::mixnode::MixNodeCostParams;
@@ -33,7 +32,7 @@ impl MixnodeBondingAccount for Account {
&self,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
owner_signature: String,
pledge: Coin,
env: &Env,
storage: &mut dyn Storage,
+6 -7
View File
@@ -47,7 +47,6 @@ mod tests {
use crate::traits::VestingAccount;
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
use crate::vesting::{populate_vesting_periods, Account};
use contracts_common::signing::MessageSignature;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coin, coins, Addr, Coin, Timestamp, Uint128};
use mixnet_contract_common::mixnode::MixNodeCostParams;
@@ -675,7 +674,7 @@ mod tests {
let err = account.try_bond_mixnode(
mix_node.clone(),
cost_params.clone(),
MessageSignature::from(vec![1, 2, 3]),
"alice".to_string(),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
@@ -688,7 +687,7 @@ mod tests {
let ok = account.try_bond_mixnode(
mix_node.clone(),
cost_params.clone(),
MessageSignature::from(vec![1, 2, 3]),
"alice".to_string(),
Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
@@ -705,7 +704,7 @@ mod tests {
let err = account.try_bond_mixnode(
mix_node,
cost_params,
MessageSignature::from(vec![1, 2, 3]),
"alice".to_string(),
Coin {
amount: Uint128::new(10_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
@@ -739,7 +738,7 @@ mod tests {
// Try delegating too much
let err = account.try_bond_gateway(
gateway.clone(),
MessageSignature::from(vec![1, 2, 3]),
"alice".to_string(),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
@@ -751,7 +750,7 @@ mod tests {
let ok = account.try_bond_gateway(
gateway.clone(),
MessageSignature::from(vec![1, 2, 3]),
"alice".to_string(),
Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
@@ -767,7 +766,7 @@ mod tests {
// Try delegating too much again
let err = account.try_bond_gateway(
gateway,
MessageSignature::from(vec![1, 2, 3]),
"alice".to_string(),
Coin {
amount: Uint128::new(500_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
-12
View File
@@ -1,12 +0,0 @@
[package]
name = "cpu-cycles"
version = "0.1.0"
edition = "2021"
build = "build.rs"
links = "cpucycles"
[dependencies]
libc = "0.2.140"
[build-dependencies]
cfg-if = "1"
-65
View File
@@ -1,65 +0,0 @@
use std::{env, path::PathBuf, process::Command};
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let out_path = PathBuf::from(&out_dir);
let source_path = PathBuf::from("libcpucycles")
.canonicalize()
.expect("cannot canonicalize path");
cfg_if::cfg_if! {
if #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "mips", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "arm")))] {
panic!("Unsupported architecture - {}!", env::var("CARGO_CFG_TARGET_ARCH").unwrap(), )
}
};
let mut compile_o_command = Command::new("./configure");
let compile_o_command = compile_o_command
.current_dir(&source_path)
.arg(format!("--prefix={out_dir}"));
match compile_o_command.output() {
Ok(output) => {
if !output.status.success() {
panic!("{:?}", unsafe {
std::str::from_utf8_unchecked(&output.stderr)
})
}
}
Err(e) => panic!("{e}"),
}
let mut compile_o_command = Command::new("make");
let compile_o_command = compile_o_command.current_dir(&source_path).arg("install");
match compile_o_command.output() {
Ok(output) => {
if !output.status.success() {
panic!("{:?}", unsafe {
std::str::from_utf8_unchecked(&output.stderr)
})
}
}
Err(e) => panic!("{e}"),
}
println!(
"cargo:rustc-link-search=native={}",
out_path.join("lib").to_str().unwrap()
);
println!("cargo:rustc-link-lib=static=cpucycles");
let mut compile_o_command = Command::new("make");
let compile_o_command = compile_o_command.current_dir(source_path).arg("clean");
match compile_o_command.output() {
Ok(output) => {
if !output.status.success() {
panic!("{:?}", unsafe {
std::str::from_utf8_unchecked(&output.stderr)
})
}
}
Err(e) => panic!("{e}"),
}
}
-8
View File
@@ -1,8 +0,0 @@
default:
cd build && $(MAKE)
install:
cd build && $(MAKE) install
clean:
cd build && $(MAKE) clean

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