Compare commits

...

73 Commits

Author SHA1 Message Date
Simon Wicky e0b8638163 add cover timing and ack logging 2023-04-20 09:09:12 +00:00
Jon Häggblad fdb5727e5a Rework Poisson process throttling 2023-04-17 13:02:40 +00:00
Simon Wicky 3da131de15 add packets logging 2023-04-17 14:44:05 +02:00
farbanas ba3a6eaeb5 Merge branch 'release/v1.1.14' 2023-04-04 11:38:40 +02:00
farbanas 9d0bc8ab9e update changelogs 2023-04-04 11:38:01 +02:00
farbanas 21611a9434 update versions of contracts in preparation for release v1.1.14 2023-04-04 11:04:44 +02:00
farbanas 78d9030567 update versions in preparation for release v1.1.14 2023-04-04 10:30:20 +02:00
Tommy Verrall f3c16476f9 Merge pull request #3267 from nymtech/feature/gateway-settings-version-and-location
location and version needed in gateway settings updates
2023-04-03 12:11:46 +01:00
fmtabbara 4678059eaf add validation for version and location 2023-04-03 11:22:49 +01:00
fmtabbara a69d4bb457 location and version needed in gateway settings updates 2023-04-03 10:40:08 +01:00
Jędrzej Stuczyński 134ab2f0d6 Stop using rewarding reports as a source of truth for whether mixnodes got rewards (#3227)
use contract information instead
2023-03-27 14:52:40 +01:00
Jędrzej Stuczyński 1a4e7433b2 Bugfix: NR after #3199 (#3225)
* updated NR config template

* upgrading NR config if usingn <= 1.1.13
2023-03-27 12:21:35 +01:00
durch 9d91a018b2 Dont clone reconstructor 2023-03-24 16:22:14 +01:00
Tommy Verrall ae9c1b4070 Merge pull request #3199 from nymtech/chore/group-client-debug-config
chore: tidy up client `Debug` config section
2023-03-24 15:48:00 +03:00
Jędrzej Stuczyński adcd8703d3 attempting to upgrade old config in 'init' 2023-03-23 16:06:22 +00:00
Jędrzej Stuczyński aee4c2d80d Feature/families signatures (#3156)
* wip family creation signatures + cli

* nym-cli commands for creating families

* Changed family join signature inside the contract

* Generating family join permit via nym-cli

* ability to join families via nym-cli

* more strongly typed FamilyHead arguments

* initial work on removing redundant family signatures

* removed all redundant signatures from families in the mixnet contract

* moved up the call stack

* nym-cli family operations

* fixed family related unit tests

* family member kick

* removed family operations from the wallet

* clippy
2023-03-23 16:36:10 +01:00
Jędrzej Stuczyński 5e04f48500 bugfix: drop tasks to connections closed by remote (#3190)
* applied patch #3187

* applied the same concept to the verloc listener
2023-03-23 12:09:43 +01:00
Fouad a7610a7a88 Gateway settings (#2725)
* add gateway settings button

* remove unneeded mixnode type check

* add additional properties to gateway type

* update node settings nav options

* set up gateway update requests

* create gateway settings page

* use update gateway validation

* PR updates

* dont show playground on gateways

* set up gateway config update

* fix lint errors in wallet

* run cargo fmt
2023-03-23 10:38:33 +01:00
Tommy Verrall 28e6e9140c Merge pull request #3203 from nymtech/chore/disable-sign-ext
disable sign-ext when using wasm-opt + update wasm-opt
2023-03-23 10:32:58 +02:00
Jędrzej Stuczyński 271a126556 removed migration code from mixnet and vesting contracts (#3207) 2023-03-22 14:27:51 +00:00
Jędrzej Stuczyński 7da444c7a4 clippy 2023-03-22 13:51:38 +00:00
Jędrzej Stuczyński 2a7fd71416 explicitly added old version number to 'oldconfig' 2023-03-22 13:51:38 +00:00
Jędrzej Stuczyński 6ffd211e51 backwards compatibility 2023-03-22 13:51:38 +00:00
Jędrzej Stuczyński f61ed48240 updated templates 2023-03-22 13:51:38 +00:00
Jędrzej Stuczyński 8d3dc405a5 clippy 2023-03-22 13:51:38 +00:00
Jędrzej Stuczyński 21bf0fb27b wasm changes 2023-03-22 13:51:36 +00:00
Jędrzej Stuczyński 7313f56c01 grouped debug config options inside client-core into substructs 2023-03-22 13:49:30 +00:00
Jon Häggblad cfef9e96e6 chore: update nym-connect mobile lock file 2023-03-22 12:17:21 +01:00
Jędrzej Stuczyński 8b9a5cf500 updated installed wasm-opt version to v112 2023-03-22 10:38:39 +00:00
Jędrzej Stuczyński ce94ce058f disabling sign-ext 2023-03-22 10:37:52 +00:00
farbanas 018e4d6079 chore: update lock files 2023-03-22 11:29:27 +01:00
farbanas ebadd9799f fix: add contracts-common back to dependencies 2023-03-22 11:29:25 +01:00
Jędrzej Stuczyński 268fa02b83 fix: restored 'nym-contracts-common' dependency in the mixnet contract 2023-03-22 10:25:42 +00:00
farbanas cb525477e5 fix: update CHANGELOG for contracts 2023-03-22 10:39:48 +01:00
farbanas 4d6d2f0d33 Merge branch 'master' into develop 2023-03-22 10:31:12 +01:00
farbanas c7f8b05604 fix: wallet CHANGELOG 2023-03-22 10:23:19 +01: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
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
Drazen Urch 7e109e7f2d Generalise MessageReceiver (#3042)
* Generalise MessageReceiver

* Generics all the way

* Generalise MessageReceiver

* Generics all the way

* Fix Cargo.lock

---------

Co-authored-by: benedettadavico <benedetta.davico@gmail.com>
2023-03-21 15:25:51 +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
Jon Häggblad 41a63a0985 Tidy top-level Makefile (#3192)
* Reorder Makefile

* split out the fmt targets

* split cargo test

* Split up clippy targets

* Add commit

* Use env variable for no-mobile instead

* Extract out target generation to use function

* Remove commented out code

* Add comment

* Minor tidy
2023-03-20 15:44:13 +01:00
Pierre Dommerc 5a149c5492 feat(nc-mobile): select service provider (#3196)
* feat: select service provider ui

* refactor: adjust ui

* fix: remove dead code
2023-03-20 13:53:29 +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
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
Gala 4f59678ded center layout 2022-12-22 15:31:35 +01:00
Gala 8a1d2af3cf adding changes 2022-12-22 14:44:58 +01:00
249 changed files with 14306 additions and 3158 deletions
@@ -80,7 +80,7 @@ jobs:
components: rustfmt, clippy
- name: Install wasm-opt
run: cargo install wasm-opt
run: cargo install --version 0.112.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.112.0 wasm-opt
- name: Build release contracts
run: make wasm
-2
View File
@@ -39,7 +39,5 @@ validator-api-config.toml
dist
storybook-static
envs/qwerty.env
Cargo.lock
nym-connect/Cargo.lock
.parcel-cache
**/.DS_Store
+32
View File
@@ -4,6 +4,38 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [v1.1.14] (2023-04-04)
- Investigate cause of qwerty validator being in invalid rewarding state ([#3224])
- Fix NR config due to changes in #3199 ([#3223])
- [Issue] Mixnodes and gateway do not close connections properly ([#3187])
- disable sign-ext when using wasm-opt + update wasm-opt ([#3203])
- chore: tidy up client `Debug` config section ([#3199])
[#3224]: https://github.com/nymtech/nym/issues/3224
[#3223]: https://github.com/nymtech/nym/issues/3223
[#3187]: https://github.com/nymtech/nym/issues/3187
[#3203]: https://github.com/nymtech/nym/pull/3203
[#3199]: https://github.com/nymtech/nym/pull/3199
## [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])
- 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
[#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
+399 -442
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -107,6 +107,16 @@ license = "Apache-2.0"
async-trait = "0.1.64"
bip39 = { version = "2.0.0", features = ["zeroize"] }
cfg-if = "1.0.0"
cosmwasm-derive = "=1.0.0"
cosmwasm-schema = "=1.0.0"
cosmwasm-std = "=1.0.0"
cosmwasm-storage = "=1.0.0"
cw-utils = "=0.13.4"
cw-storage-plus = "=0.13.4"
cw2 = { version = "=0.13.4" }
cw3 = { version = "=0.13.4" }
cw3-fixed-multisig = { version = "=0.13.4" }
cw4 = { version = "=0.13.4" }
dotenvy = "0.15.6"
lazy_static = "1.4.0"
log = "0.4"
+59 -112
View File
@@ -1,143 +1,90 @@
# Default target
all: test
test: clippy-all cargo-test wasm fmt
test-no-mobile: clippy-all-no-mobile cargo-test-no-mobile wasm fmt-no-mobile
test-all: test cargo-test-expensive
test-all-no-mobile: test-no-mobile cargo-test-expensive
no-clippy: build cargo-test wasm fmt
no-clippy-no-mobile: build-no-mobile cargo-test-no-mobile wasm fmt-no-mobile
happy: fmt clippy-happy test
happy-no-mobile: fmt-no-mobile clippy-happy-no-mobile test-no-mobile
clippy-all: clippy-all-no-mobile clippy-all-connect-mobile
clippy-all-no-mobile: clippy-main clippy-main-examples clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all-wasm-client
clippy-happy: clippy-happy-no-mobile clippy-happy-connect-mobile
clippy-happy-no-mobile: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect
cargo-test: cargo-test-no-mobile test-connect-mobile
cargo-test-no-mobile: test-main test-contracts test-wallet test-connect
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive
build: build-no-mobile build-connect-mobile
build-no-mobile: build-contracts build-wallet build-main build-main-examples build-connect build-wasm-client
fmt: fmt-no-mobile fmt-connect-mobile
fmt-no-mobile: fmt-main fmt-contracts fmt-wallet fmt-connect fmt-wasm-client
clippy-happy-main:
cargo clippy
# -----------------------------------------------------------------------------
# Define targets for a given workspace
# $(1): name
# $(2): path to workspace
# $(3): extra arguments to cargo
# -----------------------------------------------------------------------------
define add_cargo_workspace
clippy-happy-contracts:
cargo clippy --manifest-path contracts/Cargo.toml --target wasm32-unknown-unknown
clippy-happy-$(1):
cargo clippy --manifest-path $(2)/Cargo.toml $(3)
clippy-happy-wallet:
cargo clippy --manifest-path nym-wallet/Cargo.toml
clippy-$(1):
cargo clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
clippy-happy-connect:
cargo clippy --manifest-path nym-connect/desktop/Cargo.toml
clippy-$(1)-examples:
cargo clippy --manifest-path $(2)/Cargo.toml --workspace --examples -- -D warnings
clippy-happy-connect-mobile:
cargo clippy --manifest-path nym-connect/mobile/src-tauri/Cargo.toml
test-$(1):
cargo test --manifest-path $(2)/Cargo.toml --workspace
clippy-main:
cargo clippy --workspace -- -D warnings
test-$(1)-expensive:
cargo test --manifest-path $(2)/Cargo.toml --workspace -- --ignored
clippy-main-examples:
cargo clippy --workspace --examples -- -D warnings
build-$(1):
cargo build --manifest-path $(2)/Cargo.toml --workspace $(3)
clippy-wasm:
cargo clippy --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --workspace -- -D warnings
build-$(1)-examples:
cargo build --manifest-path $(2)/Cargo.toml --workspace --examples
fmt-$(1):
cargo fmt --manifest-path $(2)/Cargo.toml --all
clippy-all-contracts:
cargo clippy --workspace --manifest-path contracts/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings
clippy-happy: clippy-happy-$(1)
clippy-all: clippy-$(1) clippy-$(1)-examples
cargo-test: test-$(1)
cargo-test-expensive: test-$(1)-expensive
build: build-$(1) build-$(1)-examples
fmt: fmt-$(1)
clippy-all-wallet:
cargo clippy --workspace --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings
endef
clippy-all-connect:
cargo clippy --workspace --manifest-path nym-connect/desktop/Cargo.toml --all-features -- -D warnings
# -----------------------------------------------------------------------------
# Rust workspaces
# -----------------------------------------------------------------------------
clippy-all-connect-mobile:
cargo clippy --workspace --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-features -- -D warnings
# Generate targets for the various cargo workspaces
clippy-all-wasm-client:
cargo clippy --workspace --manifest-path clients/webassembly/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings
$(eval $(call add_cargo_workspace,main,.))
$(eval $(call add_cargo_workspace,contracts,contracts,--target wasm32-unknown-unknown))
$(eval $(call add_cargo_workspace,wasm-client,clients/webassembly,--target wasm32-unknown-unknown))
$(eval $(call add_cargo_workspace,wallet,nym-wallet,))
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
ifndef NYM_NO_MOBILE
$(eval $(call add_cargo_workspace,connect-mobile,nym-connect/mobile/src-tauri))
endif
test-main:
cargo test --workspace
test-main-expensive:
cargo test --workspace -- --ignored
test-contracts:
cargo test --manifest-path contracts/Cargo.toml --all-features
test-contracts-expensive:
cargo test --manifest-path contracts/Cargo.toml --all-features -- --ignored
test-wallet:
cargo test --manifest-path nym-wallet/Cargo.toml --all-features
test-wallet-expensive:
cargo test --manifest-path nym-wallet/Cargo.toml --all-features -- --ignored
test-connect:
cargo test --manifest-path nym-connect/desktop/Cargo.toml --all-features
test-connect-expensive:
cargo test --manifest-path nym-connect/desktop/Cargo.toml --all-features -- --ignored
test-connect-mobile:
cargo test --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-features
test-connect-mobile-expensive:
cargo test --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-features -- --ignored
build-main:
cargo build --workspace
build-main-examples:
cargo build --workspace --examples
build-contracts:
cargo build --manifest-path contracts/Cargo.toml --workspace
build-wallet:
cargo build --manifest-path nym-wallet/Cargo.toml --workspace
build-connect:
cargo build --manifest-path nym-connect/desktop/Cargo.toml --workspace
build-connect-mobile:
cargo build --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --workspace
# -----------------------------------------------------------------------------
# Convenience targets for crates that are already part of the main workspace
# -----------------------------------------------------------------------------
build-explorer-api:
cargo build --manifest-path explorer-api/Cargo.toml --workspace
build-wasm-client:
cargo build --manifest-path clients/webassembly/Cargo.toml --workspace --target wasm32-unknown-unknown
cargo build -p explorer-api
build-nym-cli:
cargo build --release --manifest-path tools/nym-cli/Cargo.toml
cargo build -p nym-cli --release
fmt-main:
cargo fmt --all
fmt-contracts:
cargo fmt --manifest-path contracts/Cargo.toml --all
fmt-wallet:
cargo fmt --manifest-path nym-wallet/Cargo.toml --all
fmt-connect:
cargo fmt --manifest-path nym-connect/desktop/Cargo.toml --all
fmt-connect-mobile:
cargo fmt --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all
fmt-wasm-client:
cargo fmt --manifest-path clients/webassembly/Cargo.toml --all
# -----------------------------------------------------------------------------
# Misc
# -----------------------------------------------------------------------------
wasm:
RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
wasm-opt -Os contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
wasm-opt -Os contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
wasm-opt --disable-sign-ext -Os contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
wasm-opt --disable-sign-ext -Os contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
# NOTE: this seems deprecated an not needed anymore?
mixnet-opt: wasm
cd contracts/mixnet && make opt
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "client-core"
version = "1.1.12"
version = "1.1.14"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.66"
@@ -5,7 +5,11 @@ use crate::{client::replies::reply_storage, config::DebugConfig};
pub fn setup_empty_reply_surb_backend(debug_config: &DebugConfig) -> reply_storage::Empty {
reply_storage::Empty {
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
min_surb_threshold: debug_config
.reply_surbs
.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config
.reply_surbs
.maximum_reply_surb_storage_threshold,
}
}
@@ -35,7 +35,7 @@ use nym_crypto::asymmetric::{encryption, identity};
use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_sphinx::receiver::ReconstructedMessage;
use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
use nym_task::{TaskClient, TaskManager};
use nym_topology::provider_trait::TopologyProvider;
@@ -236,15 +236,15 @@ where
let mut stream = LoopCoverTrafficStream::new(
ack_key,
debug_config.average_ack_delay,
debug_config.average_packet_delay,
debug_config.loop_cover_traffic_average_delay,
debug_config.acknowledgements.average_ack_delay,
debug_config.traffic.average_packet_delay,
debug_config.cover_traffic.loop_cover_traffic_average_delay,
mix_tx,
self_address,
topology_accessor,
);
if let Some(size) = debug_config.use_extended_packet_size {
if let Some(size) = debug_config.traffic.use_extended_packet_size {
log::debug!("Setting extended packet size: {:?}", size);
stream.set_custom_packet_size(size.into());
}
@@ -294,14 +294,15 @@ where
shutdown: TaskClient,
) {
info!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new(
local_encryption_keypair,
query_receiver,
mixnet_receiver,
reply_key_storage,
reply_controller_sender,
)
.start_with_shutdown(shutdown)
let controller: ReceivedMessagesBufferController<SphinxMessageReceiver> =
ReceivedMessagesBufferController::new(
local_encryption_keypair,
query_receiver,
mixnet_receiver,
reply_key_storage,
reply_controller_sender,
);
controller.start_with_shutdown(shutdown)
}
async fn start_gateway_client(
@@ -337,7 +338,9 @@ where
shared_key,
mixnet_message_sender,
ack_sender,
self.debug_config.gateway_response_timeout,
self.debug_config
.gateway_connection
.gateway_response_timeout,
self.bandwidth_controller.take(),
shutdown,
);
@@ -499,7 +502,7 @@ where
);
Self::start_topology_refresher(
topology_provider,
self.debug_config.topology_refresh_rate,
self.debug_config.topology.topology_refresh_rate,
shared_topology_accessor.clone(),
task_manager.subscribe(),
)
@@ -535,7 +538,7 @@ where
self_address,
);
if let Some(size) = self.debug_config.use_extended_packet_size {
if let Some(size) = self.debug_config.traffic.use_extended_packet_size {
log::debug!("Setting extended packet size: {:?}", size);
controller_config.set_custom_packet_size(size.into());
}
@@ -554,7 +557,11 @@ where
task_manager.subscribe(),
);
if !self.debug_config.disable_loop_cover_traffic_stream {
if !self
.debug_config
.cover_traffic
.disable_loop_cover_traffic_stream
{
Self::start_cover_traffic_stream(
self.debug_config,
self.key_manager.ack_key(),
@@ -30,8 +30,12 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
// it will only be happening on the very first run and in practice won't incur huge
// costs since the storage is going to be empty
let mem_store = CombinedReplyStorage::new(
debug_config.minimum_reply_surb_storage_threshold,
debug_config.maximum_reply_surb_storage_threshold,
debug_config
.reply_surbs
.minimum_reply_surb_storage_threshold,
debug_config
.reply_surbs
.maximum_reply_surb_storage_threshold,
);
storage_backend
.init_fresh(&mem_store)
@@ -46,8 +50,12 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
fn setup_inactive_backend(debug_config: &DebugConfig) -> fs_backend::Backend {
info!("creating inactive surb database");
fs_backend::Backend::new_inactive(
debug_config.minimum_reply_surb_storage_threshold,
debug_config.maximum_reply_surb_storage_threshold,
debug_config
.reply_surbs
.minimum_reply_surb_storage_threshold,
debug_config
.reply_surbs
.maximum_reply_surb_storage_threshold,
)
}
@@ -10,6 +10,7 @@ use nym_sphinx::{
chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID},
};
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
/// Module responsible for listening for any data resembling acknowledgements from the network
/// and firing actions to remove them from the 'Pending' state.
@@ -48,10 +49,20 @@ impl AcknowledgementListener {
// because nothing was inserted in the first place
if frag_id == COVER_FRAG_ID {
trace!("Received an ack for a cover message - no need to do anything");
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
println!("cover ack : _{}", time);
return;
}
trace!("Received {} from the mix network", frag_id);
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
println!("real ack : /{:?}/{}", frag_id, time);
self.action_sender
.unbounded_send(Action::new_remove(frag_id))
@@ -9,6 +9,7 @@ use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_task::connections::TransmissionLane;
use rand::{CryptoRng, Rng};
use std::time::{SystemTime, UNIX_EPOCH};
/// Module responsible for dealing with the received messages: splitting them, creating acknowledgements,
/// putting everything into sphinx packets, etc.
@@ -48,6 +49,11 @@ where
lane: TransmissionLane,
) {
// offload reply handling to the dedicated task
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
println!("sending reply: _{}_{}", time, data.len());
self.reply_controller_sender
.send_reply(recipient_tag, data, lane)
}
@@ -58,6 +64,11 @@ where
content: Vec<u8>,
lane: TransmissionLane,
) {
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
println!("sending plain: _{}_{}", time, content.len());
if let Err(err) = self
.message_handler
.try_send_plain_message(recipient, content, lane)
@@ -74,6 +85,11 @@ where
reply_surbs: u32,
lane: TransmissionLane,
) {
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
println!("sending anonymous: _{}_{}", time, content.len());
if let Err(err) = self
.message_handler
.try_send_message_with_reply_surbs(recipient, content, reply_surbs, lane)
@@ -153,25 +153,35 @@ impl Config {
ack_key,
self_recipient,
packet_size: Default::default(),
ack_wait_addition: base_client_debug_config.ack_wait_addition,
ack_wait_multiplier: base_client_debug_config.ack_wait_multiplier,
average_message_sending_delay: base_client_debug_config.message_sending_average_delay,
average_packet_delay_duration: base_client_debug_config.average_packet_delay,
average_ack_delay_duration: base_client_debug_config.average_ack_delay,
ack_wait_addition: base_client_debug_config.acknowledgements.ack_wait_addition,
ack_wait_multiplier: base_client_debug_config
.acknowledgements
.ack_wait_multiplier,
average_message_sending_delay: base_client_debug_config
.traffic
.message_sending_average_delay,
average_packet_delay_duration: base_client_debug_config.traffic.average_packet_delay,
average_ack_delay_duration: base_client_debug_config.acknowledgements.average_ack_delay,
disable_main_poisson_packet_distribution: base_client_debug_config
.traffic
.disable_main_poisson_packet_distribution,
minimum_reply_surb_request_size: base_client_debug_config
.reply_surbs
.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: base_client_debug_config
.reply_surbs
.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: base_client_debug_config
.reply_surbs
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: base_client_debug_config
.reply_surbs
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: base_client_debug_config
.reply_surbs
.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: base_client_debug_config.maximum_reply_surb_age,
maximum_reply_key_age: base_client_debug_config.maximum_reply_key_age,
maximum_reply_surb_age: base_client_debug_config.reply_surbs.maximum_reply_surb_age,
maximum_reply_key_age: base_client_debug_config.reply_surbs.maximum_reply_key_age,
}
}
@@ -24,6 +24,7 @@ use rand::{CryptoRng, Rng};
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use std::time::{SystemTime, UNIX_EPOCH};
#[cfg(not(target_arch = "wasm32"))]
use tokio::time;
@@ -232,7 +233,11 @@ where
return;
}
};
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
println!("cover sent_{}_{:?}",time, self.config.cover_packet_size.size());
(
generate_loop_cover_packet(
&mut self.rng,
@@ -250,6 +255,11 @@ where
)
}
StreamMessage::Real(real_message) => {
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
println!("real sent: /{:?}/{}/{}", real_message.fragment_id, time, real_message.mix_packet.sphinx_packet().payload.len());
(real_message.mix_packet, Some(real_message.fragment_id))
}
};
@@ -297,22 +307,29 @@ where
self.sending_delay_controller.current_multiplier()
);
// Even just a single used slot is enough to signal backpressure
if used_slots > 0 {
if self
.sending_delay_controller
.is_backpressure_currently_detected(used_slots)
{
log::trace!("Backpressure detected");
self.sending_delay_controller.record_backpressure_detected();
}
// If the buffer is running out, slow down the sending rate
// If the buffer is running out, slow down the sending rate by increasing the delay
// multiplier.
if self.mix_tx.capacity() == 0
&& self.sending_delay_controller.not_increased_delay_recently()
{
self.sending_delay_controller.increase_delay_multiplier();
}
// Very carefully step up the sending rate in case it seems like we can solidly handle the
// current rate.
if self.sending_delay_controller.is_sending_reliable() {
// If it looks like we are sending reliably, increase the sending rate by decreasing the
// sending delay multiplier.
if !self
.sending_delay_controller
.was_backpressure_detected_recently()
&& self.sending_delay_controller.not_decreased_delay_recently()
{
self.sending_delay_controller.decrease_delay_multiplier();
}
}
@@ -11,11 +11,14 @@ const INCREASE_DELAY_MIN_CHANGE_INTERVAL_SECS: u64 = 1;
// The minimum time between decreasing the average delay between packets. We don't want to change
// to quickly to keep things somewhat stable. Also there are buffers downstreams meaning we need to
// wait a little to see the effect before we decrease further.
const DECREASE_DELAY_MIN_CHANGE_INTERVAL_SECS: u64 = 30;
const DECREASE_DELAY_MIN_CHANGE_INTERVAL_SECS: u64 = 3;
// The queue length that is required for us to register that backpressure occured. If there are
// more than this many packets waiting to be sent, we consider the channel to be under
// backpressure.
const BACKPRESSURE_THRESHOLD: usize = 10;
// If we enough time passes without any sign of backpressure in the channel, we can consider
// lowering the average delay. The goal is to keep somewhat stable, rather than maxing out
// bandwidth at all times.
const ACCEPTABLE_TIME_WITHOUT_BACKPRESSURE_SECS: u64 = 30;
// lowering the average delay.
const ACCEPTABLE_TIME_WITHOUT_BACKPRESSURE_SECS: u64 = 1;
// The maximum multiplier we apply to the base average Poisson delay.
const MAX_DELAY_MULTIPLIER: u32 = 6;
// The minium multiplier we apply to the base average Poisson delay.
@@ -100,22 +103,27 @@ impl SendingDelayController {
}
}
pub(crate) fn record_backpressure_detected(&mut self) {
self.time_when_backpressure_detected = get_time_now();
}
pub(crate) fn not_increased_delay_recently(&self) -> bool {
get_time_now()
> self.time_when_changed + Duration::from_secs(INCREASE_DELAY_MIN_CHANGE_INTERVAL_SECS)
}
pub(crate) fn is_sending_reliable(&self) -> bool {
let now = get_time_now();
let delay_change_interval = Duration::from_secs(DECREASE_DELAY_MIN_CHANGE_INTERVAL_SECS);
let acceptable_time_without_backpressure =
Duration::from_secs(ACCEPTABLE_TIME_WITHOUT_BACKPRESSURE_SECS);
pub(crate) fn not_decreased_delay_recently(&self) -> bool {
get_time_now()
> self.time_when_changed + Duration::from_secs(DECREASE_DELAY_MIN_CHANGE_INTERVAL_SECS)
}
now > self.time_when_backpressure_detected + acceptable_time_without_backpressure
&& now > self.time_when_changed + delay_change_interval
pub(crate) fn is_backpressure_currently_detected(&self, queue_length: usize) -> bool {
queue_length > BACKPRESSURE_THRESHOLD
}
pub(crate) fn record_backpressure_detected(&mut self) {
self.time_when_backpressure_detected = get_time_now();
}
pub(crate) fn was_backpressure_detected_recently(&self) -> bool {
get_time_now()
< self.time_when_backpressure_detected
+ Duration::from_secs(ACCEPTABLE_TIME_WITHOUT_BACKPRESSURE_SECS)
}
}
@@ -20,6 +20,7 @@ use nym_sphinx::params::ReplySurbKeyDigestAlgorithm;
use nym_sphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage};
use std::collections::HashSet;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
// Buffer Requests to say "hey, send any reconstructed messages to this channel"
// or to say "hey, I'm going offline, don't send anything more to me. Just buffer them instead"
@@ -30,13 +31,13 @@ pub type ReceivedBufferRequestReceiver = mpsc::UnboundedReceiver<ReceivedBufferM
pub type ReconstructedMessagesSender = mpsc::UnboundedSender<Vec<ReconstructedMessage>>;
pub type ReconstructedMessagesReceiver = mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>;
struct ReceivedMessagesBufferInner {
struct ReceivedMessagesBufferInner<R: MessageReceiver> {
messages: Vec<ReconstructedMessage>,
local_encryption_keypair: Arc<encryption::KeyPair>,
// TODO: looking how it 'looks' here, perhaps `MessageReceiver` should be renamed to something
// else instead.
message_receiver: MessageReceiver,
message_receiver: R,
message_sender: Option<ReconstructedMessagesSender>,
// TODO: this will get cleared upon re-running the client
@@ -45,10 +46,16 @@ struct ReceivedMessagesBufferInner {
recently_reconstructed: HashSet<i32>,
}
impl ReceivedMessagesBufferInner {
impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
fn recover_from_fragment(&mut self, fragment_data: &[u8]) -> Option<NymMessage> {
let fragment_len = fragment_data.len();
if nym_sphinx::cover::is_cover(fragment_data) {
trace!("The message was a loop cover message! Skipping it");
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
println!("Cover received_{}_{}",time, fragment_len);
return None;
}
@@ -60,6 +67,12 @@ impl ReceivedMessagesBufferInner {
Ok(frag) => frag,
};
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
println!("real received : _{:?}_{}_{}_{}", &fragment.id(),&fragment.current_fragment(),time, fragment_len);
if self.recently_reconstructed.contains(&fragment.id()) {
debug!("Received a chunk of already re-assembled message ({:?})! It probably got here because the ack got lost", fragment.id());
return None;
@@ -102,13 +115,13 @@ impl ReceivedMessagesBufferInner {
&mut self,
reply_ciphertext: &mut [u8],
reply_key: SurbEncryptionKey,
) -> Option<NymMessage> {
) -> Result<Option<NymMessage>, MessageRecoveryError> {
// note: this performs decryption IN PLACE without extra allocation
self.message_receiver
.recover_plaintext_from_reply(reply_ciphertext, reply_key);
.recover_plaintext_from_reply(reply_ciphertext, reply_key)?;
let fragment_data = reply_ciphertext;
self.recover_from_fragment(fragment_data)
Ok(self.recover_from_fragment(fragment_data))
}
fn process_received_regular_packet(&mut self, mut raw_fragment: Vec<u8>) -> Option<NymMessage> {
@@ -130,13 +143,13 @@ impl ReceivedMessagesBufferInner {
#[derive(Debug, Clone)]
// Note: you should NEVER create more than a single instance of this using 'new()'.
// You should always use .clone() to create additional instances
struct ReceivedMessagesBuffer {
inner: Arc<Mutex<ReceivedMessagesBufferInner>>,
struct ReceivedMessagesBuffer<R: MessageReceiver> {
inner: Arc<Mutex<ReceivedMessagesBufferInner<R>>>,
reply_key_storage: SentReplyKeys,
reply_controller_sender: ReplyControllerSender,
}
impl ReceivedMessagesBuffer {
impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
fn new(
local_encryption_keypair: Arc<encryption::KeyPair>,
reply_key_storage: SentReplyKeys,
@@ -146,7 +159,7 @@ impl ReceivedMessagesBuffer {
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
messages: Vec::new(),
local_encryption_keypair,
message_receiver: MessageReceiver::new(),
message_receiver: R::new(),
message_sender: None,
recently_reconstructed: HashSet::new(),
})),
@@ -328,7 +341,10 @@ impl ReceivedMessagesBuffer {
})
}
async fn handle_new_received(&mut self, msgs: Vec<Vec<u8>>) {
async fn handle_new_received(
&mut self,
msgs: Vec<Vec<u8>>,
) -> Result<(), MessageRecoveryError> {
trace!(
"Processing {:?} new message that might get added to the buffer!",
msgs.len()
@@ -344,7 +360,7 @@ impl ReceivedMessagesBuffer {
// if yes - this is a reply message
let completed_message =
if let Some((reply_key, reply_message)) = self.get_reply_key(&mut msg) {
inner_guard.process_received_reply(reply_message, reply_key)
inner_guard.process_received_reply(reply_message, reply_key)?
} else {
inner_guard.process_received_regular_packet(msg)
};
@@ -360,6 +376,7 @@ impl ReceivedMessagesBuffer {
if !completed_messages.is_empty() {
self.handle_reconstructed_messages(completed_messages).await
}
Ok(())
}
}
@@ -372,14 +389,14 @@ pub enum ReceivedBufferMessage {
ReceiverDisconnect,
}
struct RequestReceiver {
received_buffer: ReceivedMessagesBuffer,
struct RequestReceiver<R: MessageReceiver> {
received_buffer: ReceivedMessagesBuffer<R>,
query_receiver: ReceivedBufferRequestReceiver,
}
impl RequestReceiver {
impl<R: MessageReceiver> RequestReceiver<R> {
fn new(
received_buffer: ReceivedMessagesBuffer,
received_buffer: ReceivedMessagesBuffer<R>,
query_receiver: ReceivedBufferRequestReceiver,
) -> Self {
RequestReceiver {
@@ -422,14 +439,14 @@ impl RequestReceiver {
}
}
struct FragmentedMessageReceiver {
received_buffer: ReceivedMessagesBuffer,
struct FragmentedMessageReceiver<R: MessageReceiver> {
received_buffer: ReceivedMessagesBuffer<R>,
mixnet_packet_receiver: MixnetMessageReceiver,
}
impl FragmentedMessageReceiver {
impl<R: MessageReceiver> FragmentedMessageReceiver<R> {
fn new(
received_buffer: ReceivedMessagesBuffer,
received_buffer: ReceivedMessagesBuffer<R>,
mixnet_packet_receiver: MixnetMessageReceiver,
) -> Self {
FragmentedMessageReceiver {
@@ -438,13 +455,16 @@ impl FragmentedMessageReceiver {
}
}
async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
async fn run_with_shutdown(
&mut self,
mut shutdown: nym_task::TaskClient,
) -> Result<(), MessageRecoveryError> {
debug!("Started FragmentedMessageReceiver with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
new_messages = self.mixnet_packet_receiver.next() => {
if let Some(new_messages) = new_messages {
self.received_buffer.handle_new_received(new_messages).await;
self.received_buffer.handle_new_received(new_messages).await?;
} else {
log::trace!("FragmentedMessageReceiver: Stopping since channel closed");
break;
@@ -457,15 +477,16 @@ impl FragmentedMessageReceiver {
}
shutdown.recv_timeout().await;
log::debug!("FragmentedMessageReceiver: Exiting");
Ok(())
}
}
pub(crate) struct ReceivedMessagesBufferController {
fragmented_message_receiver: FragmentedMessageReceiver,
request_receiver: RequestReceiver,
pub(crate) struct ReceivedMessagesBufferController<R: MessageReceiver> {
fragmented_message_receiver: FragmentedMessageReceiver<R>,
request_receiver: RequestReceiver<R>,
}
impl ReceivedMessagesBufferController {
impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferController<R> {
pub(crate) fn new(
local_encryption_keypair: Arc<encryption::KeyPair>,
query_receiver: ReceivedBufferRequestReceiver,
@@ -494,9 +515,13 @@ impl ReceivedMessagesBufferController {
let shutdown_handle = shutdown.clone();
spawn_future(async move {
fragmented_message_receiver
match fragmented_message_receiver
.run_with_shutdown(shutdown_handle)
.await;
.await
{
Ok(_) => {}
Err(e) => error!("{e}"),
}
});
spawn_future(async move {
request_receiver.run_with_shutdown(shutdown).await;
@@ -35,8 +35,12 @@ impl ReplyStorageBackend for Backend {
) -> Result<Self, Self::StorageError> {
Ok(Backend {
empty: Empty {
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
min_surb_threshold: debug_config
.reply_surbs
.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config
.reply_surbs
.maximum_reply_surb_storage_threshold,
},
})
}
@@ -35,8 +35,12 @@ impl ReplyStorageBackend for Empty {
_db_path: Option<PathBuf>,
) -> Result<Self, Self::StorageError> {
Ok(Self {
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
min_surb_threshold: debug_config
.reply_surbs
.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config
.reply_surbs
.maximum_reply_surb_storage_threshold,
})
}
+193 -92
View File
@@ -13,6 +13,7 @@ use url::Url;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
pub mod old_config_v1_1_13;
pub mod persistence;
pub const MISSING_VALUE: &str = "MISSING VALUE";
@@ -210,11 +211,12 @@ impl<T> Config<T> {
}
pub fn set_high_default_traffic_volume(&mut self) {
self.debug.average_packet_delay = Duration::from_millis(10);
self.debug.traffic.average_packet_delay = Duration::from_millis(10);
// basically don't really send cover messages
self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2_000_000);
self.debug.cover_traffic.loop_cover_traffic_average_delay =
Duration::from_millis(2_000_000);
// 250 "real" messages / s
self.debug.message_sending_average_delay = Duration::from_millis(4);
self.debug.traffic.message_sending_average_delay = Duration::from_millis(4);
}
pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self {
@@ -225,8 +227,8 @@ impl<T> Config<T> {
}
pub fn set_no_cover_traffic(&mut self) {
self.debug.disable_loop_cover_traffic_stream = true;
self.debug.disable_main_poisson_packet_distribution = true;
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
self.debug.traffic.disable_main_poisson_packet_distribution = true;
}
pub fn set_custom_version(&mut self, version: &str) {
@@ -311,87 +313,93 @@ impl<T> Config<T> {
}
pub fn get_average_packet_delay(&self) -> Duration {
self.debug.average_packet_delay
self.debug.traffic.average_packet_delay
}
pub fn get_average_ack_delay(&self) -> Duration {
self.debug.average_ack_delay
self.debug.acknowledgements.average_ack_delay
}
pub fn get_ack_wait_multiplier(&self) -> f64 {
self.debug.ack_wait_multiplier
self.debug.acknowledgements.ack_wait_multiplier
}
pub fn get_ack_wait_addition(&self) -> Duration {
self.debug.ack_wait_addition
self.debug.acknowledgements.ack_wait_addition
}
pub fn get_loop_cover_traffic_average_delay(&self) -> Duration {
self.debug.loop_cover_traffic_average_delay
self.debug.cover_traffic.loop_cover_traffic_average_delay
}
pub fn get_message_sending_average_delay(&self) -> Duration {
self.debug.message_sending_average_delay
self.debug.traffic.message_sending_average_delay
}
pub fn get_gateway_response_timeout(&self) -> Duration {
self.debug.gateway_response_timeout
self.debug.gateway_connection.gateway_response_timeout
}
pub fn get_topology_refresh_rate(&self) -> Duration {
self.debug.topology_refresh_rate
self.debug.topology.topology_refresh_rate
}
pub fn get_topology_resolution_timeout(&self) -> Duration {
self.debug.topology_resolution_timeout
self.debug.topology.topology_resolution_timeout
}
pub fn get_disabled_loop_cover_traffic_stream(&self) -> bool {
self.debug.disable_loop_cover_traffic_stream
self.debug.cover_traffic.disable_loop_cover_traffic_stream
}
pub fn get_disabled_main_poisson_packet_distribution(&self) -> bool {
self.debug.disable_main_poisson_packet_distribution
self.debug.traffic.disable_main_poisson_packet_distribution
}
pub fn get_use_extended_packet_size(&self) -> Option<ExtendedPacketSize> {
self.debug.use_extended_packet_size
self.debug.traffic.use_extended_packet_size
}
pub fn get_minimum_reply_surb_storage_threshold(&self) -> usize {
self.debug.minimum_reply_surb_storage_threshold
self.debug.reply_surbs.minimum_reply_surb_storage_threshold
}
pub fn get_maximum_reply_surb_storage_threshold(&self) -> usize {
self.debug.maximum_reply_surb_storage_threshold
self.debug.reply_surbs.maximum_reply_surb_storage_threshold
}
pub fn get_minimum_reply_surb_request_size(&self) -> u32 {
self.debug.minimum_reply_surb_request_size
self.debug.reply_surbs.minimum_reply_surb_request_size
}
pub fn get_maximum_reply_surb_request_size(&self) -> u32 {
self.debug.maximum_reply_surb_request_size
self.debug.reply_surbs.maximum_reply_surb_request_size
}
pub fn get_maximum_allowed_reply_surb_request_size(&self) -> u32 {
self.debug.maximum_allowed_reply_surb_request_size
self.debug
.reply_surbs
.maximum_allowed_reply_surb_request_size
}
pub fn get_maximum_reply_surb_rerequest_waiting_period(&self) -> Duration {
self.debug.maximum_reply_surb_rerequest_waiting_period
self.debug
.reply_surbs
.maximum_reply_surb_rerequest_waiting_period
}
pub fn get_maximum_reply_surb_drop_waiting_period(&self) -> Duration {
self.debug.maximum_reply_surb_drop_waiting_period
self.debug
.reply_surbs
.maximum_reply_surb_drop_waiting_period
}
pub fn get_maximum_reply_surb_age(&self) -> Duration {
self.debug.maximum_reply_surb_age
self.debug.reply_surbs.maximum_reply_surb_age
}
pub fn get_maximum_reply_key_age(&self) -> Duration {
self.debug.maximum_reply_key_age
self.debug.reply_surbs.maximum_reply_key_age
}
}
@@ -450,63 +458,63 @@ impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
pub struct Client<T> {
/// Version of the client for which this configuration was created.
#[serde(default = "missing_string_value")]
version: String,
pub version: String,
/// ID specifies the human readable ID of this particular client.
id: String,
pub id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
#[serde(default)]
disabled_credentials_mode: bool,
pub disabled_credentials_mode: bool,
/// Addresses to nyxd validators via which the client can communicate with the chain.
#[serde(alias = "validator_urls")]
nyxd_urls: Vec<Url>,
pub nyxd_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
nym_api_urls: Vec<Url>,
pub nym_api_urls: Vec<Url>,
/// Path to file containing private identity key.
private_identity_key_file: PathBuf,
pub private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
public_identity_key_file: PathBuf,
pub public_identity_key_file: PathBuf,
/// Path to file containing private encryption key.
private_encryption_key_file: PathBuf,
pub private_encryption_key_file: PathBuf,
/// Path to file containing public encryption key.
public_encryption_key_file: PathBuf,
pub public_encryption_key_file: PathBuf,
/// Path to file containing shared key derived with the specified gateway that is used
/// for all communication with it.
gateway_shared_key_file: PathBuf,
pub gateway_shared_key_file: PathBuf,
/// Path to file containing key used for encrypting and decrypting the content of an
/// acknowledgement so that nobody besides the client knows which packet it refers to.
ack_key_file: PathBuf,
pub ack_key_file: PathBuf,
/// Information regarding how the client should send data to gateway.
gateway_endpoint: GatewayEndpointConfig,
pub gateway_endpoint: GatewayEndpointConfig,
/// Path to the database containing bandwidth credentials of this client.
database_path: PathBuf,
pub database_path: PathBuf,
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
// this was set to use #[serde(default)] for the purposes of compatibility for multi-surbs introduced in 1.1.4.
// if you're reading this message and we have already introduced some breaking changes, feel free
// to remove that attribute since at this point the client configs should have gotten regenerated
#[serde(default)]
reply_surb_database_path: PathBuf,
pub reply_surb_database_path: PathBuf,
/// nym_home_directory specifies absolute path to the home nym Clients directory.
/// It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory: PathBuf,
pub nym_root_directory: PathBuf,
#[serde(skip)]
super_struct: PhantomData<T>,
pub super_struct: PhantomData<T>,
}
impl<T: NymConfig> Default for Client<T> {
@@ -587,9 +595,9 @@ impl<T: NymConfig> Client<T> {
#[serde(deny_unknown_fields)]
pub struct Logging {}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfig {
pub struct Traffic {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
@@ -597,6 +605,74 @@ pub struct DebugConfig {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Controls whether the sent sphinx packet use a NON-DEFAULT bigger size.
pub use_extended_packet_size: Option<ExtendedPacketSize>,
}
impl Default for Traffic {
fn default() -> Self {
Traffic {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
use_extended_packet_size: None,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTraffic {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTraffic {
fn default() -> Self {
CoverTraffic {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
disable_loop_cover_traffic_stream: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnection {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnection {
fn default() -> Self {
GatewayConnection {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Acknowledgements {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
@@ -614,24 +690,21 @@ pub struct DebugConfig {
/// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
impl Default for Acknowledgements {
fn default() -> Self {
Acknowledgements {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Topology {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
@@ -642,18 +715,20 @@ pub struct DebugConfig {
/// did not reach its destination.
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
}
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Controls whether the sent sphinx packet use a NON-DEFAULT bigger size.
pub use_extended_packet_size: Option<ExtendedPacketSize>,
impl Default for Topology {
fn default() -> Self {
Topology {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbs {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
@@ -691,29 +766,9 @@ pub struct DebugConfig {
pub maximum_reply_key_age: Duration,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ExtendedPacketSize {
Extended8,
Extended16,
Extended32,
}
impl Default for DebugConfig {
impl Default for ReplySurbs {
fn default() -> Self {
DebugConfig {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_loop_cover_traffic_stream: false,
disable_main_poisson_packet_distribution: false,
use_extended_packet_size: None,
ReplySurbs {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
@@ -728,6 +783,52 @@ impl Default for DebugConfig {
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfig {
/// Defines all configuration options related to traffic streams.
pub traffic: Traffic,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTraffic,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnection,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: Acknowledgements,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: Topology,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbs,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ExtendedPacketSize {
Extended8,
Extended16,
Extended32,
}
// it could be derived, sure, but I'd rather have an explicit implementation in case we had to change
// something manually at some point
#[allow(clippy::derivable_impls)]
impl Default for DebugConfig {
fn default() -> Self {
DebugConfig {
traffic: Default::default(),
cover_traffic: Default::default(),
gateway_connection: Default::default(),
acknowledgements: Default::default(),
topology: Default::default(),
reply_surbs: Default::default(),
}
}
}
impl From<ExtendedPacketSize> for PacketSize {
fn from(size: ExtendedPacketSize) -> PacketSize {
match size {
@@ -0,0 +1,198 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, ExtendedPacketSize,
GatewayConnection, Logging, ReplySurbs, Topology, Traffic, DEFAULT_ACK_WAIT_ADDITION,
DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_REPLY_SURB_AGE,
DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
};
use nym_config::NymConfig;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::time::Duration;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13<T> {
pub client: Client<T>,
#[serde(default)]
logging: Logging,
#[serde(default)]
debug: OldDebugConfigV1_1_13,
}
impl<T: NymConfig> Default for OldConfigV1_1_13<T> {
fn default() -> Self {
OldConfigV1_1_13 {
client: Client::<T>::default(),
logging: Default::default(),
debug: Default::default(),
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct OldDebugConfigV1_1_13 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64,
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
pub disable_loop_cover_traffic_stream: bool,
pub disable_main_poisson_packet_distribution: bool,
pub use_extended_packet_size: Option<ExtendedPacketSize>,
pub minimum_reply_surb_storage_threshold: usize,
pub maximum_reply_surb_storage_threshold: usize,
pub minimum_reply_surb_request_size: u32,
pub maximum_reply_surb_request_size: u32,
pub maximum_allowed_reply_surb_request_size: u32,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
}
impl From<OldDebugConfigV1_1_13> for DebugConfig {
fn from(value: OldDebugConfigV1_1_13) -> Self {
DebugConfig {
traffic: Traffic {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
.disable_main_poisson_packet_distribution,
use_extended_packet_size: value.use_extended_packet_size,
},
cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
},
gateway_connection: GatewayConnection {
gateway_response_timeout: value.gateway_response_timeout,
},
acknowledgements: Acknowledgements {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
},
topology: Topology {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
},
reply_surbs: ReplySurbs {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: value.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: value
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: value
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: value
.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: value.maximum_reply_surb_age,
maximum_reply_key_age: value.maximum_reply_key_age,
},
}
}
}
impl Default for OldDebugConfigV1_1_13 {
fn default() -> Self {
OldDebugConfigV1_1_13 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_loop_cover_traffic_stream: false,
disable_main_poisson_packet_distribution: false,
use_extended_packet_size: None,
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
maximum_reply_surb_rerequest_waiting_period:
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
}
}
}
impl<T, U> From<OldConfigV1_1_13<T>> for Config<U> {
fn from(value: OldConfigV1_1_13<T>) -> Self {
Config {
client: Client {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
nym_api_urls: value.client.nym_api_urls,
private_identity_key_file: value.client.private_identity_key_file,
public_identity_key_file: value.client.public_identity_key_file,
private_encryption_key_file: value.client.private_encryption_key_file,
public_encryption_key_file: value.client.public_encryption_key_file,
gateway_shared_key_file: value.client.gateway_shared_key_file,
ack_key_file: value.client.ack_key_file,
gateway_endpoint: value.client.gateway_endpoint,
database_path: value.client.database_path,
reply_surb_database_path: value.client.reply_surb_database_path,
nym_root_directory: value.client.nym_root_directory,
super_struct: PhantomData,
},
logging: value.logging,
debug: value.debug.into(),
}
}
}
+2 -2
View File
@@ -112,14 +112,14 @@ where
{
let id = config.get_id();
// If we are not going to register gateway, and an explicitly chosed gateway is not passed in,
// If we are not going to register gateway, and an explicitly chosen gateway is not passed in,
// load the existing configuration file
if !register_gateway && user_chosen_gateway_id.is_none() {
println!("Not registering gateway, will reuse existing config and keys");
return load_existing_gateway_config::<C>(&id);
}
// Else, we preceed by querying the nym-api
// Else, we proceed by querying the nym-api
let gateway = helpers::query_gateway_details(
config.get_nym_api_endpoints(),
user_chosen_gateway_id,
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.12"
version = "1.1.14"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+1
View File
@@ -15,6 +15,7 @@ pub use client_core::config::Config as BaseConfig;
pub use client_core::config::MISSING_VALUE;
pub use client_core::config::{DebugConfig, GatewayEndpointConfig};
pub mod old_config_v1_1_13;
mod template;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
@@ -0,0 +1,60 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, Socket};
use client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::NymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13 {
#[serde(flatten)]
base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
socket: Socket,
}
impl NymConfig for OldConfigV1_1_13 {
fn template() -> &'static str {
// not intended to be used
unimplemented!()
}
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("clients")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.client.nym_root_directory.clone()
}
fn config_directory(&self) -> PathBuf {
self.root_directory()
.join(&self.base.client.id)
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.root_directory()
.join(&self.base.client.id)
.join("data")
}
}
impl From<OldConfigV1_1_13> for Config {
fn from(value: OldConfigV1_1_13) -> Self {
Config {
base: value.base.into(),
socket: value.socket,
}
}
}
+9 -4
View File
@@ -110,10 +110,15 @@ host = '{{ socket.host }}'
[debug]
average_packet_delay = '{{ debug.average_packet_delay }}'
average_ack_delay = '{{ debug.average_ack_delay }}'
loop_cover_traffic_average_delay = '{{ debug.loop_cover_traffic_average_delay }}'
message_sending_average_delay = '{{ debug.message_sending_average_delay }}'
[debug.traffic]
average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
[debug.acknowledgements]
average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
[debug.cover_traffic]
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
"#
}
+4
View File
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_v1_1_13_config;
use crate::{
client::config::Config,
commands::{override_config, OverrideConfig},
@@ -121,6 +122,9 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
let already_init = Config::default_config_file_path(id).exists();
if already_init {
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
println!("Client \"{id}\" was already initialised before");
}
+17 -1
View File
@@ -1,13 +1,15 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::client::config::{BaseConfig, Config};
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use log::info;
use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_config::OptionalSet;
use nym_config::{NymConfig, OptionalSet};
use std::error::Error;
use std::net::IpAddr;
@@ -102,6 +104,20 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
)
}
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
// explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(());
};
info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated.save_to_file(None)
}
#[cfg(test)]
mod tests {
use super::*;
+5 -1
View File
@@ -4,12 +4,12 @@
use std::error::Error;
use std::net::IpAddr;
use crate::commands::try_upgrade_v1_1_13_config;
use crate::{
client::{config::Config, SocketClient},
commands::{override_config, OverrideConfig},
error::ClientError,
};
use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
@@ -100,6 +100,10 @@ fn version_check(cfg: &Config) -> bool {
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
let id = &args.id;
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
let mut config = match Config::load_from_file(id) {
Ok(cfg) => cfg,
Err(err) => {
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.12"
version = "1.1.14"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
+1
View File
@@ -15,6 +15,7 @@ use std::fmt::Debug;
use std::path::PathBuf;
use std::str::FromStr;
pub mod old_config_v1_1_13;
mod template;
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
@@ -0,0 +1,66 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, Socks5, Socks5Debug};
use client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::NymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13 {
#[serde(flatten)]
base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
socks5: Socks5,
#[serde(default)]
socks5_debug: Socks5Debug,
}
impl NymConfig for OldConfigV1_1_13 {
fn template() -> &'static str {
// not intended to be used
unimplemented!()
}
fn default_root_directory() -> PathBuf {
#[cfg(not(feature = "mobile"))]
let base_dir = dirs::home_dir().expect("Failed to evaluate $HOME value");
#[cfg(feature = "mobile")]
let base_dir = PathBuf::from("/tmp");
base_dir.join(".nym").join("socks5-clients")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.client.nym_root_directory.clone()
}
fn config_directory(&self) -> PathBuf {
self.root_directory()
.join(&self.base.client.id)
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.root_directory()
.join(&self.base.client.id)
.join("data")
}
}
impl From<OldConfigV1_1_13> for Config {
fn from(value: OldConfigV1_1_13) -> Self {
Config {
base: value.base.into(),
socks5: value.socks5,
socks5_debug: value.socks5_debug,
}
}
}
+9 -4
View File
@@ -114,10 +114,15 @@ send_anonymously = {{ socks5.send_anonymously }}
[debug]
average_packet_delay = '{{ debug.average_packet_delay }}'
average_ack_delay = '{{ debug.average_ack_delay }}'
loop_cover_traffic_average_delay = '{{ debug.loop_cover_traffic_average_delay }}'
message_sending_average_delay = '{{ debug.message_sending_average_delay }}'
[debug.traffic]
average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
[debug.acknowledgements]
average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
[debug.cover_traffic]
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
"#
}
+4
View File
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_v1_1_13_config;
use crate::{
client::config::Config,
commands::{override_config, OverrideConfig},
@@ -124,6 +125,9 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
let already_init = Config::default_config_file_path(id).exists();
if already_init {
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
println!("SOCKS5 client \"{id}\" was already initialised before");
}
+17 -1
View File
@@ -1,13 +1,15 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::client::config::{BaseConfig, Config};
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use log::info;
use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_config::OptionalSet;
use nym_config::{NymConfig, OptionalSet};
use std::error::Error;
pub mod init;
@@ -101,6 +103,20 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
)
}
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
// explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(());
};
info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated.save_to_file(None)
}
#[cfg(test)]
mod tests {
use super::*;
+5 -1
View File
@@ -1,12 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_v1_1_13_config;
use crate::{
client::{config::Config, NymClient},
commands::{override_config, OverrideConfig},
error::Socks5ClientError,
};
use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
@@ -108,6 +108,10 @@ fn version_check(cfg: &Config) -> bool {
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let id = &args.id;
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
let mut config = match Config::load_from_file(id) {
Ok(cfg) => cfg,
Err(err) => {
+239 -88
View File
@@ -3,8 +3,15 @@
// due to expansion of #[wasm_bindgen] macro on `Debug` Config struct
#![allow(clippy::drop_non_drop)]
// another issue due to #[wasm_bindgen] and `Copy` trait
#![allow(clippy::drop_copy)]
use client_core::config::{DebugConfig as ConfigDebug, ExtendedPacketSize, GatewayEndpointConfig};
use client_core::config::{
Acknowledgements as ConfigAcknowledgements, CoverTraffic as ConfigCoverTraffic,
DebugConfig as ConfigDebug, ExtendedPacketSize, GatewayConnection as ConfigGatewayConnection,
GatewayEndpointConfig, ReplySurbs as ConfigReplySurbs, Topology as ConfigTopology,
Traffic as ConfigTraffic,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -48,15 +55,124 @@ impl Config {
}
}
// just a helper structure to more easily pass through the JS boundary
#[wasm_bindgen]
pub struct Debug {
#[derive(Debug, Copy, Clone)]
pub struct Traffic {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
pub average_packet_delay_ms: u64,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
pub message_sending_average_delay_ms: u64,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
pub use_extended_packet_size: bool,
}
impl From<Traffic> for ConfigTraffic {
fn from(traffic: Traffic) -> Self {
let use_extended_packet_size = traffic
.use_extended_packet_size
.then(|| ExtendedPacketSize::Extended32);
ConfigTraffic {
average_packet_delay: Duration::from_millis(traffic.average_packet_delay_ms),
message_sending_average_delay: Duration::from_millis(
traffic.message_sending_average_delay_ms,
),
disable_main_poisson_packet_distribution: traffic
.disable_main_poisson_packet_distribution,
use_extended_packet_size,
}
}
}
impl From<ConfigTraffic> for Traffic {
fn from(traffic: ConfigTraffic) -> Self {
Traffic {
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u64,
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
as u64,
disable_main_poisson_packet_distribution: traffic
.disable_main_poisson_packet_distribution,
use_extended_packet_size: traffic.use_extended_packet_size.is_some(),
}
}
}
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct CoverTraffic {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
pub loop_cover_traffic_average_delay_ms: u64,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl From<CoverTraffic> for ConfigCoverTraffic {
fn from(cover_traffic: CoverTraffic) -> Self {
ConfigCoverTraffic {
loop_cover_traffic_average_delay: Duration::from_millis(
cover_traffic.loop_cover_traffic_average_delay_ms,
),
disable_loop_cover_traffic_stream: cover_traffic.disable_loop_cover_traffic_stream,
}
}
}
impl From<ConfigCoverTraffic> for CoverTraffic {
fn from(cover_traffic: ConfigCoverTraffic) -> Self {
CoverTraffic {
loop_cover_traffic_average_delay_ms: cover_traffic
.loop_cover_traffic_average_delay
.as_millis() as u64,
disable_loop_cover_traffic_stream: cover_traffic.disable_loop_cover_traffic_stream,
}
}
}
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct GatewayConnection {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
pub gateway_response_timeout_ms: u64,
}
impl From<GatewayConnection> for ConfigGatewayConnection {
fn from(gateway_connection: GatewayConnection) -> Self {
ConfigGatewayConnection {
gateway_response_timeout: Duration::from_millis(
gateway_connection.gateway_response_timeout_ms,
),
}
}
}
impl From<ConfigGatewayConnection> for GatewayConnection {
fn from(gateway_connection: ConfigGatewayConnection) -> Self {
GatewayConnection {
gateway_response_timeout_ms: gateway_connection.gateway_response_timeout.as_millis()
as u64,
}
}
}
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Acknowledgements {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
@@ -72,21 +188,31 @@ pub struct Debug {
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
pub ack_wait_addition_ms: u64,
}
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
pub loop_cover_traffic_average_delay_ms: u64,
impl From<Acknowledgements> for ConfigAcknowledgements {
fn from(acknowledgements: Acknowledgements) -> Self {
ConfigAcknowledgements {
average_ack_delay: Duration::from_millis(acknowledgements.average_ack_delay_ms),
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
ack_wait_addition: Duration::from_millis(acknowledgements.ack_wait_addition_ms),
}
}
}
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
pub message_sending_average_delay_ms: u64,
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
pub gateway_response_timeout_ms: u64,
impl From<ConfigAcknowledgements> for Acknowledgements {
fn from(acknowledgements: ConfigAcknowledgements) -> Self {
Acknowledgements {
average_ack_delay_ms: acknowledgements.average_ack_delay.as_millis() as u64,
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
ack_wait_addition_ms: acknowledgements.ack_wait_addition.as_millis() as u64,
}
}
}
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Topology {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
pub topology_refresh_rate_ms: u64,
@@ -95,18 +221,31 @@ pub struct Debug {
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
pub topology_resolution_timeout_ms: u64,
}
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay_ms])
pub disable_loop_cover_traffic_stream: bool,
impl From<Topology> for ConfigTopology {
fn from(topology: Topology) -> Self {
ConfigTopology {
topology_refresh_rate: Duration::from_millis(topology.topology_refresh_rate_ms),
topology_resolution_timeout: Duration::from_millis(
topology.topology_resolution_timeout_ms,
),
}
}
}
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
pub use_extended_packet_size: bool,
impl From<ConfigTopology> for Topology {
fn from(topology: ConfigTopology) -> Self {
Topology {
topology_refresh_rate_ms: topology.topology_refresh_rate.as_millis() as u64,
topology_resolution_timeout_ms: topology.topology_resolution_timeout.as_millis() as u64,
}
}
}
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct ReplySurbs {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
@@ -140,46 +279,80 @@ pub struct Debug {
pub maximum_reply_key_age_ms: u64,
}
impl From<Debug> for ConfigDebug {
fn from(debug: Debug) -> Self {
// For now we just always use the (older) 32kb extended size
let use_extended_packet_size = debug
.use_extended_packet_size
.then(|| ExtendedPacketSize::Extended32);
ConfigDebug {
average_packet_delay: Duration::from_millis(debug.average_packet_delay_ms),
average_ack_delay: Duration::from_millis(debug.average_ack_delay_ms),
ack_wait_multiplier: debug.ack_wait_multiplier,
ack_wait_addition: Duration::from_millis(debug.ack_wait_addition_ms),
loop_cover_traffic_average_delay: Duration::from_millis(
debug.loop_cover_traffic_average_delay_ms,
),
message_sending_average_delay: Duration::from_millis(
debug.message_sending_average_delay_ms,
),
gateway_response_timeout: Duration::from_millis(debug.gateway_response_timeout_ms),
topology_refresh_rate: Duration::from_millis(debug.topology_refresh_rate_ms),
topology_resolution_timeout: Duration::from_millis(
debug.topology_resolution_timeout_ms,
),
disable_loop_cover_traffic_stream: debug.disable_loop_cover_traffic_stream,
disable_main_poisson_packet_distribution: debug
.disable_main_poisson_packet_distribution,
use_extended_packet_size,
minimum_reply_surb_storage_threshold: debug.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: debug.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: debug.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: debug.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: debug.maximum_allowed_reply_surb_request_size,
impl From<ReplySurbs> for ConfigReplySurbs {
fn from(reply_surbs: ReplySurbs) -> Self {
ConfigReplySurbs {
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: reply_surbs.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: reply_surbs.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: reply_surbs
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: Duration::from_millis(
debug.maximum_reply_surb_rerequest_waiting_period_ms,
reply_surbs.maximum_reply_surb_rerequest_waiting_period_ms,
),
maximum_reply_surb_drop_waiting_period: Duration::from_millis(
debug.maximum_reply_surb_drop_waiting_period_ms,
reply_surbs.maximum_reply_surb_drop_waiting_period_ms,
),
maximum_reply_surb_age: Duration::from_millis(debug.maximum_reply_surb_age_ms),
maximum_reply_key_age: Duration::from_millis(debug.maximum_reply_key_age_ms),
maximum_reply_surb_age: Duration::from_millis(reply_surbs.maximum_reply_surb_age_ms),
maximum_reply_key_age: Duration::from_millis(reply_surbs.maximum_reply_key_age_ms),
}
}
}
impl From<ConfigReplySurbs> for ReplySurbs {
fn from(reply_surbs: ConfigReplySurbs) -> Self {
ReplySurbs {
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: reply_surbs.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: reply_surbs.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: reply_surbs
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period_ms: reply_surbs
.maximum_reply_surb_rerequest_waiting_period
.as_millis() as u64,
maximum_reply_surb_drop_waiting_period_ms: reply_surbs
.maximum_reply_surb_drop_waiting_period
.as_millis() as u64,
maximum_reply_surb_age_ms: reply_surbs.maximum_reply_surb_age.as_millis() as u64,
maximum_reply_key_age_ms: reply_surbs.maximum_reply_key_age.as_millis() as u64,
}
}
}
// just a helper structure to more easily pass through the JS boundary
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Debug {
/// Defines all configuration options related to traffic streams.
pub traffic: Traffic,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTraffic,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnection,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: Acknowledgements,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: Topology,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbs,
}
impl From<Debug> for ConfigDebug {
fn from(debug: Debug) -> Self {
ConfigDebug {
traffic: debug.traffic.into(),
cover_traffic: debug.cover_traffic.into(),
gateway_connection: debug.gateway_connection.into(),
acknowledgements: debug.acknowledgements.into(),
topology: debug.topology.into(),
reply_surbs: debug.reply_surbs.into(),
}
}
}
@@ -187,34 +360,12 @@ impl From<Debug> for ConfigDebug {
impl From<ConfigDebug> for Debug {
fn from(debug: ConfigDebug) -> Self {
Debug {
average_packet_delay_ms: debug.average_packet_delay.as_millis() as u64,
average_ack_delay_ms: debug.average_ack_delay.as_millis() as u64,
ack_wait_multiplier: debug.ack_wait_multiplier,
ack_wait_addition_ms: debug.ack_wait_addition.as_millis() as u64,
loop_cover_traffic_average_delay_ms: debug.loop_cover_traffic_average_delay.as_millis()
as u64,
message_sending_average_delay_ms: debug.message_sending_average_delay.as_millis()
as u64,
gateway_response_timeout_ms: debug.gateway_response_timeout.as_millis() as u64,
topology_refresh_rate_ms: debug.topology_refresh_rate.as_millis() as u64,
topology_resolution_timeout_ms: debug.topology_resolution_timeout.as_millis() as u64,
disable_loop_cover_traffic_stream: debug.disable_loop_cover_traffic_stream,
disable_main_poisson_packet_distribution: debug
.disable_main_poisson_packet_distribution,
use_extended_packet_size: debug.use_extended_packet_size.is_some(),
minimum_reply_surb_storage_threshold: debug.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: debug.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: debug.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: debug.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: debug.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period_ms: debug
.maximum_reply_surb_rerequest_waiting_period
.as_millis() as u64,
maximum_reply_surb_drop_waiting_period_ms: debug
.maximum_reply_surb_drop_waiting_period
.as_millis() as u64,
maximum_reply_surb_age_ms: debug.maximum_reply_surb_age.as_millis() as u64,
maximum_reply_key_age_ms: debug.maximum_reply_key_age.as_millis() as u64,
traffic: debug.traffic.into(),
cover_traffic: debug.cover_traffic.into(),
gateway_connection: debug.gateway_connection.into(),
acknowledgements: debug.acknowledgements.into(),
topology: debug.topology.into(),
reply_surbs: debug.reply_surbs.into(),
}
}
}
+8 -2
View File
@@ -82,8 +82,14 @@ impl NymClientBuilder {
// with no persistence
fn setup_reply_surb_storage_backend(config: &Config) -> browser_backend::Backend {
browser_backend::Backend::new(
config.debug.minimum_reply_surb_storage_threshold,
config.debug.maximum_reply_surb_storage_threshold,
config
.debug
.reply_surbs
.minimum_reply_surb_storage_threshold,
config
.debug
.reply_surbs
.maximum_reply_surb_storage_threshold,
)
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-bin-common"
version = "0.2.0"
version = "0.4.0"
description = "Common code for nym binaries"
edition = { workspace = true }
authors = { workspace = true }
@@ -40,14 +40,14 @@ async-trait = { workspace = true, optional = true }
bip39 = { workspace = true, features = ["rand"], optional = true }
nym-config = { path = "../../config", optional = true }
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true}
cw3 = { version = "0.13.4", optional = true }
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 }
zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
cosmwasm-std = { workspace = true, optional = true }
[dev-dependencies]
ts-rs = "6.1.2"
@@ -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";
@@ -1,4 +1,4 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::coin::Coin;
@@ -9,6 +9,7 @@ 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::families::FamilyHead;
use nym_mixnet_contract_common::gateway::GatewayConfigUpdate;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
@@ -146,25 +147,16 @@ pub trait MixnetSigningClient {
// family related
async fn create_family(
&self,
owner_signature: String,
label: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::CreateFamily {
owner_signature,
label,
},
vec![],
)
.await
self.execute_mixnet_contract(fee, MixnetExecuteMsg::CreateFamily { label }, vec![])
.await
}
async fn create_family_on_behalf(
&self,
owner_address: String,
owner_signature: String,
label: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -172,7 +164,6 @@ pub trait MixnetSigningClient {
fee,
MixnetExecuteMsg::CreateFamilyOnBehalf {
owner_address,
owner_signature,
label,
},
vec![],
@@ -182,14 +173,14 @@ pub trait MixnetSigningClient {
async fn join_family(
&self,
signature: String,
family_head: String,
join_permit: MessageSignature,
family_head: FamilyHead,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::JoinFamily {
signature,
join_permit,
family_head,
},
vec![],
@@ -200,17 +191,15 @@ pub trait MixnetSigningClient {
async fn join_family_on_behalf(
&self,
member_address: String,
node_identity_signature: String,
family_signature: String,
family_head: String,
join_permit: MessageSignature,
family_head: FamilyHead,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::JoinFamilyOnBehalf {
member_address,
node_identity_signature,
family_signature,
join_permit,
family_head,
},
vec![],
@@ -220,33 +209,23 @@ pub trait MixnetSigningClient {
async fn leave_family(
&self,
signature: String,
family_head: String,
family_head: FamilyHead,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::LeaveFamily {
signature,
family_head,
},
vec![],
)
.await
self.execute_mixnet_contract(fee, MixnetExecuteMsg::LeaveFamily { family_head }, vec![])
.await
}
async fn leave_family_on_behalf(
&self,
member_address: String,
node_identity_signature: String,
family_head: String,
family_head: FamilyHead,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::LeaveFamilyOnBehalf {
member_address,
node_identity_signature,
family_head,
},
vec![],
@@ -256,22 +235,16 @@ pub trait MixnetSigningClient {
async fn kick_family_member(
&self,
signature: String,
member: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::KickFamilyMember { signature, member },
vec![],
)
.await
self.execute_mixnet_contract(fee, MixnetExecuteMsg::KickFamilyMember { member }, vec![])
.await
}
async fn kick_family_member_on_behalf(
&self,
head_address: String,
signature: String,
member: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -279,7 +252,6 @@ pub trait MixnetSigningClient {
fee,
MixnetExecuteMsg::KickFamilyMemberOnBehalf {
head_address,
signature,
member,
},
vec![],
@@ -7,6 +7,7 @@ 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::families::FamilyHead;
use nym_mixnet_contract_common::gateway::GatewayConfigUpdate;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
@@ -136,6 +137,50 @@ pub trait VestingSigningClient {
cap: Option<PledgeCap>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError>;
async fn vesting_create_family(
&self,
label: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(fee, VestingExecuteMsg::CreateFamily { label }, vec![])
.await
}
async fn vesting_join_family(
&self,
join_permit: MessageSignature,
family_head: FamilyHead,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::JoinFamily {
join_permit,
family_head,
},
vec![],
)
.await
}
async fn vesting_leave_family(
&self,
family_head: FamilyHead,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(fee, VestingExecuteMsg::LeaveFamily { family_head }, vec![])
.await
}
async fn vesting_kick_family_member(
&self,
member: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(fee, VestingExecuteMsg::KickFamilyMember { member }, vec![])
.await
}
}
#[async_trait]
+3 -2
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,9 +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-crypto = { path = "../../common/crypto", features = ["asymmetric"] }
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" }
@@ -0,0 +1,37 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use validator_client::nyxd::traits::MixnetSigningClient;
use validator_client::nyxd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
/// Label that is going to be used for creating the family
#[arg(long)]
pub family_label: String,
/// Indicates whether the family is going to get created via a vesting account
#[arg(long)]
pub with_vesting_account: bool,
}
pub async fn create_family(args: Args, client: SigningClient) {
info!("Create family");
let res = if args.with_vesting_account {
client
.vesting_create_family(args.family_label, None)
.await
.expect("failed to create family with vesting account")
} else {
client
.create_family(args.family_label, None)
.await
.expect("failed to create family")
};
info!("Family creation result: {:?}", res);
}
@@ -0,0 +1,72 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClient;
use crate::utils::account_id_to_cw_addr;
use clap::Parser;
use cosmrs::AccountId;
use log::info;
use nym_crypto::asymmetric::identity;
use nym_mixnet_contract_common::construct_family_join_permit;
use nym_mixnet_contract_common::families::FamilyHead;
use validator_client::nyxd::traits::MixnetQueryClient;
#[derive(Debug, Parser)]
pub struct Args {
/// Account address (i.e. owner of the family head) which will be used for issuing the permit
#[arg(long)]
pub address: AccountId,
/// Indicates whether the member joining the family is going to use the vesting account for joining.
#[arg(long)]
pub with_vesting_account: bool,
// might as well validate the value when parsing the arguments
/// Identity of the member for whom we're issuing the permit
#[arg(long)]
pub member: identity::PublicKey,
}
pub async fn create_family_join_permit_sign_payload(args: Args, client: QueryClient) {
info!("Create family join permit sign payload");
// get the address of our mixnode to recover the family head information
let Some(mixnode) = client.get_owned_mixnode(&args.address).await.unwrap().mixnode_details else {
eprintln!("{} does not seem to even own a mixnode!", args.address);
return
};
// make sure this mixnode is actually a family head
if client
.get_node_family_by_head(mixnode.bond_information.identity())
.await
.unwrap()
.is_none()
{
eprintln!("{} does not even seem to own a family!", args.address);
return;
}
let nonce = match client.get_signing_nonce(&args.address).await {
Ok(nonce) => nonce,
Err(err) => {
eprint!(
"failed to query for the signing nonce of {}: {err}",
args.address
);
return;
}
};
// let address = account_id_to_cw_addr(&args.address);
let proxy = if args.with_vesting_account {
Some(account_id_to_cw_addr(client.vesting_contract_address()))
} else {
None
};
let head = FamilyHead::new(mixnode.bond_information.identity());
let payload = construct_family_join_permit(nonce, head, proxy, args.member.to_base58_string());
println!("{}", payload.to_base58_string().unwrap())
}
@@ -0,0 +1,46 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use nym_contracts_common::signing::MessageSignature;
use nym_crypto::asymmetric::identity;
use nym_mixnet_contract_common::families::FamilyHead;
use validator_client::nyxd::traits::MixnetSigningClient;
use validator_client::nyxd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
/// The head of the family that we intend to join
#[arg(long)]
pub family_head: identity::PublicKey,
/// Indicates whether the member joining the family is going to do so via the vesting contract
#[arg(long)]
pub with_vesting_account: bool,
/// Permission, as provided by the family head, for joining the family
#[arg(long)]
pub join_permit: MessageSignature,
}
pub async fn join_family(args: Args, client: SigningClient) {
info!("Join family");
let family_head = FamilyHead::new(&args.family_head.to_base58_string());
let res = if args.with_vesting_account {
client
.vesting_join_family(args.join_permit, family_head, None)
.await
.expect("failed to join family with vesting account")
} else {
client
.join_family(args.join_permit, family_head, None)
.await
.expect("failed to join family")
};
info!("Family join result: {:?}", res);
}
@@ -0,0 +1,40 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use nym_crypto::asymmetric::identity;
use validator_client::nyxd::traits::MixnetSigningClient;
use validator_client::nyxd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
/// The member of the family that we intend to kick
#[arg(long)]
pub member: identity::PublicKey,
/// Indicates whether the family was created (and managed) via the vesting contract
#[arg(long)]
pub with_vesting_account: bool,
}
pub async fn kick_family_member(args: Args, client: SigningClient) {
info!("Leave family");
let member = args.member.to_base58_string();
let res = if args.with_vesting_account {
client
.vesting_kick_family_member(member, None)
.await
.expect("failed to kick family member with vesting account")
} else {
client
.kick_family_member(member, None)
.await
.expect("failed to kick family member")
};
info!("Family leave result: {:?}", res);
}
@@ -0,0 +1,41 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use nym_crypto::asymmetric::identity;
use nym_mixnet_contract_common::families::FamilyHead;
use validator_client::nyxd::traits::MixnetSigningClient;
use validator_client::nyxd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
/// The head of the family that we intend to leave
#[arg(long)]
pub family_head: identity::PublicKey,
/// Indicates whether we joined the family via the vesting contract
#[arg(long)]
pub with_vesting_account: bool,
}
pub async fn leave_family(args: Args, client: SigningClient) {
info!("Leave family");
let family_head = FamilyHead::new(&args.family_head.to_base58_string());
let res = if args.with_vesting_account {
client
.vesting_leave_family(family_head, None)
.await
.expect("failed to leave family with vesting account")
} else {
client
.leave_family(family_head, None)
.await
.expect("failed to leave family")
};
info!("Family leave result: {:?}", res);
}
@@ -0,0 +1,35 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod create_family;
pub mod create_family_join_permit_sign_payload;
pub mod join_family;
pub mod kick_family_member;
pub mod leave_family;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsMixnodeFamilies {
#[clap(subcommand)]
pub command: MixnetOperatorsMixnodeFamiliesCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsMixnodeFamiliesCommands {
/// Create family
CreateFamily(create_family::Args),
/// Join family
JoinFamily(join_family::Args),
/// Leave family,
LeaveFamily(leave_family::Args),
/// Kick family member
KickFamilyMember(kick_family_member::Args),
/// Create a message payload that is required to get signed in order to obtain a permit for joining family
CreateFamilyJoinPermitSignPayload(create_family_join_permit_sign_payload::Args),
}
@@ -4,6 +4,7 @@
use clap::{Args, Subcommand};
pub mod bond_mixnode;
pub mod families;
pub mod keys;
pub mod mixnode_bonding_sign_payload;
pub mod rewards;
@@ -27,6 +28,8 @@ pub enum MixnetOperatorsMixnodeCommands {
Rewards(rewards::MixnetOperatorsMixnodeRewards),
/// Manage your mixnode settings stored in the directory
Settings(settings::MixnetOperatorsMixnodeSettings),
/// Operations for mixnode families
Families(families::MixnetOperatorsMixnodeFamilies),
/// Bond to a mixnode
Bond(bond_mixnode::Args),
/// Unbond from a 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.4.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,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.4.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.4.0" }
# use 0.4.1 as that's the version used by cosmwasm-std 1.0.0
# (and ideally we don't want to pull the same dependency twice)
serde-json-wasm = "=0.4.1"
@@ -1,7 +1,9 @@
use crate::{IdentityKey, IdentityKeyRef};
use cosmwasm_std::Addr;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
@@ -20,9 +22,45 @@ pub struct Family {
feature = "generate-ts",
ts(export_to = "ts-packages/types/src/types/rust/NodeFamilyHead.ts")
)]
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, JsonSchema)]
#[derive(Debug, Clone, Eq, PartialEq, JsonSchema)]
pub struct FamilyHead(IdentityKey);
impl Serialize for FamilyHead {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for FamilyHead {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let inner = IdentityKey::deserialize(deserializer)?;
Ok(FamilyHead(inner))
}
}
impl FromStr for FamilyHead {
type Err = <IdentityKey as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// theoretically we should be verifying whether it's a valid base58 value
// (or even better, whether it's a valid ed25519 public key), but definition of
// `FamilyHead` might change later
Ok(FamilyHead(IdentityKey::from_str(s)?))
}
}
impl Display for FamilyHead {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl FamilyHead {
pub fn new(identity: IdentityKeyRef<'_>) -> Self {
FamilyHead(identity.to_string())
@@ -34,11 +72,11 @@ impl FamilyHead {
}
impl Family {
pub fn new(head: FamilyHead, proxy: Option<Addr>, label: &str) -> Self {
pub fn new(head: FamilyHead, proxy: Option<Addr>, label: String) -> Self {
Family {
head,
proxy: proxy.map(|p| p.to_string()),
label: label.to_string(),
label,
}
}
@@ -60,3 +98,21 @@ impl Family {
&self.label
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn family_head_serde() {
let dummy = FamilyHead::new("foomp");
let ser_str = serde_json_wasm::to_string(&dummy).unwrap();
let de_str: FamilyHead = serde_json_wasm::from_str(&ser_str).unwrap();
assert_eq!(dummy, de_str);
let ser_bytes = serde_json_wasm::to_vec(&dummy).unwrap();
let de_bytes: FamilyHead = serde_json_wasm::from_slice(&ser_bytes).unwrap();
assert_eq!(dummy, de_bytes);
}
}
@@ -3,6 +3,7 @@
use crate::delegation::OwnerProxySubKey;
use crate::error::MixnetContractError;
use crate::families::FamilyHead;
use crate::gateway::GatewayConfigUpdate;
use crate::helpers::IntoBaseDecimal;
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
@@ -82,42 +83,35 @@ pub enum ExecuteMsg {
// Families
/// Only owner of the node can crate the family with node as head
CreateFamily {
owner_signature: String,
label: String,
},
/// Family head needs to sign the joining node IdentityKey
JoinFamily {
signature: String,
family_head: IdentityKey,
join_permit: MessageSignature,
family_head: FamilyHead,
},
LeaveFamily {
signature: String,
family_head: IdentityKey,
family_head: FamilyHead,
},
KickFamilyMember {
signature: String,
member: IdentityKey,
},
CreateFamilyOnBehalf {
owner_address: String,
owner_signature: String,
label: String,
},
/// Family head needs to sign the joining node IdentityKey, MixNode needs to provide its signature proving that it wants to join the family
JoinFamilyOnBehalf {
member_address: String,
node_identity_signature: String,
family_signature: String,
family_head: IdentityKey,
join_permit: MessageSignature,
family_head: FamilyHead,
},
LeaveFamilyOnBehalf {
member_address: String,
node_identity_signature: String,
family_head: IdentityKey,
family_head: FamilyHead,
},
KickFamilyMemberOnBehalf {
head_address: String,
signature: String,
member: IdentityKey,
},
@@ -1,7 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{Gateway, MixNode, MixNodeCostParams};
use crate::families::FamilyHead;
use crate::{Gateway, IdentityKey, MixNode, MixNodeCostParams};
use contracts_common::signing::{
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
};
@@ -10,6 +11,7 @@ use serde::Serialize;
pub type SignableMixNodeBondingMsg = SignableMessage<ContractMessageContent<MixnodeBondingPayload>>;
pub type SignableGatewayBondingMsg = SignableMessage<ContractMessageContent<GatewayBondingPayload>>;
pub type SignableFamilyJoinPermitMsg = SignableMessage<FamilyJoinPermit>;
#[derive(Serialize)]
pub struct MixnodeBondingPayload {
@@ -77,75 +79,43 @@ pub fn construct_gateway_bonding_sign_payload(
}
#[derive(Serialize)]
pub struct FamilyCreationSignature {
label: String,
// TODO: add any extra fields?
pub struct FamilyJoinPermit {
// the granter of this permit
family_head: FamilyHead,
// whether the **member** will want to join via the proxy (i.e. vesting contract)
proxy: Option<Addr>,
// the actual member we want to permit to join
member_node: IdentityKey,
}
impl FamilyCreationSignature {
pub fn new(label: String) -> Self {
Self { label }
impl FamilyJoinPermit {
pub fn new(family_head: FamilyHead, proxy: Option<Addr>, member_node: IdentityKey) -> Self {
Self {
family_head,
proxy,
member_node,
}
}
}
impl SigningPurpose for FamilyCreationSignature {
impl SigningPurpose for FamilyJoinPermit {
fn message_type() -> MessageType {
MessageType::new("family-creation")
MessageType::new("family-join-permit")
}
}
#[derive(Serialize)]
pub struct FamilyJoinSignature {
family_head: String,
// TODO: add any extra fields?
}
pub fn construct_family_join_permit(
nonce: Nonce,
family_head: FamilyHead,
proxy: Option<Addr>,
member_node: IdentityKey,
) -> SignableFamilyJoinPermitMsg {
let payload = FamilyJoinPermit::new(family_head, proxy, member_node);
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")
}
// note: we're NOT wrapping it in `ContractMessageContent` because the family head is not going to be the one
// sending the message to the contract
SignableMessage::new(nonce, payload)
}
// TODO: depending on our threat model, we should perhaps extend it to include all _on_behalf methods
// (update: but we trust our vesting contract since its compromise would be even more devastating so there's no need)
@@ -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.4.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.4.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.4.0" }
serde = { version = "1.0", features = ["derive"] }
schemars = "0.8"
ts-rs = {version = "6.1.2", optional = true}
@@ -1,5 +1,6 @@
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{Coin, Timestamp};
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::{
gateway::GatewayConfigUpdate,
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
@@ -60,21 +61,17 @@ pub enum ExecuteMsg {
// Families
/// Only owner of the node can crate the family with node as head
CreateFamily {
owner_signature: String,
label: String,
},
/// Family head needs to sign the joining node IdentityKey, the Node provides its signature signaling consent to join the family
JoinFamily {
node_identity_signature: String,
family_signature: String,
family_head: IdentityKey,
join_permit: MessageSignature,
family_head: FamilyHead,
},
LeaveFamily {
node_identity_signature: String,
family_head: IdentityKey,
family_head: FamilyHead,
},
KickFamilyMember {
signature: String,
member: IdentityKey,
},
TrackReward {
+14 -10
View File
@@ -99,17 +99,24 @@ impl ConnectionHandler {
let mut framed_conn = Framed::new(conn, EchoPacketCodec);
while !shutdown_listener.is_shutdown() {
tokio::select! {
Some(echo_packet) = framed_conn.next() => {
biased;
_ = shutdown_listener.recv() => {
trace!("ConnectionHandler: Shutdown received");
}
maybe_echo_packet = framed_conn.next() => {
// handle echo packet
let reply_packet = match echo_packet {
Ok(echo_packet) => self.handle_echo_packet(echo_packet),
Err(err) => {
error!(
"The socket connection got corrupted with error: {}. Closing the socket",
err
let reply_packet = match maybe_echo_packet {
Some(Ok(echo_packet)) => self.handle_echo_packet(echo_packet),
Some(Err(err)) => {
error!(
"The socket connection got corrupted with error: {err}. Closing the socket",
);
return;
}
None => {
error!("The socket connection got terminated by the remote!");
return;
}
};
// write back the reply (note the lack of framing)
@@ -125,9 +132,6 @@ impl ConnectionHandler {
return;
}
},
_ = shutdown_listener.recv() => {
trace!("ConnectionHandler: Shutdown received");
}
}
}
}
+3
View File
@@ -26,6 +26,9 @@ nym-sphinx-types = { path = "types" }
nym-crypto = { path = "../crypto", version = "0.2.0" }
nym-topology = { path = "../topology" }
# outfox
nym-outfox = { path = "../../nym-outfox" }
[dev-dependencies]
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
+104 -38
View File
@@ -7,6 +7,8 @@ use nym_crypto::asymmetric::encryption;
use nym_crypto::shared_key::recompute_shared_key;
use nym_crypto::symmetric::stream_cipher;
use nym_crypto::symmetric::stream_cipher::CipherKey;
use nym_outfox::error::OutfoxError;
use nym_outfox::lion::lion_transform_decrypt;
use nym_sphinx_anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx_anonymous_replies::SurbEncryptionKey;
use nym_sphinx_chunking::fragment::Fragment;
@@ -74,54 +76,76 @@ pub enum MessageRecoveryError {
#[error("Failed to recover message fragment - {0}")]
FragmentRecoveryError(#[from] ChunkingError),
#[error("Outfox: {source}")]
OutfoxRecoveryError {
#[from]
source: OutfoxError,
},
}
pub struct MessageReceiver {
/// High level public structure used to buffer all received data [`Fragment`]s and eventually
/// returning original messages that they encapsulate.
#[derive(Default)]
pub struct OutfoxMessageReceiver {
reconstructor: MessageReconstructor,
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
/// Note that it does not include gateway hops.
num_mix_hops: u8,
}
impl MessageReceiver {
impl OutfoxMessageReceiver {
pub fn new() -> Self {
Default::default()
}
}
/// Allows setting non-default number of expected mix hops in the network.
#[must_use]
pub fn with_mix_hops(mut self, hops: u8) -> Self {
self.num_mix_hops = hops;
self
impl MessageReceiver for OutfoxMessageReceiver {
fn new() -> Self {
Self::default()
}
fn decrypt_raw_message<C>(&self, message: &mut [u8], key: &CipherKey<C>)
fn reconstructor(&mut self) -> &mut MessageReconstructor {
&mut self.reconstructor
}
fn num_mix_hops(&self) -> u8 {
DEFAULT_NUM_MIX_HOPS
}
fn decrypt_raw_message<C>(
&self,
message: &mut [u8],
key: &CipherKey<C>,
) -> Result<(), MessageRecoveryError>
where
C: StreamCipher + KeyIvInit,
{
let zero_iv = stream_cipher::zero_iv::<C>();
stream_cipher::decrypt_in_place::<C>(key, &zero_iv, message)
lion_transform_decrypt(message, key)?;
Ok(())
}
}
/// Given raw fragment data, **WITH KEY DIGEST PREFIX ALREADY REMOVED!!**, uses looked up
/// key to decrypt fragment data
pub fn recover_plaintext_from_reply(
pub trait MessageReceiver {
fn new() -> Self;
fn reconstructor(&mut self) -> &mut MessageReconstructor;
fn num_mix_hops(&self) -> u8;
fn decrypt_raw_message<C>(
&self,
message: &mut [u8],
key: &CipherKey<C>,
) -> Result<(), MessageRecoveryError>
where
C: StreamCipher + KeyIvInit;
fn recover_plaintext_from_reply(
&self,
reply_ciphertext: &mut [u8],
reply_key: SurbEncryptionKey,
) {
) -> Result<(), MessageRecoveryError> {
self.decrypt_raw_message::<ReplySurbEncryptionAlgorithm>(
reply_ciphertext,
reply_key.inner(),
)
}
/// Given raw fragment data, recovers the remote ephemeral key, recomputes shared secret,
/// uses it to decrypt fragment data
pub fn recover_plaintext_from_regular_packet<'a>(
fn recover_plaintext_from_regular_packet<'a>(
&self,
local_key: &encryption::PrivateKey,
raw_enc_frag: &'a mut [u8],
@@ -146,30 +170,25 @@ impl MessageReceiver {
// 3. decrypt fragment data
let fragment_ciphertext = &mut raw_enc_frag[encryption::PUBLIC_KEY_SIZE..];
self.decrypt_raw_message::<PacketEncryptionAlgorithm>(fragment_ciphertext, &encryption_key);
self.decrypt_raw_message::<PacketEncryptionAlgorithm>(
fragment_ciphertext,
&encryption_key,
)?;
let fragment_data = fragment_ciphertext;
Ok(fragment_data)
}
/// Given fragment data recovers [`Fragment`] itself.
pub fn recover_fragment(&self, frag_data: &[u8]) -> Result<Fragment, MessageRecoveryError> {
fn recover_fragment(&self, frag_data: &[u8]) -> Result<Fragment, MessageRecoveryError> {
Ok(Fragment::try_from_bytes(frag_data)?)
}
/// Inserts given [`Fragment`] into the reconstructor.
/// If it was last remaining [`Fragment`] for the original message, the message is reconstructed
/// and returned alongside all (if applicable) set ids used in the message.
///
/// # Returns:
/// - The reconstructed message alongside optional reply SURB,
/// - List of ids of all the [`Set`]s used during reconstruction to detect stale retransmissions.
pub fn insert_new_fragment(
fn insert_new_fragment(
&mut self,
fragment: Fragment,
) -> Result<Option<(NymMessage, Vec<i32>)>, MessageRecoveryError> {
if let Some((message, used_sets)) = self.reconstructor.insert_new_fragment(fragment) {
match PaddedMessage::new_reconstructed(message).remove_padding(self.num_mix_hops) {
if let Some((message, used_sets)) = self.reconstructor().insert_new_fragment(fragment) {
match PaddedMessage::new_reconstructed(message).remove_padding(self.num_mix_hops()) {
Ok(message) => Ok(Some((message, used_sets))),
Err(err) => Err(MessageRecoveryError::MalformedReconstructedMessage {
source: err,
@@ -182,9 +201,56 @@ impl MessageReceiver {
}
}
impl Default for MessageReceiver {
#[derive(Clone)]
pub struct SphinxMessageReceiver {
/// High level public structure used to buffer all received data [`Fragment`]s and eventually
/// returning original messages that they encapsulate.
reconstructor: MessageReconstructor,
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
/// Note that it does not include gateway hops.
num_mix_hops: u8,
}
impl SphinxMessageReceiver {
/// Allows setting non-default number of expected mix hops in the network.
#[must_use]
pub fn with_mix_hops(mut self, hops: u8) -> Self {
self.num_mix_hops = hops;
self
}
}
impl MessageReceiver for SphinxMessageReceiver {
fn new() -> Self {
Default::default()
}
fn decrypt_raw_message<C>(
&self,
message: &mut [u8],
key: &CipherKey<C>,
) -> Result<(), MessageRecoveryError>
where
C: StreamCipher + KeyIvInit,
{
let zero_iv = stream_cipher::zero_iv::<C>();
stream_cipher::decrypt_in_place::<C>(key, &zero_iv, message);
Ok(())
}
fn reconstructor(&mut self) -> &mut MessageReconstructor {
&mut self.reconstructor
}
fn num_mix_hops(&self) -> u8 {
self.num_mix_hops
}
}
impl Default for SphinxMessageReceiver {
fn default() -> Self {
MessageReceiver {
SphinxMessageReceiver {
reconstructor: Default::default(),
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
}
+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 = [
+15
View File
@@ -2,6 +2,21 @@
## Unreleased
## [v1.1.6] (2023-04-04)
- change in-contract signatures to include nonces and to sign entire payloads for family-related operations ([#3125])
- change in-contract signatures to include nonces and to sign entire payloads for node bonding (will require wallet changes) ([#3067])
- removed migration code from mixnet and vesting contracts ([#3207])
[#3125]: https://github.com/nymtech/nym/issues/3125
[#3067]: https://github.com/nymtech/nym/issues/3067
[#3207]: https://github.com/nymtech/nym/pull/3207
## [v1.1.5] (2023-03-21)
- Fix contracts and nym-api audit findings ([#3026])
[#3026]: https://github.com/nymtech/nym/issues/3026
## [v1.1.4] (2023-02-21)
- Problem 142 (rust-side) ([#3024])
+2028
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"
+4 -4
View File
@@ -12,10 +12,10 @@ crate-type = ["cdylib", "rlib"]
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"
+8 -8
View File
@@ -12,13 +12,13 @@ nym-coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut
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 +26,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" }
+10 -9
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-mixnet-contract"
version = "1.2.0-pre.0"
version = "1.3.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.4.0" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.4.0" }
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.4.0" }
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,7 +40,7 @@ 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"
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
+1 -1
View File
@@ -1,5 +1,5 @@
opt: wasm
wasm-opt -Os ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm
wasm-opt --disable-sign-ext -Os ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm
wasm:
RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown
+17 -42
View File
@@ -8,7 +8,6 @@ use crate::mixnodes::storage as mixnode_storage;
use crate::rewards::storage as rewards_storage;
use cosmwasm_std::{
entry_point, to_binary, Addr, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
StdError,
};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{
@@ -105,68 +104,56 @@ pub fn execute(
crate::mixnodes::transactions::assign_mixnode_layer(deps, info, mix_id, layer)
}
// families
ExecuteMsg::CreateFamily {
owner_signature,
label,
} => crate::families::transactions::try_create_family(deps, info, owner_signature, &label),
ExecuteMsg::JoinFamily {
signature,
family_head,
} => {
crate::families::transactions::try_join_family(deps, info, None, signature, family_head)
ExecuteMsg::CreateFamily { label } => {
crate::families::transactions::try_create_family(deps, info, label)
}
ExecuteMsg::LeaveFamily {
signature,
ExecuteMsg::JoinFamily {
join_permit,
family_head,
} => crate::families::transactions::try_leave_family(deps, info, signature, family_head),
ExecuteMsg::KickFamilyMember { signature, member } => {
crate::families::transactions::try_head_kick_member(deps, info, signature, &member)
} => crate::families::transactions::try_join_family(deps, info, join_permit, family_head),
ExecuteMsg::LeaveFamily { family_head } => {
crate::families::transactions::try_leave_family(deps, info, family_head)
}
ExecuteMsg::KickFamilyMember { member } => {
crate::families::transactions::try_head_kick_member(deps, info, member)
}
ExecuteMsg::CreateFamilyOnBehalf {
owner_address,
owner_signature,
label,
} => crate::families::transactions::try_create_family_on_behalf(
deps,
info,
owner_address,
owner_signature,
&label,
label,
),
ExecuteMsg::JoinFamilyOnBehalf {
member_address,
node_identity_signature,
family_signature,
join_permit,
family_head,
} => crate::families::transactions::try_join_family_on_behalf(
deps,
info,
member_address,
Some(node_identity_signature),
family_signature,
join_permit,
family_head,
),
ExecuteMsg::LeaveFamilyOnBehalf {
member_address,
node_identity_signature,
family_head,
} => crate::families::transactions::try_leave_family_on_behalf(
deps,
info,
member_address,
node_identity_signature,
family_head,
),
ExecuteMsg::KickFamilyMemberOnBehalf {
head_address,
signature,
member,
} => crate::families::transactions::try_head_kick_member_on_behalf(
deps,
info,
head_address,
signature,
&member,
member,
),
// state/sys-params-related
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
@@ -393,13 +380,13 @@ pub fn query(
&crate::families::queries::get_family_by_head(&head, deps.storage)?,
),
QueryMsg::GetFamilyByLabel { label } => to_binary(
&crate::families::queries::get_family_by_label(&label, deps.storage)?,
&crate::families::queries::get_family_by_label(label, deps.storage)?,
),
QueryMsg::GetFamilyMembersByHead { head } => to_binary(
&crate::families::queries::get_family_members_by_head(&head, deps.storage)?,
),
QueryMsg::GetFamilyMembersByLabel { label } => to_binary(
&crate::families::queries::get_family_members_by_label(&label, deps.storage)?,
&crate::families::queries::get_family_members_by_label(label, deps.storage)?,
),
QueryMsg::GetContractVersion {} => {
to_binary(&crate::mixnet_contract_settings::queries::query_contract_version())
@@ -603,17 +590,6 @@ pub fn migrate(
_env: Env,
msg: MigrateMsg,
) -> Result<Response, MixnetContractError> {
// this is the first migration that uses cw2 standard and thus the value in the storage doesn't yet exist
// set it instead.
if matches!(
cw2::get_contract_version(deps.storage),
Err(StdError::NotFound { .. })
) {
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
crate::queued_migrations::create_epoch_status(deps.storage)?;
return Ok(Response::new());
}
// note: don't remove this particular bit of code as we have to ALWAYS check whether we have to update the stored version
let version: Version = CONTRACT_VERSION.parse().map_err(|error: semver::Error| {
MixnetContractError::SemVerFailure {
@@ -635,8 +611,7 @@ pub fn migrate(
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
// If state structure changed in any contract version in the way migration is needed, it
// should occur here
crate::queued_migrations::create_epoch_status(deps.storage)?;
// should occur here, for example anything from `crate::queued_migrations::`
}
// due to circular dependency on contract addresses (i.e. mixnet contract requiring vesting contract address
+4
View File
@@ -1,3 +1,7 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod queries;
pub mod signature_helpers;
pub mod storage;
pub mod transactions;
+10 -19
View File
@@ -1,31 +1,27 @@
use std::collections::HashSet;
use crate::constants::{FAMILIES_DEFAULT_RETRIEVAL_LIMIT, FAMILIES_MAX_RETRIEVAL_LIMIT};
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage::{families, get_family, get_members, MEMBERS};
use crate::constants::{FAMILIES_DEFAULT_RETRIEVAL_LIMIT, FAMILIES_MAX_RETRIEVAL_LIMIT};
use cosmwasm_std::{Order, Storage};
use cw_storage_plus::Bound;
use mixnet_contract_common::families::{Family, FamilyHead};
use mixnet_contract_common::{error::MixnetContractError, IdentityKeyRef};
use mixnet_contract_common::{IdentityKey, PagedFamiliesResponse, PagedMembersResponse};
use std::collections::HashSet;
pub fn get_family_by_label(
label: &str,
label: String,
storage: &dyn Storage,
) -> Result<Option<Family>, MixnetContractError> {
Ok(families()
.idx
.label
.item(storage, label.to_string())?
.map(|o| o.1))
Ok(families().idx.label.item(storage, label)?.map(|o| o.1))
}
pub fn get_family_by_head(
head: IdentityKeyRef<'_>,
storage: &dyn Storage,
) -> Result<Family, MixnetContractError> {
let family_head = FamilyHead::new(head);
get_family(&family_head, storage)
) -> Result<Option<Family>, MixnetContractError> {
Ok(families().may_load(storage, head.to_string())?)
}
pub fn get_family_members_by_head(
@@ -38,15 +34,10 @@ pub fn get_family_members_by_head(
}
pub fn get_family_members_by_label(
label: &str,
label: String,
storage: &dyn Storage,
) -> Result<Option<HashSet<String>>, MixnetContractError> {
if let Some(family) = families()
.idx
.label
.item(storage, label.to_string())?
.map(|o| o.1)
{
if let Some(family) = families().idx.label.item(storage, label)?.map(|o| o.1) {
Ok(Some(get_members(&family, storage)?))
} else {
Ok(None)
@@ -0,0 +1,38 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::mixnodes::storage as mixnodes_storage;
use crate::signing::storage as signing_storage;
use crate::support::helpers::decode_ed25519_identity_key;
use cosmwasm_std::{Addr, Deps};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::{construct_family_join_permit, IdentityKeyRef};
use nym_contracts_common::signing::{MessageSignature, Verifier};
pub(crate) fn verify_family_join_permit(
deps: Deps<'_>,
granter: FamilyHead,
proxy: Option<Addr>,
member: IdentityKeyRef,
signature: MessageSignature,
) -> Result<(), MixnetContractError> {
// recover the public key
let public_key = decode_ed25519_identity_key(granter.identity())?;
// that's kinda a backwards way of getting the granter's nonce, but it works, so ¯\_(ツ)_/¯
let Some(head_mixnode) = mixnodes_storage::mixnode_bonds()
.idx
.identity_key
.item(deps.storage, granter.identity().to_owned())?.map(|record| record.1) else {
return Err(MixnetContractError::FamilyDoesNotExist { head: granter.identity().to_string() })
};
let nonce = signing_storage::get_signing_nonce(deps.storage, head_mixnode.owner)?;
let msg = construct_family_join_permit(nonce, granter, proxy, member.to_owned());
if deps.api.verify_message(msg, signature, &public_key)? {
Ok(())
} else {
Err(MixnetContractError::InvalidEd25519Signature)
}
}
+6 -18
View File
@@ -1,9 +1,11 @@
use std::collections::HashSet;
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Order, StdError, Storage};
use cosmwasm_std::{Order, Storage};
use cw_storage_plus::{Index, IndexList, IndexedMap, Map, UniqueIndex};
use mixnet_contract_common::families::{Family, FamilyHead};
use mixnet_contract_common::{error::MixnetContractError, IdentityKey, IdentityKeyRef};
use std::collections::HashSet;
use crate::constants::{FAMILIES_INDEX_NAMESPACE, FAMILIES_MAP_NAMESPACE, MEMBERS_MAP_NAMESPACE};
@@ -51,22 +53,8 @@ pub fn get_family(head: &FamilyHead, store: &dyn Storage) -> Result<Family, Mixn
}
}
pub fn create_family(f: &Family, store: &mut dyn Storage) -> Result<(), MixnetContractError> {
match families().save(store, f.head_identity().to_string(), f) {
Ok(()) => Ok(()),
Err(e) => match &e {
StdError::GenericErr { msg } => {
if msg.starts_with("Violates unique constraint") {
Err(MixnetContractError::FamilyWithLabelExists(
f.label().to_string(),
))
} else {
Err(e.into())
}
}
_ => Err(e.into()),
},
}
pub fn save_family(f: &Family, store: &mut dyn Storage) -> Result<(), MixnetContractError> {
Ok(families().save(store, f.head_identity().to_string(), f)?)
}
pub fn add_family_member(
+146 -239
View File
@@ -1,136 +1,99 @@
use crate::support::helpers::{
ensure_bonded, ensure_sent_by_vesting_contract, validate_family_signature,
validate_node_identity_signature,
};
use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response};
use mixnet_contract_common::families::{Family, FamilyHead};
use mixnet_contract_common::{error::MixnetContractError, IdentityKey, IdentityKeyRef};
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage::{
add_family_member, create_family, get_family, is_any_member, is_family_member,
remove_family_member,
add_family_member, get_family, is_any_member, is_family_member, remove_family_member,
save_family,
};
use crate::families::queries::get_family_by_label;
use crate::families::signature_helpers::verify_family_join_permit;
use crate::support::helpers::{ensure_bonded, ensure_sent_by_vesting_contract};
use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response};
use mixnet_contract_common::families::{Family, FamilyHead};
use mixnet_contract_common::{error::MixnetContractError, IdentityKey};
use nym_contracts_common::signing::MessageSignature;
/// Creates a new MixNode family with senders node as head
pub fn try_create_family(
deps: DepsMut,
info: MessageInfo,
owner_signature: String,
label: &str,
label: String,
) -> Result<Response, MixnetContractError> {
_try_create_family(deps, &info.sender, owner_signature, label, None)
_try_create_family(deps, &info.sender, label, None)
}
pub fn try_create_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
owner_address: String,
owner_signature: String,
label: &str,
label: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let owner_address = deps.api.addr_validate(&owner_address)?;
_try_create_family(
deps,
&owner_address,
owner_signature,
label,
Some(info.sender),
)
_try_create_family(deps, &owner_address, label, Some(info.sender))
}
fn _try_create_family(
deps: DepsMut,
owner: &Addr,
owner_signature: String,
label: &str,
label: String,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond = crate::mixnodes::storage::mixnode_bonds()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond {
owner: owner.clone(),
})?
.1;
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
ensure_bonded(&existing_bond)?;
validate_node_identity_signature(
deps.as_ref(),
owner,
&owner_signature,
existing_bond.identity(),
)?;
let family_head = FamilyHead::new(existing_bond.identity());
if let Ok(_family) = get_family(&family_head, deps.storage) {
// can't overwrite existing family
if get_family(&family_head, deps.storage).is_ok() {
return Err(MixnetContractError::FamilyCanHaveOnlyOne);
}
// the label must be unique
if get_family_by_label(label.clone(), deps.storage)?.is_some() {
return Err(MixnetContractError::FamilyWithLabelExists(label));
}
let family = Family::new(family_head, proxy, label);
create_family(&family, deps.storage)?;
save_family(&family, deps.storage)?;
Ok(Response::default())
}
pub fn try_join_family(
deps: DepsMut,
info: MessageInfo,
// Required for proxy joining
node_identity_signature: Option<String>,
family_signature: String,
family_head: IdentityKey,
join_permit: MessageSignature,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
let family_head = FamilyHead::new(&family_head);
_try_join_family(
deps,
&info.sender,
node_identity_signature,
family_signature,
family_head,
)
_try_join_family(deps, &info.sender, join_permit, family_head, None)
}
pub fn try_join_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
member_address: String,
node_identity_signature: Option<String>,
family_signature: String,
family_head: IdentityKey,
join_permit: MessageSignature,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let member_address = deps.api.addr_validate(&member_address)?;
let family_head = FamilyHead::new(&family_head);
_try_join_family(
deps,
&member_address,
node_identity_signature,
family_signature,
family_head,
)
let proxy = Some(info.sender);
_try_join_family(deps, &member_address, join_permit, family_head, proxy)
}
fn _try_join_family(
deps: DepsMut,
owner: &Addr,
node_identity_signature: Option<String>,
family_signature: String,
join_permit: MessageSignature,
family_head: FamilyHead,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond = crate::mixnodes::storage::mixnode_bonds()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond {
owner: owner.clone(),
})?
.1;
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
ensure_bonded(&existing_bond)?;
@@ -147,20 +110,12 @@ fn _try_join_family(
));
}
if let Some(node_identity_signature) = node_identity_signature {
validate_node_identity_signature(
deps.as_ref(),
owner,
&node_identity_signature,
existing_bond.identity(),
)?;
}
validate_family_signature(
verify_family_join_permit(
deps.as_ref(),
family_head.clone(),
proxy,
existing_bond.identity(),
&family_signature,
family_head.identity(),
join_permit,
)?;
let family = get_family(&family_head, deps.storage)?;
@@ -173,41 +128,30 @@ fn _try_join_family(
pub fn try_leave_family(
deps: DepsMut,
info: MessageInfo,
signature: String,
family_head: IdentityKey,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
let family_head = FamilyHead::new(&family_head);
_try_leave_family(deps, &info.sender, signature, family_head)
_try_leave_family(deps, &info.sender, family_head)
}
pub fn try_leave_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
member_address: String,
node_family_signature: String,
family_head: IdentityKey,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let family_head = FamilyHead::new(&family_head);
let member_address = deps.api.addr_validate(&member_address)?;
_try_leave_family(deps, &member_address, node_family_signature, family_head)
_try_leave_family(deps, &member_address, family_head)
}
fn _try_leave_family(
deps: DepsMut,
owner: &Addr,
node_family_signature: String,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
let existing_bond = crate::mixnodes::storage::mixnode_bonds()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond {
owner: owner.clone(),
})?
.1;
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
ensure_bonded(&existing_bond)?;
@@ -226,13 +170,6 @@ fn _try_leave_family(
});
}
validate_node_identity_signature(
deps.as_ref(),
owner,
&node_family_signature,
existing_bond.identity(),
)?;
remove_family_member(deps.storage, existing_bond.identity());
Ok(Response::default())
@@ -241,62 +178,56 @@ fn _try_leave_family(
pub fn try_head_kick_member(
deps: DepsMut,
info: MessageInfo,
owner_signature: String,
member: IdentityKeyRef,
member: IdentityKey,
) -> Result<Response, MixnetContractError> {
_try_head_kick_member(deps, &info.sender, owner_signature, member)
_try_head_kick_member(deps, &info.sender, member)
}
pub fn try_head_kick_member_on_behalf(
deps: DepsMut,
info: MessageInfo,
head_address: String,
owner_signature: String,
member: IdentityKeyRef,
member: IdentityKey,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let head_address = deps.api.addr_validate(&head_address)?;
_try_head_kick_member(deps, &head_address, owner_signature, member)
_try_head_kick_member(deps, &head_address, member)
}
#[allow(unused_variables)]
fn _try_head_kick_member(
deps: DepsMut,
owner: &Addr,
owner_signature: String,
member: IdentityKeyRef<'_>,
member: IdentityKey,
) -> Result<Response, MixnetContractError> {
Err(MixnetContractError::NotImplemented)
// let existing_bond = crate::mixnodes::storage::mixnode_bonds()
// .idx
// .owner
// .item(deps.storage, owner.clone())?
// .ok_or(MixnetContractError::NoAssociatedMixNodeBond {
// owner: owner.clone(),
// })?
// .1;
let head_bond = crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
// ensure_bonded(&existing_bond)?;
// make sure we're still in the mixnet
ensure_bonded(&head_bond)?;
// validate_node_identity_signature(
// deps.as_ref(),
// owner,
// &owner_signature,
// existing_bond.identity(),
// )?;
// make sure we're not trying to kick ourselves...
if member == head_bond.identity() {
return Err(MixnetContractError::CantLeaveOwnFamily {
head: head_bond.identity().to_string(),
member,
});
}
// let family_head = FamilyHead::new(existing_bond.identity());
// let family = get_family(&family_head, deps.storage)?;
// if !is_family_member(deps.storage, &family, member)? {
// return Err(MixnetContractError::NotAMember {
// head: family_head.identity().to_string(),
// member: existing_bond.identity().to_string(),
// });
// }
// get the family details
let family_head = FamilyHead::new(head_bond.identity());
let family = get_family(&family_head, deps.storage)?;
// remove_family_member(deps.storage, member);
// Ok(Response::default())
// make sure the member we're trying to kick is an actual member
if !is_family_member(deps.storage, &family, &member)? {
return Err(MixnetContractError::NotAMember {
head: family_head.identity().to_string(),
member,
});
}
// finally get rid of the member
remove_family_member(deps.storage, &member);
Ok(Response::default())
}
#[cfg(test)]
@@ -322,9 +253,9 @@ mod test {
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) =
let (malicious_mixnode, malicious_bond_sig, _malicious_keypair) =
test.mixnode_with_signature(malicious_head, None);
let (member_mixnode, member_bond_sig, member_keypair) =
let (member_mixnode, member_bond_sig, _member_keypair) =
test.mixnode_with_signature(member, None);
crate::mixnodes::transactions::try_add_mixnode(
@@ -357,26 +288,14 @@ mod test {
)
.unwrap();
let old_style_head_sig = head_keypair.private_key().sign_text(head);
let old_style_malicious_head_sig =
malicious_keypair.private_key().sign_text(malicious_head);
let old_style_member_sig = member_keypair.private_key().sign_text(member);
try_create_family(
test.deps_mut(),
mock_info(head, &[]),
old_style_head_sig,
"test",
)
.unwrap();
try_create_family(test.deps_mut(), mock_info(head, &[]), "test".to_string()).unwrap();
let family_head = FamilyHead::new(&head_mixnode.identity_key);
assert!(get_family(&family_head, test.deps().storage).is_ok());
let nope = try_create_family(
test.deps_mut(),
mock_info(malicious_head, &[]),
old_style_malicious_head_sig,
"test",
"test".to_string(),
);
match nope {
@@ -387,24 +306,23 @@ mod test {
},
}
let family = get_family_by_label("test", test.deps().storage).unwrap();
let family = get_family_by_label("test".to_string(), 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(), test.deps().storage)
.unwrap()
.unwrap();
assert_eq!(family.head_identity(), family_head.identity());
let join_signature = head_keypair
.private_key()
.sign(member_mixnode.identity_key.as_bytes())
.to_base58_string();
let join_permit =
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key, false);
try_join_family(
test.deps_mut(),
mock_info(member, &[]),
Some(old_style_member_sig.clone()),
join_signature.clone(),
head_mixnode.identity_key.clone(),
join_permit,
family_head.clone(),
)
.unwrap();
@@ -414,11 +332,34 @@ mod test {
is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
try_leave_family(
try_leave_family(test.deps_mut(), mock_info(member, &[]), family_head.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 new_join_permit =
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key, false);
try_join_family(
test.deps_mut(),
mock_info(member, &[]),
old_style_member_sig.clone(),
head_mixnode.identity_key.clone(),
new_join_permit,
family_head.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()
);
try_head_kick_member(
test.deps_mut(),
mock_info(head, &[]),
member_mixnode.identity_key.clone(),
)
.unwrap();
@@ -426,32 +367,6 @@ mod test {
assert!(
!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
try_join_family(
test.deps_mut(),
mock_info(member, &[]),
Some(old_style_member_sig),
join_signature,
head_mixnode.identity_key,
)
.unwrap();
let family = get_family(&family_head, test.deps().storage).unwrap();
assert!(
is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
// try_head_kick_member(
// deps.as_mut(),
// mock_info(&head, &[]),
// head_sig.clone(),
// &member_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());
}
#[cfg(test)]
@@ -468,15 +383,13 @@ mod test {
let head = "alice";
let (_, keypair) = test.add_dummy_mixnode_with_proxy_and_keypair(head, None);
let sig = keypair.private_key().sign_text(head);
test.add_dummy_mixnode(head, None);
let res = try_create_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
head.to_string(),
sig,
"label",
"label".to_string(),
)
.unwrap_err();
@@ -507,23 +420,22 @@ 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
.private_key()
.sign_text(&member_keys.public_key().to_base58_string());
let member_sig = member_keys.private_key().sign_text(new_member);
let join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
false,
);
let head_identity = head_keys.public_key().to_base58_string();
let family_head = FamilyHead::new(&head_identity);
let res = try_join_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
new_member.to_string(),
Some(member_sig),
join_signature,
head_identity,
join_permit,
family_head,
)
.unwrap_err();
@@ -554,33 +466,30 @@ 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
.private_key()
.sign_text(&member_keys.public_key().to_base58_string());
let member_sig = member_keys.private_key().sign_text(new_member);
let join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
true,
);
let head_identity = head_keys.public_key().to_base58_string();
let family_head = FamilyHead::new(&head_identity);
try_join_family_on_behalf(
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
new_member.to_string(),
Some(member_sig.clone()),
join_signature,
head_identity,
join_permit,
family_head.clone(),
)
.unwrap();
let leave_signature = member_sig;
let res = try_leave_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
new_member.to_string(),
leave_signature,
head_keys.public_key().to_base58_string(),
family_head.clone(),
)
.unwrap_err();
@@ -611,33 +520,31 @@ 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
.private_key()
.sign_text(&member_keys.public_key().to_base58_string());
let member_sig = member_keys.private_key().sign_text(new_member);
let join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
true,
);
let head_identity = head_keys.public_key().to_base58_string();
let family_head = FamilyHead::new(&head_identity);
try_join_family_on_behalf(
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
new_member.to_string(),
Some(member_sig),
join_signature,
head_identity,
join_permit,
family_head,
)
.unwrap();
// TODO: a completely wrong signature is being used
let res = try_head_kick_member_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
head.to_string(),
head_keys.private_key().sign_text(head),
&member_keys.public_key().to_base58_string(),
member_keys.public_key().to_base58_string(),
)
.unwrap_err();
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::signing::storage as signing_storage;
use crate::support::helpers::decode_ed25519_identity_key;
use cosmwasm_std::{Addr, Coin, Deps};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{construct_gateway_bonding_sign_payload, Gateway};
@@ -17,10 +18,7 @@ pub(crate) fn verify_gateway_bonding_signature(
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()))?;
let public_key = decode_ed25519_identity_key(&gateway.identity_key)?;
// reconstruct the payload
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
@@ -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(
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::signing::storage as signing_storage;
use crate::support::helpers::decode_ed25519_identity_key;
use cosmwasm_std::{Addr, Coin, Deps};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNode, MixNodeCostParams};
@@ -18,10 +19,7 @@ pub(crate) fn verify_mixnode_bonding_signature(
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()))?;
let public_key = decode_ed25519_identity_key(&mixnode.identity_key)?;
// reconstruct the payload
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
@@ -245,12 +245,7 @@ pub(crate) fn _try_remove_mixnode(
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond = storage::mixnode_bonds()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner })?
.1;
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &owner)?;
// unbonding is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
+1 -17
View File
@@ -1,18 +1,2 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::interval::storage as interval_storage;
use cosmwasm_std::Storage;
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::EpochStatus;
pub(crate) fn create_epoch_status(storage: &mut dyn Storage) -> Result<(), MixnetContractError> {
let current_rewarding_validator =
crate::mixnet_contract_settings::storage::rewarding_validator_address(storage)?;
interval_storage::save_current_epoch_status(
storage,
&EpochStatus::new(current_rewarding_validator),
)?;
Ok(())
}
+9 -176
View File
@@ -4,9 +4,7 @@
use crate::gateways::storage as gateways_storage;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::storage as mixnodes_storage;
use cosmwasm_std::{
wasm_execute, Addr, BankMsg, Coin, CosmosMsg, Deps, MessageInfo, Response, Storage,
};
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, CosmosMsg, MessageInfo, Response, Storage};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{EpochState, EpochStatus, IdentityKeyRef, MixId, MixNodeBond};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
@@ -371,184 +369,19 @@ pub(crate) fn ensure_no_existing_bond(
Ok(())
}
pub fn validate_node_identity_signature(
deps: Deps<'_>,
owner: &Addr,
signature: &str,
identity: IdentityKeyRef<'_>,
) -> Result<(), MixnetContractError> {
validate_ed25519_signature(deps, owner.as_bytes(), signature, identity)
}
pub fn validate_family_signature(
deps: Deps<'_>,
family_member: IdentityKeyRef<'_>,
signature: &str,
family_head: IdentityKeyRef<'_>,
) -> Result<(), MixnetContractError> {
validate_ed25519_signature(deps, family_member.as_bytes(), signature, family_head)
}
pub(crate) fn validate_ed25519_signature(
deps: Deps<'_>,
signed_bytes: &[u8],
signature: &str,
identity: IdentityKeyRef<'_>,
) -> Result<(), MixnetContractError> {
let mut identity_bytes = [0u8; 32];
let mut signature_bytes = [0u8; 64];
let identity_used_bytes = bs58::decode(identity)
.into(&mut identity_bytes)
pub(crate) fn decode_ed25519_identity_key(
encoded: IdentityKeyRef,
) -> Result<[u8; 32], MixnetContractError> {
let mut public_key = [0u8; 32];
let used = bs58::decode(encoded)
.into(&mut public_key)
.map_err(|err| MixnetContractError::MalformedEd25519IdentityKey(err.to_string()))?;
let signature_used_bytes = bs58::decode(signature)
.into(&mut signature_bytes)
.map_err(|err| MixnetContractError::MalformedEd25519Signature(err.to_string()))?;
if identity_used_bytes != 32 {
if used != 32 {
return Err(MixnetContractError::MalformedEd25519IdentityKey(
"Too few bytes provided for the public key".into(),
));
}
if signature_used_bytes != 64 {
return Err(MixnetContractError::MalformedEd25519Signature(
"Too few bytes provided for the signature".into(),
));
}
let res = deps
.api
.ed25519_verify(signed_bytes, &signature_bytes, &identity_bytes)
.map_err(cosmwasm_std::StdError::verification_err)?;
if !res {
Err(MixnetContractError::InvalidEd25519Signature)
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::mock_dependencies;
use nym_crypto::asymmetric::identity;
use rand_chacha::rand_core::SeedableRng;
#[test]
fn validating_node_signature() {
let deps = mock_dependencies();
// since those tests are NOT compiled to wasm, we can use rng-related dependency
let dummy_seed = [1u8; 32];
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
let short_bs58 = "2SfEgZ4aQUr3HSwqE";
let long_bs58 = "g34PyULki9fc3FqKobj5wdVNCaWAt1M9oZowyyMFfWSCejxg7wt574piZVjqjFEN2UXsgZ56KTkKf3jnWD4DJ2Gsf7KXQAvptFfcYRrZHTjMVo3NXcBSNm3wDBKZWZURzp4Fixv";
let address1 = Addr::unchecked("some-dummy-address1");
let address2 = Addr::unchecked("some-dummy-address2");
let keypair1 = identity::KeyPair::new(&mut rng);
let keypair2 = identity::KeyPair::new(&mut rng);
let sig_addr1_key1 = keypair1
.private_key()
.sign(address1.as_bytes())
.to_base58_string();
let sig_addr2_key1 = keypair1
.private_key()
.sign(address2.as_bytes())
.to_base58_string();
let sig_addr1_key2 = keypair2
.private_key()
.sign(address1.as_bytes())
.to_base58_string();
assert_eq!(
Err(MixnetContractError::MalformedEd25519IdentityKey(
"buffer provided to decode base58 encoded string into was too small".into()
)),
validate_node_identity_signature(deps.as_ref(), &address1, &sig_addr1_key1, long_bs58,)
);
assert_eq!(
Err(MixnetContractError::MalformedEd25519Signature(
"buffer provided to decode base58 encoded string into was too small".into()
)),
validate_node_identity_signature(
deps.as_ref(),
&address1,
long_bs58.into(),
&keypair1.public_key().to_base58_string(),
)
);
assert_eq!(
Err(MixnetContractError::MalformedEd25519IdentityKey(
"Too few bytes provided for the public key".into()
)),
validate_node_identity_signature(deps.as_ref(), &address1, &sig_addr1_key1, short_bs58,)
);
assert_eq!(
Err(MixnetContractError::MalformedEd25519Signature(
"Too few bytes provided for the signature".into()
)),
validate_node_identity_signature(
deps.as_ref(),
&address1,
short_bs58.into(),
&keypair1.public_key().to_base58_string(),
)
);
assert_eq!(
Err(MixnetContractError::InvalidEd25519Signature),
validate_node_identity_signature(
deps.as_ref(),
&address1,
&sig_addr1_key1,
&keypair2.public_key().to_base58_string(),
)
);
assert_eq!(
Err(MixnetContractError::InvalidEd25519Signature),
validate_node_identity_signature(
deps.as_ref(),
&address1,
&sig_addr2_key1,
&keypair1.public_key().to_base58_string(),
)
);
assert_eq!(
Err(MixnetContractError::InvalidEd25519Signature),
validate_node_identity_signature(
deps.as_ref(),
&address2,
&sig_addr1_key1,
&keypair1.public_key().to_base58_string(),
)
);
assert_eq!(
Err(MixnetContractError::InvalidEd25519Signature),
validate_node_identity_signature(
deps.as_ref(),
&address1,
&sig_addr1_key2,
&keypair1.public_key().to_base58_string(),
)
);
assert!(validate_node_identity_signature(
deps.as_ref(),
&address1,
&sig_addr1_key1,
&keypair1.public_key().to_base58_string(),
)
.is_ok());
}
Ok(public_key)
}
+99 -14
View File
@@ -54,6 +54,7 @@ pub mod test_helpers {
use mixnet_contract_common::events::{
may_find_attribute, MixnetEventType, DELEGATES_REWARD_KEY, OPERATOR_REWARD_KEY,
};
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::mixnode::{MixNodeRewarding, UnbondedMixnode};
use mixnet_contract_common::pending_events::{PendingEpochEventData, PendingIntervalEventData};
use mixnet_contract_common::reward_params::{Performance, RewardingParams};
@@ -61,10 +62,10 @@ 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,
construct_family_join_permit, Delegation, EpochState, EpochStatus, Gateway,
GatewayBondingPayload, IdentityKey, IdentityKeyRef, InitialRewardingParams, InstantiateMsg,
Interval, MixId, MixNode, MixNodeBond, MixnodeBondingPayload, Percent,
RewardedSetNodeStatus, SignableGatewayBondingMsg, SignableMixNodeBondingMsg,
};
use nym_contracts_common::signing::{
ContractMessageContent, MessageSignature, SignableMessage, SigningAlgorithm, SigningPurpose,
@@ -169,26 +170,64 @@ pub mod test_helpers {
.collect::<Vec<_>>()
}
pub fn generate_family_join_permit(
&mut self,
family_owner_keys: &identity::KeyPair,
member_node: IdentityKeyRef,
vesting: bool,
) -> MessageSignature {
let identity = family_owner_keys.public_key().to_base58_string();
let head_mixnode = mixnodes_storage::mixnode_bonds()
.idx
.identity_key
.item(self.deps().storage, identity.clone())
.unwrap()
.map(|record| record.1)
.unwrap();
let family_head = FamilyHead::new(&identity);
let owner = head_mixnode.owner;
let nonce =
signing_storage::get_signing_nonce(self.deps().storage, owner.clone()).unwrap();
let proxy = if vesting {
Some(self.vesting_contract())
} else {
None
};
let msg =
construct_family_join_permit(nonce, family_head, proxy, member_node.to_owned());
let sig_bytes = family_owner_keys
.private_key()
.sign(&msg.to_plaintext().unwrap())
.to_bytes();
MessageSignature::from(sig_bytes.as_ref())
}
#[allow(unused)]
pub fn join_family(
&mut self,
member: &str,
member_keys: &identity::KeyPair,
head_keys: &identity::KeyPair,
vesting: bool,
) {
let identity_signature = member_keys.private_key().sign_text(member);
let join_signature = head_keys
.private_key()
.sign(&member_keys.public_key().to_bytes())
.to_base58_string();
let member_identity = member_keys.public_key().to_base58_string();
let head_identity = head_keys.public_key().to_base58_string();
let join_permit =
self.generate_family_join_permit(head_keys, &member_identity, vesting);
let family_head = FamilyHead::new(&head_identity);
try_join_family(
self.deps_mut(),
mock_info(member, &[]),
Some(identity_signature),
join_signature,
head_identity,
join_permit,
family_head,
)
.unwrap();
}
@@ -199,9 +238,8 @@ pub mod test_helpers {
label: &str,
) -> (MixId, identity::KeyPair) {
let (mix_id, keys) = self.add_dummy_mixnode_with_proxy_and_keypair(head, None);
let sig = keys.private_key().sign_text(head);
try_create_family(self.deps_mut(), mock_info(head, &[]), sig, label).unwrap();
try_create_family(self.deps_mut(), mock_info(head, &[]), label.to_string()).unwrap();
(mix_id, keys)
}
@@ -290,6 +328,53 @@ pub mod test_helpers {
ed25519_sign_message(msg, key)
}
pub fn add_dummy_mixnode_with_keypair(
&mut self,
owner: &str,
stake: Option<Uint128>,
) -> (MixId, identity::KeyPair) {
let stake = self.make_mix_pledge(stake);
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(),
owner,
None,
mixnode.clone(),
stake.clone(),
);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
let info = mock_info(owner, &stake);
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, keypair)
}
pub fn add_dummy_mixnode_with_proxy_and_keypair(
&mut self,
owner: &str,
@@ -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.3.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.4.0" }
contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", package = "nym-contracts-common", version = "0.4.0" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.4.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"] }
+15 -47
View File
@@ -11,9 +11,10 @@ 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,
QueryResponse, Response, StdError, StdResult, Timestamp, Uint128,
QueryResponse, Response, StdResult, Timestamp, Uint128,
};
use cw_storage_plus::Bound;
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::gateway::GatewayConfigUpdate;
use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use mixnet_contract_common::{Gateway, MixId, MixNode};
@@ -75,15 +76,6 @@ pub fn instantiate(
#[entry_point]
pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
// this is the first migration that uses cw2 standard and thus the value in the storage doesn't yet exist
// set it instead.
if matches!(
cw2::get_contract_version(deps.storage),
Err(StdError::NotFound { .. })
) {
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
}
// note: don't remove this particular bit of code as we have to ALWAYS check whether we have to update the stored version
let version: Version =
CONTRACT_VERSION
@@ -106,7 +98,7 @@ pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Respons
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
// If state structure changed in any contract version in the way migration is needed, it
// should occur here
// should occur here, for example anything from `crate::queued_migrations::`
}
Ok(Response::new())
@@ -120,28 +112,13 @@ pub fn execute(
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::CreateFamily {
owner_signature,
label,
} => try_create_family(info, deps, owner_signature, label),
ExecuteMsg::CreateFamily { label } => try_create_family(info, deps, label),
ExecuteMsg::JoinFamily {
node_identity_signature,
family_signature,
join_permit,
family_head,
} => try_join_family(
info,
deps,
node_identity_signature,
family_signature,
family_head,
),
ExecuteMsg::LeaveFamily {
node_identity_signature,
family_head,
} => try_leave_family(info, deps, node_identity_signature, family_head),
ExecuteMsg::KickFamilyMember { signature, member } => {
try_kick_family_member(info, deps, signature, member)
}
} => try_join_family(info, deps, join_permit, family_head),
ExecuteMsg::LeaveFamily { family_head } => try_leave_family(info, deps, family_head),
ExecuteMsg::KickFamilyMember { member } => try_kick_family_member(info, deps, member),
ExecuteMsg::UpdateLockedPledgeCap { address, cap } => {
try_update_locked_pledge_cap(address, cap, info, deps)
}
@@ -235,44 +212,35 @@ pub fn execute(
pub fn try_create_family(
info: MessageInfo,
deps: DepsMut,
owner_signature: String,
label: String,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?;
account.try_create_family(deps.storage, owner_signature, label)
account.try_create_family(deps.storage, label)
}
pub fn try_join_family(
info: MessageInfo,
deps: DepsMut,
node_identity_signature: String,
family_signature: String,
family_head: String,
join_permit: MessageSignature,
family_head: FamilyHead,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?;
account.try_join_family(
deps.storage,
node_identity_signature,
family_signature,
&family_head,
)
account.try_join_family(deps.storage, join_permit, family_head)
}
pub fn try_leave_family(
info: MessageInfo,
deps: DepsMut,
node_identity_signature: String,
family_head: String,
family_head: FamilyHead,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?;
account.try_leave_family(deps.storage, node_identity_signature, &family_head)
account.try_leave_family(deps.storage, family_head)
}
pub fn try_kick_family_member(
info: MessageInfo,
deps: DepsMut,
signature: String,
member: String,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?;
account.try_head_kick_member(deps.storage, signature, &member)
account.try_head_kick_member(deps.storage, &member)
}
/// Update locked_pledge_cap, the hard cap for staking/bonding with unvested tokens.
+1 -1
View File
@@ -1,2 +1,2 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
@@ -1,34 +1,32 @@
use crate::errors::ContractError;
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{Response, Storage};
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::IdentityKeyRef;
pub trait NodeFamilies {
fn try_create_family(
&self,
storage: &dyn Storage,
owner_signature: String,
label: String,
) -> Result<Response, ContractError>;
fn try_join_family(
&self,
storage: &dyn Storage,
node_identity_signature: String,
family_signature: String,
family_head: IdentityKeyRef,
join_permit: MessageSignature,
family_head: FamilyHead,
) -> Result<Response, ContractError>;
fn try_leave_family(
&self,
storage: &dyn Storage,
signature: String,
family_head: IdentityKeyRef,
family_head: FamilyHead,
) -> Result<Response, ContractError>;
fn try_head_kick_member(
&self,
storage: &dyn Storage,
signature: String,
member: IdentityKeyRef<'_>,
) -> Result<Response, ContractError>;
}
@@ -1,18 +1,18 @@
use super::Account;
use crate::{errors::ContractError, storage::MIXNET_CONTRACT_ADDRESS, traits::NodeFamilies};
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{wasm_execute, Response, Storage};
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, IdentityKeyRef};
impl NodeFamilies for Account {
fn try_create_family(
&self,
storage: &dyn Storage,
owner_signature: String,
label: String,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::CreateFamilyOnBehalf {
owner_address: self.owner_address().to_string(),
owner_signature,
owner_address: self.owner_address().into_string(),
label,
};
@@ -24,15 +24,13 @@ impl NodeFamilies for Account {
fn try_join_family(
&self,
storage: &dyn Storage,
node_identity_signature: String,
family_signature: String,
family_head: IdentityKeyRef,
join_permit: MessageSignature,
family_head: FamilyHead,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::JoinFamilyOnBehalf {
member_address: self.owner_address().to_string(),
node_identity_signature,
family_signature,
family_head: family_head.to_string(),
join_permit,
family_head,
};
let msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?;
@@ -43,13 +41,11 @@ impl NodeFamilies for Account {
fn try_leave_family(
&self,
storage: &dyn Storage,
node_identity_signature: String,
family_head: IdentityKeyRef,
family_head: FamilyHead,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::LeaveFamilyOnBehalf {
member_address: self.owner_address().to_string(),
node_identity_signature,
family_head: family_head.to_string(),
family_head,
};
let msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?;
@@ -60,12 +56,10 @@ impl NodeFamilies for Account {
fn try_head_kick_member(
&self,
storage: &dyn Storage,
signature: String,
member: IdentityKeyRef<'_>,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::KickFamilyMemberOnBehalf {
head_address: self.owner_address().to_string(),
signature,
member: member.to_string(),
};
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "explorer-api"
version = "1.1.12"
version = "1.1.14"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1
View File
@@ -42,6 +42,7 @@ pub(crate) struct PrettyDetailedMixNodeBond {
pub operating_cost: Coin,
pub profit_margin_percent: Percent,
pub family_id: Option<u16>,
pub blacklisted: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
+1
View File
@@ -162,6 +162,7 @@ impl ThreadsafeMixNodesCache {
operating_cost: rewarding_info.cost_params.interval_operating_cost.clone(),
profit_margin_percent: rewarding_info.cost_params.profit_margin_percent,
family_id,
blacklisted: node.blacklisted,
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ impl ExplorerApiTasks {
async fn retrieve_all_mixnodes(&self) -> Vec<MixNodeBondAnnotated> {
info!("About to retrieve all mixnode bonds...");
self.retrieve_mixnodes(validator_client::Client::get_cached_mixnodes_detailed)
self.retrieve_mixnodes(validator_client::Client::get_cached_mixnodes_detailed_unfiltered)
.await
}

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