Compare commits

..

10 Commits

Author SHA1 Message Date
Georgio Nicolas 5e2135812e update benchmarking setup 2025-10-29 14:55:12 +01:00
Georgio Nicolas 48304afd3a Update manifests 2025-10-29 08:08:21 +01:00
Georgio Nicolas a66d4002a1 Add KKT 2025-10-29 08:08:03 +01:00
Georgio Nicolas 40e07e2a0c add xwing support 2025-10-07 19:11:47 +02:00
Georgio Nicolas d5194722cf Switch back to cryspen repo 2025-08-19 13:56:00 +02:00
Georgio Nicolas 7d8a31339e temporary support for modified v1 api 2025-08-08 17:23:56 +02:00
Georgio Nicolas 79bf97ceaa Tests pass 2025-08-08 12:50:11 +02:00
Georgio Nicolas 84fcf81852 update psq implementation 2025-08-08 11:07:35 +02:00
Georgio Nicolas 481ad79376 Integrated but not tested 2025-08-08 11:07:07 +02:00
Georgio Nicolas 1a9995535f temp changes 2025-07-07 16:44:45 +02:00
636 changed files with 103495 additions and 12742 deletions
@@ -57,7 +57,6 @@ jobs:
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_ecash.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_pool_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_performance_contract.wasm $OUTPUT_DIR
- name: Deploy branch to CI www
continue-on-error: true
+1 -5
View File
@@ -19,11 +19,7 @@ jobs:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
strategy:
fail-fast: false
matrix:
include:
- os: arc-ubuntu-22.04
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.os }}
runs-on: arc-ubuntu-22.04
outputs:
release_id: ${{ steps.create-release.outputs.id }}
Generated
+370 -61
View File
@@ -1298,6 +1298,16 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "classic-mceliece-rust"
version = "3.2.0"
source = "git+https://github.com/georgio/classic-mceliece-rust#d2367683c06330b694047132d7bd38924ac97da8"
dependencies = [
"rand 0.9.2",
"sha3",
"zeroize",
]
[[package]]
name = "colorchoice"
version = "1.0.3"
@@ -1457,6 +1467,16 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "core-models"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"hax-lib",
"pastey",
"rand 0.9.2",
]
[[package]]
name = "cosmos-sdk-proto"
version = "0.26.1"
@@ -1878,6 +1898,7 @@ dependencies = [
"curve25519-dalek-derive",
"digest 0.10.7",
"fiat-crypto",
"rand_core 0.6.4",
"rustc_version 0.4.1",
"serde",
"subtle 2.6.1",
@@ -1923,26 +1944,6 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "cw-multi-test"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "533b31c94b9e10e77e2468a2b1559aa506505d18c4e52eb64cbfc624ca876ad2"
dependencies = [
"anyhow",
"bech32",
"cosmwasm-schema",
"cosmwasm-std",
"cw-storage-plus",
"cw-utils",
"itertools 0.14.0",
"prost 0.13.5",
"schemars",
"serde",
"sha2 0.10.9",
"thiserror 2.0.12",
]
[[package]]
name = "cw-storage-plus"
version = "2.0.0"
@@ -3161,6 +3162,43 @@ dependencies = [
"hashbrown 0.15.2",
]
[[package]]
name = "hax-lib"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d9ba66d1739c68e0219b2b2238b5c4145f491ebf181b9c6ab561a19352ae86"
dependencies = [
"hax-lib-macros",
"num-bigint",
"num-traits",
]
[[package]]
name = "hax-lib-macros"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ba777a231a58d1bce1d68313fa6b6afcc7966adef23d60f45b8a2b9b688bf1"
dependencies = [
"hax-lib-macros-types",
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.98",
]
[[package]]
name = "hax-lib-macros-types"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "867e19177d7425140b417cd27c2e05320e727ee682e98368f88b7194e80ad515"
dependencies = [
"proc-macro2",
"quote",
"serde",
"serde_json",
"uuid",
]
[[package]]
name = "hdrhistogram"
version = "7.5.4"
@@ -3270,7 +3308,7 @@ dependencies = [
"idna",
"ipnet",
"once_cell",
"rand 0.9.0",
"rand 0.9.2",
"ring",
"rustls 0.23.25",
"thiserror 2.0.12",
@@ -3295,7 +3333,7 @@ dependencies = [
"moka",
"once_cell",
"parking_lot",
"rand 0.9.0",
"rand 0.9.2",
"resolv-conf",
"rustls 0.23.25",
"smallvec",
@@ -4092,6 +4130,15 @@ dependencies = [
"signature",
]
[[package]]
name = "keccak"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
dependencies = [
"cpufeatures",
]
[[package]]
name = "keystream"
version = "1.0.0"
@@ -4170,6 +4217,213 @@ version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]]
name = "libcrux-chacha20poly1305"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
"libcrux-poly1305",
"libcrux-secrets",
"libcrux-traits",
]
[[package]]
name = "libcrux-curve25519"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
"libcrux-secrets",
"libcrux-traits",
]
[[package]]
name = "libcrux-ecdh"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-curve25519",
"libcrux-p256",
"rand 0.9.2",
"tls_codec",
]
[[package]]
name = "libcrux-ed25519"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
"libcrux-sha2",
"rand_core 0.9.1",
"tls_codec",
]
[[package]]
name = "libcrux-hacl-rs"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-macros",
]
[[package]]
name = "libcrux-hkdf"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-hacl-rs",
"libcrux-hmac",
"libcrux-secrets",
]
[[package]]
name = "libcrux-hmac"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
"libcrux-sha2",
]
[[package]]
name = "libcrux-intrinsics"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"core-models",
"hax-lib",
]
[[package]]
name = "libcrux-kem"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-curve25519",
"libcrux-ecdh",
"libcrux-ml-kem",
"libcrux-p256",
"libcrux-sha3",
"libcrux-traits",
"rand 0.9.2",
"tls_codec",
]
[[package]]
name = "libcrux-macros"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"quote",
"syn 2.0.98",
]
[[package]]
name = "libcrux-ml-kem"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"hax-lib",
"libcrux-intrinsics",
"libcrux-platform",
"libcrux-secrets",
"libcrux-sha3",
"libcrux-traits",
"rand 0.9.2",
"tls_codec",
]
[[package]]
name = "libcrux-p256"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
"libcrux-secrets",
"libcrux-sha2",
"libcrux-traits",
]
[[package]]
name = "libcrux-platform"
version = "0.0.2"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libc",
]
[[package]]
name = "libcrux-poly1305"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
]
[[package]]
name = "libcrux-psq"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-chacha20poly1305",
"libcrux-ecdh",
"libcrux-ed25519",
"libcrux-hkdf",
"libcrux-hmac",
"libcrux-kem",
"libcrux-ml-kem",
"libcrux-sha2",
"libcrux-traits",
"rand 0.9.2",
"tls_codec",
]
[[package]]
name = "libcrux-secrets"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"hax-lib",
]
[[package]]
name = "libcrux-sha2"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
"libcrux-traits",
]
[[package]]
name = "libcrux-sha3"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"hax-lib",
"libcrux-intrinsics",
"libcrux-platform",
"libcrux-traits",
]
[[package]]
name = "libcrux-traits"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
dependencies = [
"libcrux-secrets",
"rand 0.9.2",
]
[[package]]
name = "libm"
version = "0.2.11"
@@ -5471,7 +5725,6 @@ dependencies = [
name = "nym-contracts-common"
version = "0.5.0"
dependencies = [
"anyhow",
"bs58",
"cosmwasm-schema",
"cosmwasm-std",
@@ -5499,19 +5752,6 @@ dependencies = [
"vergen",
]
[[package]]
name = "nym-contracts-common-testing"
version = "0.1.0"
dependencies = [
"anyhow",
"cosmwasm-std",
"cw-multi-test",
"cw-storage-plus",
"rand 0.8.5",
"rand_chacha 0.3.1",
"serde",
]
[[package]]
name = "nym-cpp-ffi"
version = "0.1.2"
@@ -6135,7 +6375,6 @@ dependencies = [
"futures",
"mime",
"serde",
"serde_json",
"serde_yaml",
"subtle 2.6.1",
"time",
@@ -6258,6 +6497,35 @@ dependencies = [
"url",
]
[[package]]
name = "nym-kkt"
version = "0.1.0"
dependencies = [
"aead",
"arc-swap",
"blake3",
"bytes",
"classic-mceliece-rust",
"criterion",
"curve25519-dalek",
"futures",
"libcrux-ecdh",
"libcrux-kem",
"libcrux-ml-kem",
"libcrux-psq",
"libcrux-sha3",
"libcrux-traits",
"nym-crypto 0.4.0",
"pin-project",
"rand 0.9.2",
"strum 0.26.3",
"thiserror 2.0.12",
"tokio",
"tokio-util",
"tracing",
"zeroize",
]
[[package]]
name = "nym-ledger"
version = "0.1.0"
@@ -6323,6 +6591,7 @@ dependencies = [
"schemars",
"semver 1.0.26",
"serde",
"serde-json-wasm",
"serde_repr",
"thiserror 2.0.12",
"time",
@@ -6685,7 +6954,7 @@ dependencies = [
[[package]]
name = "nym-node-status-api"
version = "3.1.1"
version = "3.1.0"
dependencies = [
"ammonia",
"anyhow",
@@ -6815,9 +7084,14 @@ dependencies = [
"arc-swap",
"bytes",
"futures",
"libcrux-ed25519",
"libcrux-kem",
"libcrux-psq",
"nym-crypto 0.4.0",
"nym-noise-keys",
"nympsq",
"pin-project",
"rand 0.9.2",
"rand_chacha 0.3.1",
"sha2 0.10.9",
"snow",
@@ -6927,19 +7201,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "nym-performance-contract-common"
version = "0.1.0"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
"nym-contracts-common 0.5.0",
"schemars",
"serde",
"thiserror 2.0.12",
]
[[package]]
name = "nym-pool-contract-common"
version = "0.1.0"
@@ -6999,7 +7260,6 @@ dependencies = [
"tap",
"tempfile",
"thiserror 2.0.12",
"time",
"tokio",
"tokio-stream",
"tokio-util",
@@ -7487,7 +7747,7 @@ dependencies = [
[[package]]
name = "nym-statistics-api"
version = "0.1.4"
version = "0.1.3"
dependencies = [
"anyhow",
"axum 0.7.9",
@@ -7760,7 +8020,6 @@ dependencies = [
"nym-mixnet-contract-common 0.6.0",
"nym-multisig-contract-common 0.1.0",
"nym-network-defaults 0.1.0",
"nym-performance-contract-common",
"nym-serde-helpers 0.1.0",
"nym-vesting-contract-common 0.7.0",
"prost 0.13.5",
@@ -8017,6 +8276,21 @@ dependencies = [
"x25519-dalek",
]
[[package]]
name = "nympsq"
version = "0.1.0"
dependencies = [
"criterion",
"libcrux-ed25519",
"libcrux-kem",
"libcrux-psq",
"libcrux-traits",
"nym-crypto 0.4.0",
"rand 0.9.2",
"thiserror 2.0.12",
"tls_codec",
]
[[package]]
name = "nymvisor"
version = "0.1.22"
@@ -8335,6 +8609,12 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pastey"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a8cb46bdc156b1c90460339ae6bfd45ba0394e5effbaa640badb4987fdc261"
[[package]]
name = "peg"
version = "0.8.4"
@@ -8655,7 +8935,7 @@ dependencies = [
"hmac",
"md-5",
"memchr",
"rand 0.9.0",
"rand 0.9.2",
"sha2 0.10.9",
"stringprep",
]
@@ -8906,7 +9186,7 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
dependencies = [
"bytes",
"getrandom 0.3.1",
"rand 0.9.0",
"rand 0.9.2",
"ring",
"rustc-hash",
"rustls 0.23.25",
@@ -8960,13 +9240,12 @@ dependencies = [
[[package]]
name = "rand"
version = "0.9.0"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.1",
"zerocopy 0.8.20",
]
[[package]]
@@ -9998,6 +10277,16 @@ dependencies = [
"digest 0.10.7",
]
[[package]]
name = "sha3"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
dependencies = [
"digest 0.10.7",
"keccak",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@@ -10806,7 +11095,6 @@ dependencies = [
"nym-mixnet-contract-common 0.6.0",
"nym-multisig-contract-common 0.1.0",
"nym-pemstore 0.3.0",
"nym-performance-contract-common",
"nym-validator-client 0.1.0",
"nym-vesting-contract-common 0.7.0",
"rand 0.8.5",
@@ -10973,6 +11261,27 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tls_codec"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b"
dependencies = [
"tls_codec_derive",
"zeroize",
]
[[package]]
name = "tls_codec_derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]
[[package]]
name = "tokio"
version = "1.45.1"
@@ -11032,7 +11341,7 @@ dependencies = [
"pin-project-lite",
"postgres-protocol",
"postgres-types",
"rand 0.9.0",
"rand 0.9.2",
"socket2",
"tokio",
"tokio-util",
+7 -3
View File
@@ -34,12 +34,11 @@ members = [
"common/config",
"common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/contracts-common-testing",
"common/cosmwasm-smart-contracts/easy_addr",
"common/cosmwasm-smart-contracts/ecash-contract",
"common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/nym-performance-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/nym-pool-contract",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/credential-storage",
@@ -67,8 +66,10 @@ members = [
"common/nym-id",
"common/nym-metrics",
"common/nym_offline_compact_ecash",
"common/nymkkt",
"common/nymnoise",
"common/nymnoise/keys",
"common/nympsq",
"common/nymsphinx",
"common/nymsphinx/acknowledgements",
"common/nymsphinx/addressing",
@@ -137,6 +138,7 @@ members = [
"tools/internal/testnet-manager",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/internal/validator-status-check",
"tools/nym-cli",
"tools/nym-id-cli",
@@ -370,6 +372,9 @@ subtle = "2.5.0"
# cosmwasm-related
cosmwasm-schema = "=2.2.2"
cosmwasm-std = "=2.2.2"
# use 1.0.1 as that's the version used by cosmwasm-std 2.2.1
# (and ideally we don't want to pull the same dependency twice)
serde-json-wasm = "=1.0.1"
# same version as used by cosmwasm
cw-utils = "=2.0.0"
cw-storage-plus = "=2.0.0"
@@ -377,7 +382,6 @@ cw2 = { version = "=2.0.0" }
cw3 = { version = "=2.0.0" }
cw4 = { version = "=2.0.0" }
cw-controllers = { version = "=2.0.0" }
cw-multi-test = "=2.3.2"
# cosmrs-related
bip32 = { version = "0.5.3", default-features = false }
+1 -1
View File
@@ -133,7 +133,7 @@ clippy: sdk-wasm-lint
# Build contracts ready for deploy
# -----------------------------------------------------------------------------
CONTRACTS=vesting_contract mixnet_contract nym_ecash cw3_flex_multisig cw4_group nym_coconut_dkg nym_pool_contract nym_performance_contract
CONTRACTS=vesting_contract mixnet_contract nym_ecash cw3_flex_multisig cw4_group nym_coconut_dkg nym_pool_contract
CONTRACTS_WASM=$(addsuffix .wasm, $(CONTRACTS))
CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release
+1 -1
View File
@@ -318,7 +318,7 @@ impl Handler {
async fn handle_text_message(&mut self, msg: String) -> Option<WsMessage> {
debug!("Handling text message request");
trace!("Content: {msg:?}");
trace!("Content: {:?}", msg);
self.received_response_type = ReceivedResponseType::Text;
let client_request = ClientRequest::try_from_text(msg);
+2 -2
View File
@@ -68,9 +68,9 @@ impl Listener {
new_conn = tcp_listener.accept() => {
match new_conn {
Ok((mut socket, remote_addr)) => {
debug!("Received connection from {remote_addr:?}");
debug!("Received connection from {:?}", remote_addr);
if self.state.is_connected() {
warn!("Tried to open a duplicate websocket connection. The request came from {remote_addr}");
warn!("Tried to open a duplicate websocket connection. The request came from {}", remote_addr);
// if we've already got a connection, don't allow another one
// while we only ever want to accept a single connection, we don't want
// to leave clients hanging (and also allow for reconnection if it somehow
+1 -1
View File
@@ -137,7 +137,7 @@ impl AsyncFileWatcher {
log::error!("the file watcher receiver has been dropped!");
}
} else {
log::debug!("will not propagate information about {event:?}");
log::debug!("will not propagate information about {:?}", event);
}
}
Err(err) => {
+1 -1
View File
@@ -11,7 +11,7 @@ impl std::fmt::Display for BandwidthStatusMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BandwidthStatusMessage::RemainingBandwidth(b) => {
write!(f, "remaining bandwidth: {b}")
write!(f, "remaining bandwidth: {}", b)
}
BandwidthStatusMessage::NoBandwidth => write!(f, "no bandwidth left"),
}
@@ -418,9 +418,6 @@ pub struct Traffic {
/// will be routed as usual, to the entry gateway, through three mix nodes, egressing
/// through the exit gateway. If mix hops are disabled, traffic will be routed directly
/// from the entry gateway to the exit gateway, bypassing the mix nodes.
///
/// This overrides the `use_legacy_sphinx_format` setting as reduced mix hops
/// requires use of the updated SURB packet format.
pub disable_mix_hops: bool,
}
@@ -146,7 +146,7 @@ impl MixTrafficController {
Some(client_request) => {
match self.gateway_transceiver.send_client_request(client_request).await {
Ok(_) => (),
Err(e) => error!("Failed to send client request: {e}"),
Err(e) => error!("Failed to send client request: {}", e),
};
},
None => {
@@ -65,7 +65,7 @@ impl AcknowledgementListener {
return;
}
trace!("Received {frag_id} from the mix network");
trace!("Received {} from the mix network", frag_id);
self.stats_tx
.report(PacketStatisticsEvent::RealAckReceived(ack_content.len()).into());
if let Err(err) = self
@@ -126,7 +126,7 @@ impl ActionController {
fn handle_insert(&mut self, pending_acks: Vec<PendingAcknowledgement>) {
for pending_ack in pending_acks {
let frag_id = pending_ack.message_chunk.fragment_identifier();
trace!("{frag_id} is inserted");
trace!("{} is inserted", frag_id);
if self
.pending_acks_data
@@ -161,16 +161,22 @@ impl ActionController {
let new_queue_key = self.pending_acks_timers.insert(frag_id, timeout);
*queue_key = Some(new_queue_key)
} else {
debug!("Tried to START TIMER on pending ack that is already gone! - {frag_id}");
debug!(
"Tried to START TIMER on pending ack that is already gone! - {}",
frag_id
);
}
}
fn handle_remove(&mut self, frag_id: FragmentIdentifier) {
trace!("{frag_id} is getting removed");
trace!("{} is getting removed", frag_id);
match self.pending_acks_data.remove(&frag_id) {
None => {
debug!("Tried to REMOVE pending ack that is already gone! - {frag_id}");
debug!(
"Tried to REMOVE pending ack that is already gone! - {}",
frag_id
);
}
Some((_, queue_key)) => {
if let Some(queue_key) = queue_key {
@@ -182,7 +188,10 @@ impl ActionController {
} else {
// I'm not 100% sure if having a `None` key is even possible here
// (REMOVE would have to be called before START TIMER),
debug!("Tried to REMOVE pending ack without TIMER active - {frag_id}");
debug!(
"Tried to REMOVE pending ack without TIMER active - {}",
frag_id
);
}
}
}
@@ -191,7 +200,7 @@ impl ActionController {
// initiated basically as a first step of retransmission. At first data has its delay updated
// (as new sphinx packet was created with new expected delivery time)
fn handle_update_pending_ack(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
trace!("{frag_id} is updating its delay");
trace!("{} is updating its delay", frag_id);
// TODO: is it possible to solve this without either locking or temporarily removing the value?
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) {
// this Action is triggered by `RetransmissionRequestListener` (for 'normal' packets)
@@ -204,7 +213,10 @@ impl ActionController {
self.pending_acks_data
.insert(frag_id, (Arc::new(inner_data), queue_key));
} else {
debug!("Tried to UPDATE TIMER on pending ack that is already gone! - {frag_id}");
debug!(
"Tried to UPDATE TIMER on pending ack that is already gone! - {}",
frag_id
);
}
}
@@ -105,9 +105,6 @@ pub(crate) struct Config {
/// will be routed as usual, to the entry gateway, through three mix nodes, egressing
/// through the exit gateway. If mix hops are disabled, traffic will be routed directly
/// from the entry gateway to the exit gateway, bypassing the mix nodes.
///
/// This overrides the `use_legacy_sphinx_format` setting as reduced mix hops
/// requires use of the updated SURB packet format.
disable_mix_hops: bool,
/// Average delay a data packet is going to get delay at a single mixnode.
@@ -162,12 +159,8 @@ impl Config {
}
/// Configure whether messages senders using this config should use mix hops or not when sending messages.
///
/// This overrides the `use_legacy_sphinx_format` setting as disabled mix hops
/// requires use of the updated SURB packet format.
pub fn disable_mix_hops(mut self, disable_mix_hops: bool) -> Self {
self.disable_mix_hops = disable_mix_hops;
self.use_legacy_sphinx_format = false;
self
}
}
@@ -202,7 +202,7 @@ where
// well technically the message was not sent just yet, but now it's up to internal
// queues and client load rather than the required delay. So realistically we can treat
// whatever is about to happen as negligible additional delay.
trace!("{frag_id} is about to get sent to the mixnet");
trace!("{} is about to get sent to the mixnet", frag_id);
if let Err(err) = self.sent_notifier.unbounded_send(frag_id) {
error!("Failed to notify about sent message: {err}");
}
@@ -164,11 +164,11 @@ impl SendingDelayController {
self.current_multiplier()
);
if self.current_multiplier() > 0 {
log::debug!("{status_str}");
log::debug!("{}", status_str);
} else if self.current_multiplier() > 1 {
log::info!("{status_str}");
log::info!("{}", status_str);
} else if self.current_multiplier() > 2 {
log::warn!("{status_str}");
log::warn!("{}", status_str);
}
self.time_when_logged_about_elevated_multiplier = now;
}
@@ -221,7 +221,10 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
let stored_messages = std::mem::take(&mut guard.messages);
if !stored_messages.is_empty() {
if let Err(err) = sender.unbounded_send(stored_messages) {
error!("The sender channel we just received is already invalidated - {err:?}");
error!(
"The sender channel we just received is already invalidated - {:?}",
err
);
// put the values back to the buffer
// the returned error has two fields: err: SendError and val: T,
// where val is the value that was failed to get sent;
@@ -217,14 +217,14 @@ where
.surbs_storage_ref()
.contains_surbs_for(&recipient_tag)
{
warn!("received reply request for {recipient_tag:?} but we don't have any surbs stored for that recipient!");
warn!("received reply request for {:?} but we don't have any surbs stored for that recipient!", recipient_tag);
return;
}
trace!("handling reply to {recipient_tag:?}");
trace!("handling reply to {:?}", recipient_tag);
let mut fragments = self.message_handler.split_reply_message(data);
let total_size = fragments.len();
trace!("This reply requires {total_size:?} SURBs");
trace!("This reply requires {:?} SURBs", total_size);
let available_surbs = self
.full_reply_storage
@@ -327,7 +327,10 @@ where
.await
{
let err = err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
warn!("failed to request additional surbs from {target:?} - {err}");
warn!(
"failed to request additional surbs from {:?} - {err}",
target
);
return Err(err);
} else {
self.full_reply_storage
@@ -406,7 +409,10 @@ where
err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
self.re_insert_pending_retransmission(&target, to_take);
warn!("failed to clear pending retransmission queue for {target:?} - {err}");
warn!(
"failed to clear pending retransmission queue for {:?} - {err}",
target
);
return;
}
};
@@ -483,7 +489,7 @@ where
let err =
err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
self.re_insert_pending_replies(&target, to_send);
warn!("failed to clear pending queue for {target:?} - {err}");
warn!("failed to clear pending queue for {:?} - {err}", target);
}
} else {
trace!("the pending queue is empty");
@@ -810,7 +816,7 @@ where
if diff > max_drop_wait {
to_remove.push(*pending_reply_target)
} else {
debug!("We haven't received any surbs in {diff:?} from {pending_reply_target}. Going to explicitly ask for more");
debug!("We haven't received any surbs in {:?} from {pending_reply_target}. Going to explicitly ask for more", diff);
to_request.push(*pending_reply_target);
}
}
@@ -93,7 +93,7 @@ impl StatisticsControl {
None,
);
if let Err(err) = self.report_tx.send(report_message).await {
log::error!("Failed to report client stats: {err:?}");
log::error!("Failed to report client stats: {:?}", err);
} else {
self.stats.reset();
}
@@ -211,7 +211,7 @@ impl<T> TransmissionBuffer<T> {
};
let msg = self.pop_front_from_lane(&lane)?;
log::trace!("picking to send from lane: {lane:?}");
log::trace!("picking to send from lane: {:?}", lane);
Some((lane, msg))
}
+3 -3
View File
@@ -110,7 +110,7 @@ pub async fn gateways_for_init<R: Rng>(
let gateways = client.get_all_basic_entry_assigned_nodes_v2().await?.nodes;
info!("nym api reports {} gateways", gateways.len());
log::trace!("Gateways: {gateways:#?}");
log::trace!("Gateways: {:#?}", gateways);
// filter out gateways below minimum performance and ones that could operate as a mixnode
// (we don't want instability)
@@ -121,7 +121,7 @@ pub async fn gateways_for_init<R: Rng>(
.filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<_>>();
log::debug!("After checking validity: {}", valid_gateways.len());
log::trace!("Valid gateways: {valid_gateways:#?}");
log::trace!("Valid gateways: {:#?}", valid_gateways);
log::info!(
"and {} after validity and performance filtering",
@@ -286,7 +286,7 @@ pub(super) fn get_specified_gateway(
gateways: &[RoutingNode],
must_use_tls: bool,
) -> Result<RoutingNode, ClientCoreError> {
log::debug!("Requesting specified gateway: {gateway_identity}");
log::debug!("Requesting specified gateway: {}", gateway_identity);
let user_gateway = ed25519::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
@@ -133,7 +133,7 @@ impl ManagedConnection {
debug!("Managed to establish connection to {}", self.address);
let noise_stream =
match upgrade_noise_initiator(stream, &self.noise_config).await {
match upgrade_noise_initiator(stream, &self.noise_config, None).await {
Ok(noise_stream) => noise_stream,
Err(err) => {
error!("Failed to perform Noise handshake with {address} - {err}");
@@ -19,7 +19,6 @@ nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-c
nym-ecash-contract-common = { path = "../../cosmwasm-smart-contracts/ecash-contract" }
nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
nym-performance-contract-common = { path = "../../cosmwasm-smart-contracts/nym-performance-contract" }
nym-serde-helpers = { path = "../../serde-helpers", features = ["hex", "base64"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
@@ -13,7 +13,6 @@ pub mod ecash_query_client;
pub mod group_query_client;
pub mod mixnet_query_client;
pub mod multisig_query_client;
pub mod performance_query_client;
pub mod vesting_query_client;
// signing clients
@@ -22,7 +21,6 @@ pub mod ecash_signing_client;
pub mod group_signing_client;
pub mod mixnet_signing_client;
pub mod multisig_signing_client;
pub mod performance_signing_client;
pub mod vesting_signing_client;
// re-export query traits
@@ -31,7 +29,6 @@ pub use ecash_query_client::{EcashQueryClient, PagedEcashQueryClient};
pub use group_query_client::{GroupQueryClient, PagedGroupQueryClient};
pub use mixnet_query_client::{MixnetQueryClient, PagedMixnetQueryClient};
pub use multisig_query_client::{MultisigQueryClient, PagedMultisigQueryClient};
pub use performance_query_client::{PagedPerformanceQueryClient, PerformanceQueryClient};
pub use vesting_query_client::{PagedVestingQueryClient, VestingQueryClient};
// re-export signing traits
@@ -40,7 +37,6 @@ pub use ecash_signing_client::EcashSigningClient;
pub use group_signing_client::GroupSigningClient;
pub use mixnet_signing_client::MixnetSigningClient;
pub use multisig_signing_client::MultisigSigningClient;
pub use performance_signing_client::PerformanceSigningClient;
pub use vesting_signing_client::VestingSigningClient;
// helper for providing blanket implementation for query clients
@@ -48,7 +44,6 @@ pub trait NymContractsProvider {
// main
fn mixnet_contract_address(&self) -> Option<&AccountId>;
fn vesting_contract_address(&self) -> Option<&AccountId>;
fn performance_contract_address(&self) -> Option<&AccountId>;
// coconut-related
fn ecash_contract_address(&self) -> Option<&AccountId>;
@@ -61,7 +56,6 @@ pub trait NymContractsProvider {
pub struct TypedNymContracts {
pub mixnet_contract_address: Option<AccountId>,
pub vesting_contract_address: Option<AccountId>,
pub performance_contract_address: Option<AccountId>,
pub ecash_contract_address: Option<AccountId>,
pub group_contract_address: Option<AccountId>,
@@ -82,10 +76,6 @@ impl TryFrom<NymContracts> for TypedNymContracts {
.vesting_contract_address
.map(|addr| addr.parse())
.transpose()?,
performance_contract_address: value
.performance_contract_address
.map(|addr| addr.parse())
.transpose()?,
ecash_contract_address: value
.ecash_contract_address
.map(|addr| addr.parse())
@@ -1,271 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::collect_paged;
use crate::nyxd::contract_traits::NymContractsProvider;
use crate::nyxd::error::NyxdError;
use crate::nyxd::CosmWasmClient;
use async_trait::async_trait;
use cosmrs::AccountId;
use serde::Deserialize;
pub use nym_performance_contract_common::{
msg::QueryMsg as PerformanceQueryMsg, types::NetworkMonitorResponse, EpochId,
EpochMeasurementsPagedResponse, EpochNodePerformance, EpochPerformancePagedResponse,
FullHistoricalPerformancePagedResponse, HistoricalPerformance, LastSubmission,
NetworkMonitorInformation, NetworkMonitorsPagedResponse, NodeId, NodeMeasurement,
NodeMeasurementsResponse, NodePerformance, NodePerformancePagedResponse,
NodePerformanceResponse, RetiredNetworkMonitor, RetiredNetworkMonitorsPagedResponse,
};
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait PerformanceQueryClient {
async fn query_performance_contract<T>(
&self,
query: PerformanceQueryMsg,
) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn admin(&self) -> Result<cw_controllers::AdminResponse, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::Admin {})
.await
}
async fn get_node_performance(
&self,
epoch_id: EpochId,
node_id: NodeId,
) -> Result<NodePerformanceResponse, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::NodePerformance { epoch_id, node_id })
.await
}
async fn get_node_performance_paged(
&self,
node_id: NodeId,
start_after: Option<EpochId>,
limit: Option<u32>,
) -> Result<NodePerformancePagedResponse, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::NodePerformancePaged {
node_id,
start_after,
limit,
})
.await
}
async fn get_node_measurements(
&self,
epoch_id: EpochId,
node_id: NodeId,
) -> Result<NodeMeasurementsResponse, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::NodeMeasurements { epoch_id, node_id })
.await
}
async fn get_epoch_measurements_paged(
&self,
epoch_id: EpochId,
start_after: Option<NodeId>,
limit: Option<u32>,
) -> Result<EpochMeasurementsPagedResponse, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::EpochMeasurementsPaged {
epoch_id,
start_after,
limit,
})
.await
}
async fn get_epoch_performance_paged(
&self,
epoch_id: EpochId,
start_after: Option<NodeId>,
limit: Option<u32>,
) -> Result<EpochPerformancePagedResponse, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::EpochPerformancePaged {
epoch_id,
start_after,
limit,
})
.await
}
async fn get_full_historical_performance_paged(
&self,
start_after: Option<(EpochId, NodeId)>,
limit: Option<u32>,
) -> Result<FullHistoricalPerformancePagedResponse, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::FullHistoricalPerformancePaged {
start_after,
limit,
})
.await
}
async fn get_network_monitor(
&self,
address: &AccountId,
) -> Result<NetworkMonitorResponse, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::NetworkMonitor {
address: address.to_string(),
})
.await
}
async fn get_network_monitors_paged(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<NetworkMonitorsPagedResponse, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::NetworkMonitorsPaged {
start_after,
limit,
})
.await
}
async fn get_retired_network_monitors_paged(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<RetiredNetworkMonitorsPagedResponse, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::RetiredNetworkMonitorsPaged {
start_after,
limit,
})
.await
}
async fn get_last_submission(&self) -> Result<LastSubmission, NyxdError> {
self.query_performance_contract(PerformanceQueryMsg::LastSubmittedMeasurement {})
.await
}
}
// extension trait to the query client to deal with the paged queries
// (it didn't feel appropriate to combine it with the existing trait
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait PagedPerformanceQueryClient: PerformanceQueryClient {
async fn get_all_node_performance(
&self,
node_id: NodeId,
) -> Result<Vec<EpochNodePerformance>, NyxdError> {
collect_paged!(self, get_node_performance_paged, performance, node_id)
}
async fn get_all_epoch_measurements(
&self,
node_id: NodeId,
) -> Result<Vec<NodeMeasurement>, NyxdError> {
collect_paged!(self, get_epoch_measurements_paged, measurements, node_id)
}
async fn get_all_epoch_performance(
&self,
epoch_id: EpochId,
) -> Result<Vec<NodePerformance>, NyxdError> {
collect_paged!(self, get_epoch_performance_paged, performance, epoch_id)
}
async fn get_all_full_historical_performance(
&self,
) -> Result<Vec<HistoricalPerformance>, NyxdError> {
collect_paged!(self, get_full_historical_performance_paged, performance)
}
async fn get_all_network_monitors(&self) -> Result<Vec<NetworkMonitorInformation>, NyxdError> {
collect_paged!(self, get_network_monitors_paged, info)
}
async fn get_all_retired_network_monitors(
&self,
) -> Result<Vec<RetiredNetworkMonitor>, NyxdError> {
collect_paged!(self, get_retired_network_monitors_paged, info)
}
}
#[async_trait]
impl<T> PagedPerformanceQueryClient for T where T: PerformanceQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> PerformanceQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_performance_contract<T>(
&self,
query: PerformanceQueryMsg,
) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>,
{
let performance_contract_address = &self
.performance_contract_address()
.ok_or_else(|| NyxdError::unavailable_contract_address("performance contract"))?;
self.query_contract_smart(performance_contract_address, &query)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::IgnoreValue;
use nym_performance_contract_common::QueryMsg;
// it's enough that this compiles and clippy is happy about it
#[allow(dead_code)]
fn all_query_variants_are_covered<C: PerformanceQueryClient + Send + Sync>(
client: C,
msg: PerformanceQueryMsg,
) {
match msg {
PerformanceQueryMsg::Admin {} => client.admin().ignore(),
PerformanceQueryMsg::NodePerformance { epoch_id, node_id } => {
client.get_node_performance(epoch_id, node_id).ignore()
}
PerformanceQueryMsg::NodePerformancePaged {
node_id,
start_after,
limit,
} => client
.get_node_performance_paged(node_id, start_after, limit)
.ignore(),
PerformanceQueryMsg::NodeMeasurements { epoch_id, node_id } => {
client.get_node_measurements(epoch_id, node_id).ignore()
}
PerformanceQueryMsg::EpochMeasurementsPaged {
epoch_id,
start_after,
limit,
} => client
.get_epoch_measurements_paged(epoch_id, start_after, limit)
.ignore(),
PerformanceQueryMsg::EpochPerformancePaged {
epoch_id,
start_after,
limit,
} => client
.get_epoch_performance_paged(epoch_id, start_after, limit)
.ignore(),
PerformanceQueryMsg::FullHistoricalPerformancePaged { start_after, limit } => client
.get_full_historical_performance_paged(start_after, limit)
.ignore(),
PerformanceQueryMsg::NetworkMonitor { address } => client
.get_network_monitor(&address.parse().unwrap())
.ignore(),
PerformanceQueryMsg::NetworkMonitorsPaged { start_after, limit } => client
.get_network_monitors_paged(start_after, limit)
.ignore(),
PerformanceQueryMsg::RetiredNetworkMonitorsPaged { start_after, limit } => client
.get_retired_network_monitors_paged(start_after, limit)
.ignore(),
QueryMsg::LastSubmittedMeasurement {} => client.get_last_submission().ignore(),
};
}
}
@@ -1,217 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::coin::Coin;
use crate::nyxd::contract_traits::NymContractsProvider;
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
use crate::nyxd::cosmwasm_client::ContractResponseData;
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Fee, SigningCosmWasmClient};
use crate::signing::signer::OfflineSigner;
use async_trait::async_trait;
use nym_performance_contract_common::{
EpochId, ExecuteMsg as PerformanceExecuteMsg, NodeId, NodePerformance,
RemoveEpochMeasurementsResponse,
};
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait PerformanceSigningClient {
async fn execute_performance_contract(
&self,
fee: Option<Fee>,
msg: PerformanceExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn update_admin(
&self,
admin: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_performance_contract(
fee,
PerformanceExecuteMsg::UpdateAdmin { admin },
"PerformanceContract::UpdateAdmin".to_string(),
vec![],
)
.await
}
async fn submit_performance(
&self,
epoch: EpochId,
data: NodePerformance,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_performance_contract(
fee,
PerformanceExecuteMsg::Submit { epoch, data },
"PerformanceContract::Submit".to_string(),
vec![],
)
.await
}
async fn batch_submit_performance(
&self,
epoch: EpochId,
data: Vec<NodePerformance>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_performance_contract(
fee,
PerformanceExecuteMsg::BatchSubmit { epoch, data },
"PerformanceContract::BatchSubmit".to_string(),
vec![],
)
.await
}
async fn authorise_network_monitor(
&self,
address: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_performance_contract(
fee,
PerformanceExecuteMsg::AuthoriseNetworkMonitor { address },
"PerformanceContract::AuthoriseNetworkMonitor".to_string(),
vec![],
)
.await
}
async fn retire_network_monitor(
&self,
address: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_performance_contract(
fee,
PerformanceExecuteMsg::RetireNetworkMonitor { address },
"PerformanceContract::RetireNetworkMonitor".to_string(),
vec![],
)
.await
}
async fn remove_node_measurements(
&self,
epoch_id: EpochId,
node_id: NodeId,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_performance_contract(
fee,
PerformanceExecuteMsg::RemoveNodeMeasurements { epoch_id, node_id },
"PerformanceContract::RemoveNodeMeasurements".to_string(),
vec![],
)
.await
}
async fn partial_remove_epoch_measurements(
&self,
epoch_id: EpochId,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_performance_contract(
fee,
PerformanceExecuteMsg::RemoveEpochMeasurements { epoch_id },
"PerformanceContract::RemoveEpochMeasurements".to_string(),
vec![],
)
.await
}
async fn remove_epoch_measurements(
&self,
epoch_id: EpochId,
fee: Option<Fee>,
) -> Result<(), NyxdError> {
loop {
let execute_res = self
.partial_remove_epoch_measurements(epoch_id, fee.clone())
.await?;
let response = execute_res
.parse_singleton_json_contract_response::<RemoveEpochMeasurementsResponse>()?;
if !response.additional_entries_to_remove_remaining {
break;
}
}
Ok(())
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> PerformanceSigningClient for C
where
C: SigningCosmWasmClient + NymContractsProvider + Sync,
NyxdError: From<<Self as OfflineSigner>::Error>,
{
async fn execute_performance_contract(
&self,
fee: Option<Fee>,
msg: PerformanceExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
let performance_contract_address = &self
.performance_contract_address()
.ok_or_else(|| NyxdError::unavailable_contract_address("performance contract"))?;
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let signer_address = &self.signer_addresses()?[0];
self.execute(
signer_address,
performance_contract_address,
&msg,
fee,
memo,
funds,
)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::IgnoreValue;
use nym_performance_contract_common::ExecuteMsg;
// it's enough that this compiles and clippy is happy about it
#[allow(dead_code)]
fn all_execute_variants_are_covered<C: PerformanceSigningClient + Send + Sync>(
client: C,
msg: PerformanceExecuteMsg,
) {
match msg {
PerformanceExecuteMsg::UpdateAdmin { admin } => {
client.update_admin(admin, None).ignore()
}
PerformanceExecuteMsg::Submit { epoch, data } => {
client.submit_performance(epoch, data, None).ignore()
}
PerformanceExecuteMsg::BatchSubmit { epoch, data } => {
client.batch_submit_performance(epoch, data, None).ignore()
}
PerformanceExecuteMsg::AuthoriseNetworkMonitor { address } => {
client.authorise_network_monitor(address, None).ignore()
}
PerformanceExecuteMsg::RetireNetworkMonitor { address } => {
client.retire_network_monitor(address, None).ignore()
}
ExecuteMsg::RemoveNodeMeasurements { epoch_id, node_id } => client
.remove_node_measurements(epoch_id, node_id, None)
.ignore(),
ExecuteMsg::RemoveEpochMeasurements { epoch_id } => client
.partial_remove_epoch_measurements(epoch_id, None)
.ignore(),
};
}
}
@@ -12,8 +12,6 @@ use tendermint_rpc::endpoint::broadcast;
use tracing::error;
pub use cosmrs::abci::MsgResponse;
use cosmwasm_std::from_json;
use serde::de::DeserializeOwned;
pub fn parse_singleton_u32_from_contract_response(b: Vec<u8>) -> Result<u32, NyxdError> {
if b.len() != 4 {
@@ -75,11 +73,6 @@ pub fn parse_msg_responses(data: Bytes) -> Vec<MsgResponse> {
// requires there's a single response message
pub trait ContractResponseData: Sized {
fn parse_singleton_json_contract_response<T: DeserializeOwned>(&self) -> Result<T, NyxdError> {
let b = self.to_singleton_contract_data()?;
from_json(&b).map_err(|err| err.into())
}
fn parse_singleton_u32_contract_data(&self) -> Result<u32, NyxdError> {
let b = self.to_singleton_contract_data()?;
parse_singleton_u32_from_contract_response(b)
@@ -276,10 +276,6 @@ impl<C, S> NymContractsProvider for NyxdClient<C, S> {
self.config.contracts.vesting_contract_address.as_ref()
}
fn performance_contract_address(&self) -> Option<&AccountId> {
self.config.contracts.performance_contract_address.as_ref()
}
fn ecash_contract_address(&self) -> Option<&AccountId> {
self.config.contracts.ecash_contract_address.as_ref()
}
+2 -2
View File
@@ -49,14 +49,14 @@ pub fn show_error<E>(e: E)
where
E: Display,
{
error!("{e}");
error!("{}", e);
}
pub fn show_error_passthrough<E>(e: E) -> E
where
E: Error + Display,
{
error!("{e}");
error!("{}", e);
e
}
@@ -42,7 +42,7 @@ pub async fn query_balance(
.address
.unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic"));
info!("Getting balance for {address}...");
info!("Getting balance for {}...", address);
match client.get_all_balances(&address).await {
Ok(coins) => {
@@ -57,17 +57,17 @@ pub fn get_pubkey_from_mnemonic(address: AccountId, prefix: &str, mnemonic: bip3
println!("{}", account.public_key().to_string());
}
None => {
error!("Could not derive key that matches {address}")
error!("Could not derive key that matches {}", address)
}
},
Err(e) => {
error!("Failed to derive accounts. {e}");
error!("Failed to derive accounts. {}", e);
}
}
}
pub async fn get_pubkey_from_chain(address: AccountId, client: &QueryClient) {
info!("Getting public key for address {address} from chain...");
info!("Getting public key for address {} from chain...", address);
match client.get_account(&address).await {
Ok(Some(account)) => {
if let Ok(base_account) = account.try_get_base_account() {
@@ -37,7 +37,7 @@ pub async fn send_multiple(args: Args, client: &SigningClient) {
let rows = InputFileReader::new(&args.input);
if let Err(e) = rows {
error!("Failed to read input file: {e}");
error!("Failed to read input file: {}", e);
return;
}
let rows = rows.unwrap();
@@ -67,7 +67,7 @@ pub async fn send_multiple(args: Args, client: &SigningClient) {
.prompt();
if let Err(e) = ans {
info!("Aborting, {e}...");
info!("Aborting, {}...", e);
return;
}
if let Ok(false) = ans {
@@ -100,10 +100,13 @@ pub async fn send_multiple(args: Args, client: &SigningClient) {
println!("Transaction hash: {}", &res.hash);
if let Some(output_filename) = args.output {
println!("\nWriting output log to {output_filename}");
println!("\nWriting output log to {}", output_filename);
if let Err(e) = write_output_file(rows, res, &output_filename) {
error!("Failed to write output file {output_filename} with error {e}");
error!(
"Failed to write output file {} with error {}",
output_filename, e
);
}
}
}
@@ -133,7 +136,7 @@ fn write_output_file(
.collect::<Vec<String>>()
.join("\n");
Ok(file.write_all(format!("{data}\n").as_bytes())?)
Ok(file.write_all(format!("{}\n", data).as_bytes())?)
}
#[derive(Debug)]
@@ -168,7 +171,7 @@ impl InputFileReader {
// multiply when a whole token amount, e.g. 50nym (50.123456nym is not allowed, that must be input as 50123456unym)
let (amount, denom) = if !denom.starts_with('u') {
(amount * 1_000_000u128, format!("u{denom}"))
(amount * 1_000_000u128, format!("u{}", denom))
} else {
(amount, denom)
};
@@ -55,6 +55,6 @@ pub async fn execute(args: Args, client: SigningClient) {
.await
{
Ok(res) => info!("SUCCESS ✅\n{}", json!(res)),
Err(e) => error!("FAILURE ❌\n{e}"),
Err(e) => error!("FAILURE ❌\n{}", e),
}
}
@@ -43,7 +43,7 @@ pub struct Args {
pub async fn generate(args: Args) {
info!("Starting to generate vesting contract instantiate msg");
debug!("Received arguments: {args:?}");
debug!("Received arguments: {:?}", args);
let multisig_addr = args.multisig_addr.unwrap_or_else(|| {
let address = std::env::var(nym_network_defaults::var_names::REWARDING_VALIDATOR_ADDRESS)
@@ -97,7 +97,7 @@ pub async fn generate(args: Args) {
key_size: DEFAULT_DEALINGS as u32,
};
debug!("instantiate_msg: {instantiate_msg:?}");
debug!("instantiate_msg: {:?}", instantiate_msg);
let res =
serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json");
@@ -28,7 +28,7 @@ pub struct Args {
pub async fn generate(args: Args) {
info!("Starting to generate vesting contract instantiate msg");
debug!("Received arguments: {args:?}");
debug!("Received arguments: {:?}", args);
let group_addr = args.group_addr.unwrap_or_else(|| {
let address = std::env::var(nym_network_defaults::var_names::GROUP_CONTRACT_ADDRESS)
@@ -51,7 +51,7 @@ pub async fn generate(args: Args) {
deposit_amount: args.deposit_amount,
};
debug!("instantiate_msg: {instantiate_msg:?}");
debug!("instantiate_msg: {:?}", instantiate_msg);
let res =
serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json");
@@ -88,7 +88,7 @@ pub struct Args {
pub async fn generate(args: Args) {
info!("Starting to generate mixnet contract instantiate msg");
debug!("Received arguments: {args:?}");
debug!("Received arguments: {:?}", args);
let initial_rewarding_params = InitialRewardingParams {
initial_reward_pool: Decimal::from_atomics(args.initial_reward_pool, 0)
@@ -114,7 +114,7 @@ pub async fn generate(args: Args) {
},
};
debug!("initial_rewarding_params: {initial_rewarding_params:?}");
debug!("initial_rewarding_params: {:?}", initial_rewarding_params);
let rewarding_validator_address = args.rewarding_validator_address.unwrap_or_else(|| {
let address = std::env::var(nym_network_defaults::var_names::REWARDING_VALIDATOR_ADDRESS)
@@ -160,7 +160,7 @@ pub async fn generate(args: Args) {
key_validity_in_epochs: None,
};
debug!("instantiate_msg: {instantiate_msg:?}");
debug!("instantiate_msg: {:?}", instantiate_msg);
let res =
serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json");
@@ -31,7 +31,7 @@ pub struct Args {
pub async fn generate(args: Args) {
info!("Starting to generate vesting contract instantiate msg");
debug!("Received arguments: {args:?}");
debug!("Received arguments: {:?}", args);
let ecash_contract_address = args.ecash_contract_address.unwrap_or_else(|| {
let address = std::env::var(nym_network_defaults::var_names::ECASH_CONTRACT_ADDRESS)
@@ -60,7 +60,7 @@ pub async fn generate(args: Args) {
coconut_dkg_contract_address: coconut_dkg_contract_address.to_string(),
};
debug!("instantiate_msg: {instantiate_msg:?}");
debug!("instantiate_msg: {:?}", instantiate_msg);
let res =
serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json");
@@ -21,7 +21,7 @@ pub struct Args {
pub async fn generate(args: Args) {
info!("Starting to generate vesting contract instantiate msg");
debug!("Received arguments: {args:?}");
debug!("Received arguments: {:?}", args);
let mixnet_contract_address = args.mixnet_contract_address.unwrap_or_else(|| {
let address = std::env::var(nym_network_defaults::var_names::MIXNET_CONTRACT_ADDRESS)
@@ -39,7 +39,7 @@ pub async fn generate(args: Args) {
mix_denom,
};
debug!("instantiate_msg: {instantiate_msg:?}");
debug!("instantiate_msg: {:?}", instantiate_msg);
let res =
serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json");
@@ -72,7 +72,7 @@ pub async fn init(args: Args, client: SigningClient, network_details: &NymNetwor
.await
.expect("failed to instantiate the contract!");
info!("Init result: {res:?}");
info!("Init result: {:?}", res);
println!("{}", res.contract_address)
}
@@ -47,5 +47,5 @@ pub async fn migrate(args: Args, client: SigningClient) {
.expect("failed to migrate the contract!")
};
info!("Migrate result: {res:?}");
info!("Migrate result: {:?}", res);
}
@@ -31,7 +31,7 @@ pub async fn upload(args: Args, client: SigningClient) {
.await
.expect("failed to upload the contract!");
info!("Upload result: {res:?}");
info!("Upload result: {:?}", res);
println!("{}", res.code_id)
}
@@ -47,5 +47,5 @@ pub async fn delegate_to_mixnode(args: Args, client: SigningClient) {
.await
.expect("failed to delegate to mixnode!");
info!("delegating to mixnode: {res:?}");
info!("delegating to mixnode: {:?}", res);
}
@@ -196,7 +196,7 @@ pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) {
let records = match InputFileReader::new(&args.input) {
Ok(records) => records,
Err(e) => {
println!("Error reading input file: {e}");
println!("Error reading input file: {}", e);
return;
}
};
@@ -262,11 +262,11 @@ pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) {
}
if !undelegation_msgs.is_empty() {
println!("Undelegation records : \n{undelegation_table}\n\n");
println!("Undelegation records : \n{}\n\n", undelegation_table);
}
if !delegation_msgs.is_empty() {
println!("Delegation records : \n{delegation_table}\n\n");
println!("Delegation records : \n{}\n\n", delegation_table);
}
let ans = inquire::Confirm::new("Do you want to continue with the shown operations?")
@@ -275,7 +275,7 @@ pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) {
.prompt();
if let Err(e) = ans {
info!("Aborting, {e}...");
info!("Aborting, {}...", e);
return;
}
@@ -348,7 +348,7 @@ pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) {
if args.output.is_some() {
if let Err(e) = write_to_csv(output_details, args.output) {
info!("Failed to write to CSV, {e}");
info!("Failed to write to CSV, {}", e);
}
}
}
@@ -38,5 +38,5 @@ pub async fn migrate_vested_delegation(args: Args, client: SigningClient) {
.await
.expect("failed to migrate delegation!");
info!("migration result: {res:?}")
info!("migration result: {:?}", res)
}
@@ -40,5 +40,5 @@ pub async fn claim_delegator_reward(args: Args, client: SigningClient) {
.await
.expect("failed to claim delegator-reward");
info!("Claiming delegator reward: {res:?}")
info!("Claiming delegator reward: {:?}", res)
}
@@ -40,5 +40,5 @@ pub async fn vesting_claim_delegator_reward(args: Args, client: SigningClient) {
.await
.expect("failed to claim vesting delegator-reward");
info!("Claiming vesting delegator reward: {res:?}")
info!("Claiming vesting delegator reward: {:?}", res)
}
@@ -40,5 +40,5 @@ pub async fn undelegate_from_mixnode(args: Args, client: SigningClient) {
.await
.expect("failed to remove stake from mixnode!");
info!("removing stake from mixnode: {res:?}")
info!("removing stake from mixnode: {:?}", res)
}
@@ -53,5 +53,5 @@ pub async fn vesting_delegate_to_mixnode(args: Args, client: SigningClient) {
.await
.expect("failed to delegate to mixnode!");
info!("vesting delegating to mixnode: {res:?}");
info!("vesting delegating to mixnode: {:?}", res);
}
@@ -45,5 +45,5 @@ pub async fn vesting_undelegate_from_mixnode(args: Args, client: SigningClient)
.await
.expect("failed to remove stake from vesting account on mixnode!");
info!("removing stake from vesting mixnode: {res:?}")
info!("removing stake from vesting mixnode: {:?}", res)
}
@@ -73,5 +73,5 @@ pub async fn bond_gateway(args: Args, client: SigningClient) {
.await
.expect("failed to bond gateway!");
info!("Bonding result: {res:?}")
info!("Bonding result: {:?}", res)
}
@@ -52,5 +52,5 @@ pub async fn migrate_to_nymnode(args: Args, client: SigningClient) {
.await
.expect("failed to migrate gateway!");
info!("migration result: {res:?}")
info!("migration result: {:?}", res)
}
@@ -56,5 +56,5 @@ pub async fn update_config(args: Args, client: SigningClient) {
.await
.expect("updating gateway config");
info!("gateway config updated: {res:?}")
info!("gateway config updated: {:?}", res)
}
@@ -57,5 +57,5 @@ pub async fn vesting_update_config(args: Args, client: SigningClient) {
.await
.expect("updating vesting gateway config");
info!("gateway config updated: {res:?}")
info!("gateway config updated: {:?}", res)
}
@@ -17,5 +17,5 @@ pub async fn unbond_gateway(client: SigningClient) {
.await
.expect("failed to unbond gateway!");
info!("Unbonding result: {res:?}")
info!("Unbonding result: {:?}", res)
}
@@ -73,5 +73,5 @@ pub async fn vesting_bond_gateway(args: Args, client: SigningClient) {
.await
.expect("failed to bond gateway!");
info!("Vesting bonding gateway result: {res:?}")
info!("Vesting bonding gateway result: {:?}", res)
}
@@ -17,5 +17,5 @@ pub async fn vesting_unbond_gateway(client: SigningClient) {
.await
.expect("failed to unbond vesting gateway!");
info!("Unbonding vesting result: {res:?}")
info!("Unbonding vesting result: {:?}", res)
}
@@ -106,5 +106,5 @@ pub async fn bond_mixnode(args: Args, client: SigningClient) {
.await
.expect("failed to bond mixnode!");
info!("Bonding result: {res:?}")
info!("Bonding result: {:?}", res)
}
@@ -25,5 +25,5 @@ pub async fn decrease_pledge(args: Args, client: SigningClient) {
.await
.expect("failed to decrease pledge!");
info!("decreasing pledge: {res:?}");
info!("decreasing pledge: {:?}", res);
}
@@ -15,5 +15,5 @@ pub async fn migrate_vested_mixnode(_args: Args, client: SigningClient) {
.await
.expect("failed to migrate mixnode!");
info!("migration result: {res:?}")
info!("migration result: {:?}", res)
}
@@ -15,5 +15,5 @@ pub async fn migrate_to_nymnode(_args: Args, client: SigningClient) {
.await
.expect("failed to migrate mixnode!");
info!("migration result: {res:?}")
info!("migration result: {:?}", res)
}
@@ -25,5 +25,5 @@ pub async fn pledge_more(args: Args, client: SigningClient) {
.await
.expect("failed to pledge more!");
info!("pledging more: {res:?}");
info!("pledging more: {:?}", res);
}
@@ -17,5 +17,5 @@ pub async fn claim_operator_reward(_args: Args, client: SigningClient) {
.await
.expect("failed to claim operator reward");
info!("Claiming operator reward: {res:?}")
info!("Claiming operator reward: {:?}", res)
}
@@ -20,5 +20,5 @@ pub async fn vesting_claim_operator_reward(client: SigningClient) {
.await
.expect("failed to claim vesting operator reward");
info!("Claiming vesting operator reward: {res:?}")
info!("Claiming vesting operator reward: {:?}", res)
}
@@ -64,5 +64,5 @@ pub async fn update_config(args: Args, client: SigningClient) {
.await
.expect("updating mix-node config");
info!("mixnode config updated: {res:?}")
info!("mixnode config updated: {:?}", res)
}
@@ -65,5 +65,5 @@ pub async fn vesting_update_config(client: SigningClient, args: Args) {
.await
.expect("updating vesting mix-node config");
info!("mixnode config updated: {res:?}")
info!("mixnode config updated: {:?}", res)
}
@@ -18,5 +18,5 @@ pub async fn unbond_mixnode(_args: Args, client: SigningClient) {
.await
.expect("failed to unbond mixnode!");
info!("Unbonding result: {res:?}")
info!("Unbonding result: {:?}", res)
}
@@ -106,5 +106,5 @@ pub async fn vesting_bond_mixnode(client: SigningClient, args: Args, denom: &str
.await
.expect("failed to bond vesting mixnode!");
info!("Bonding vesting result: {res:?}")
info!("Bonding vesting result: {:?}", res)
}
@@ -25,5 +25,5 @@ pub async fn vesting_decrease_pledge(args: Args, client: SigningClient) {
.await
.expect("failed to vesting decrease pledge!");
info!("vesting decreasing pledge: {res:?}");
info!("vesting decreasing pledge: {:?}", res);
}
@@ -26,5 +26,5 @@ pub async fn vesting_pledge_more(args: Args, client: SigningClient) {
.await
.expect("failed to pledge more!");
info!("vesting pledge more: {res:?}");
info!("vesting pledge more: {:?}", res);
}
@@ -20,5 +20,5 @@ pub async fn vesting_unbond_mixnode(client: SigningClient) {
.await
.expect("failed to unbond vesting mixnode!");
info!("Unbonding vesting result: {res:?}")
info!("Unbonding vesting result: {:?}", res)
}
@@ -85,5 +85,5 @@ pub async fn bond_nymnode(args: Args, client: SigningClient) {
.await
.expect("failed to bond nymnode!");
info!("Bonding result: {res:?}")
info!("Bonding result: {:?}", res)
}
@@ -25,5 +25,5 @@ pub async fn decrease_pledge(args: Args, client: SigningClient) {
.await
.expect("failed to decrease pledge!");
info!("decreasing pledge: {res:?}");
info!("decreasing pledge: {:?}", res);
}
@@ -25,5 +25,5 @@ pub async fn increase_pledge(args: Args, client: SigningClient) {
.await
.expect("failed to pledge more!");
info!("pledging more: {res:?}");
info!("pledging more: {:?}", res);
}
@@ -17,5 +17,5 @@ pub async fn claim_operator_reward(_args: Args, client: SigningClient) {
.await
.expect("failed to claim operator reward");
info!("Claiming operator reward: {res:?}")
info!("Claiming operator reward: {:?}", res)
}
@@ -46,5 +46,5 @@ pub async fn update_config(args: Args, client: SigningClient) {
.await
.expect("updating nym node config");
info!("nym node config updated: {res:?}")
info!("nym node config updated: {:?}", res)
}
@@ -68,6 +68,6 @@ pub async fn update_cost_params(args: Args, client: SigningClient) -> anyhow::Re
.await
.expect("failed to update cost params");
info!("Cost params result: {res:?}");
info!("Cost params result: {:?}", res);
Ok(())
}
@@ -18,5 +18,5 @@ pub async fn unbond_nymnode(_args: Args, client: SigningClient) {
.await
.expect("failed to unbond Nym Node!");
info!("Unbonding result: {res:?}")
info!("Unbonding result: {:?}", res)
}
@@ -52,7 +52,7 @@ pub fn sign(args: Args, prefix: &str, mnemonic: Option<bip39::Mnemonic>) {
println!("{}", json!(output));
}
Err(e) => {
error!("Failed to sign message. {e}");
error!("Failed to sign message. {}", e);
}
}
}
@@ -61,7 +61,7 @@ pub fn sign(args: Args, prefix: &str, mnemonic: Option<bip39::Mnemonic>) {
}
},
Err(e) => {
error!("Failed to derive accounts. {e}");
error!("Failed to derive accounts. {}", e);
}
}
}
@@ -38,7 +38,7 @@ pub async fn verify(args: Args, client: &QueryClient) {
let public_key = match AccountId::from_str(&args.public_key_or_address) {
Ok(address) => {
info!("Found account address instead of public key, so looking up public key for {address} from chain");
info!("Found account address instead of public key, so looking up public key for {} from chain", address);
match client.get_account_public_key(&address).await.ok() {
Some(public_key) => {
if let Some(k) = public_key {
@@ -48,7 +48,8 @@ pub async fn verify(args: Args, client: &QueryClient) {
}
None => {
error!(
"Address {address} does not have a public key recorded on the chain. This is probably because the account has never signed a transaction."
"Address {} does not have a public key recorded on the chain. This is probably because the account has never signed a transaction.",
address
);
None
}
@@ -57,7 +58,7 @@ pub async fn verify(args: Args, client: &QueryClient) {
Err(_) => match PublicKey::from_json(&args.public_key_or_address) {
Ok(parsed) => Some(parsed),
Err(e) => {
error!("Public key should be JSON. Unable to parse: {e}");
error!("Public key should be JSON. Unable to parse: {}", e);
None
}
},
@@ -77,7 +78,7 @@ pub async fn verify(args: Args, client: &QueryClient) {
) {
Ok(()) => println!("SUCCESS ✅ signature verified"),
Err(e) => {
error!("FAILURE ❌ Signature verification failed: {e}");
error!("FAILURE ❌ Signature verification failed: {}", e);
}
}
}
@@ -86,6 +86,6 @@ pub async fn create(args: Args, client: SigningClient, network_details: &NymNetw
.await
.unwrap();
info!("Vesting result: {res:?}");
info!("Coin send result: {send_coin_response:?}");
info!("Vesting result: {:?}", res);
info!("Coin send result: {:?}", send_coin_response);
}
@@ -29,7 +29,7 @@ pub async fn query(args: Args, client: QueryClient, address_from_mnemonic: Optio
.address
.unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic"));
info!("Checking account {account_id} for a vesting schedule...");
info!("Checking account {} for a vesting schedule...", account_id);
let vesting_address = account_id.to_string();
let denom = client.current_chain_details().mix_denom.base.as_str();
@@ -1,24 +0,0 @@
[package]
name = "nym-contracts-common-testing"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
[dependencies]
anyhow = { workspace = true }
cosmwasm-std = { workspace = true }
cw-storage-plus = { workspace = true }
serde = { workspace = true }
rand_chacha = { workspace = true }
rand = { workspace = true }
cw-multi-test = { workspace = true }
[lints]
workspace = true
@@ -1,127 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::testing::{message_info, MockApi, MockQuerier, MockStorage};
use cosmwasm_std::{
coins, Addr, BankMsg, CosmosMsg, Empty, Env, MemoryStorage, MessageInfo, Order, OwnedDeps,
Response, StdResult, Storage,
};
use cw_storage_plus::{KeyDeserialize, Map, Prefix, PrimaryKey};
use rand::{RngCore, SeedableRng};
use rand_chacha::ChaCha20Rng;
use serde::de::DeserializeOwned;
use serde::Serialize;
pub const TEST_DENOM: &str = "unym";
pub const TEST_PREFIX: &str = "n";
pub fn mock_api() -> MockApi {
MockApi::default().with_prefix(TEST_PREFIX)
}
pub fn mock_dependencies() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
OwnedDeps {
storage: MockStorage::default(),
api: mock_api(),
querier: MockQuerier::default(),
custom_query_type: Default::default(),
}
}
pub fn test_rng() -> ChaCha20Rng {
let dummy_seed = [42u8; 32];
rand_chacha::ChaCha20Rng::from_seed(dummy_seed)
}
pub fn deps_with_balance(env: &Env) -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
let mut deps = mock_dependencies();
deps.querier = MockQuerier::<Empty>::new(&[(
env.contract.address.as_str(),
coins(100000000000, TEST_DENOM).as_slice(),
)]);
deps
}
pub fn generate_sorted_addresses(n: usize) -> Vec<Addr> {
let mut rng = test_rng();
let mut addrs = Vec::with_capacity(n);
for i in 0..n {
addrs.push(mock_api().addr_make(&format!("addr{i}{}", rng.next_u64())));
}
addrs.sort();
addrs
}
pub fn addr<S: AsRef<str>>(raw: S) -> Addr {
mock_api().addr_make(raw.as_ref())
}
pub fn sender<S: AsRef<str>>(raw: S) -> MessageInfo {
message_info(&addr(raw), &[])
}
pub trait ExtractBankMsg {
fn unwrap_bank_msg(self) -> Option<BankMsg>;
}
impl ExtractBankMsg for Response {
fn unwrap_bank_msg(self) -> Option<BankMsg> {
for msg in self.messages {
match msg.msg {
CosmosMsg::Bank(bank_msg) => return Some(bank_msg),
_ => continue,
}
}
None
}
}
pub trait FullReader<'a> {
type Key;
type Value: Serialize + DeserializeOwned;
fn all_values(&self, store: &dyn Storage) -> StdResult<Vec<Self::Value>>;
fn all_key_values(&self, store: &dyn Storage) -> StdResult<Vec<(Self::Key, Self::Value)>>;
}
impl<'a, K, T> FullReader<'a> for Map<K, T>
where
T: Serialize + DeserializeOwned,
K: PrimaryKey<'a> + KeyDeserialize,
K::Output: 'static,
{
type Key = K::Output;
type Value = T;
fn all_values(&self, store: &dyn Storage) -> StdResult<Vec<Self::Value>> {
self.range(store, None, None, Order::Ascending)
.map(|record| record.map(|r| r.1))
.collect()
}
fn all_key_values(&self, store: &dyn Storage) -> StdResult<Vec<(Self::Key, Self::Value)>> {
self.range(store, None, None, Order::Ascending).collect()
}
}
impl<'a, K, T, B> FullReader<'a> for Prefix<K, T, B>
where
K: KeyDeserialize + 'static,
T: Serialize + DeserializeOwned,
B: PrimaryKey<'a>,
{
type Key = K::Output;
type Value = T;
fn all_values(&self, store: &dyn Storage) -> StdResult<Vec<Self::Value>> {
self.range(store, None, None, Order::Ascending)
.map(|record| record.map(|r| r.1))
.collect()
}
fn all_key_values(&self, store: &dyn Storage) -> StdResult<Vec<(Self::Key, Self::Value)>> {
self.range(store, None, None, Order::Ascending).collect()
}
}
@@ -1,13 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// those are all used exclusively for testing thus unwraps, et al. are allowed
#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
#![allow(clippy::panic)]
pub mod helpers;
pub mod tester;
pub use helpers::*;
pub use tester::*;
@@ -1,239 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{ContractTester, TestableNymContract};
use cosmwasm_std::testing::{message_info, mock_env};
use cosmwasm_std::{
from_json, Addr, Coin, ContractInfo, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
Storage, Timestamp,
};
use cw_multi_test::{next_block, AppResponse, Executor};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::any::type_name;
use std::fmt::Debug;
pub trait ContractOpts {
type ExecuteMsg;
type QueryMsg;
type ContractError;
fn deps(&self) -> Deps<'_>;
fn deps_mut(&mut self) -> DepsMut<'_>;
fn env(&self) -> Env;
fn addr_make(&self, input: &str) -> Addr;
fn deps_mut_env(&mut self) -> (DepsMut<'_>, Env) {
let env = self.env().clone();
(self.deps_mut(), env)
}
fn storage(&self) -> &dyn Storage;
fn storage_mut(&mut self) -> &mut dyn Storage;
fn read_from_contract_storage<T: DeserializeOwned>(&self, key: impl AsRef<[u8]>) -> Option<T>;
fn set_contract_storage(&mut self, key: impl AsRef<[u8]>, value: impl AsRef<[u8]>);
fn unchecked_read_from_contract_storage<T: DeserializeOwned>(
&self,
key: impl AsRef<[u8]>,
) -> T {
let typ = type_name::<T>();
self.read_from_contract_storage(key)
.unwrap_or_else(|| panic!("value of type {typ} not present in the storage"))
}
fn execute_raw(
&mut self,
sender: Addr,
message: Self::ExecuteMsg,
) -> Result<Response, Self::ContractError> {
self.execute_raw_with_balance(sender, &[], message)
}
fn execute_raw_with_balance(
&mut self,
sender: Addr,
coins: &[Coin],
message: Self::ExecuteMsg,
) -> Result<Response, Self::ContractError>;
}
impl<C> ContractOpts for ContractTester<C>
where
C: TestableNymContract,
{
type ExecuteMsg = C::ExecuteMsg;
type QueryMsg = C::QueryMsg;
type ContractError = C::ContractError;
fn deps(&self) -> Deps<'_> {
Deps {
storage: &self.storage,
api: self.app.api(),
querier: self.app.wrap(),
}
}
fn deps_mut(&mut self) -> DepsMut<'_> {
DepsMut {
storage: &mut self.storage,
api: self.app.api(),
querier: self.app.wrap(),
}
}
fn env(&self) -> Env {
Env {
block: self.app.block_info(),
contract: ContractInfo {
address: self.contract_address.clone(),
},
..mock_env()
}
}
fn addr_make(&self, input: &str) -> Addr {
self.app.api().addr_make(input)
}
fn storage(&self) -> &dyn Storage {
&self.storage
}
fn storage_mut(&mut self) -> &mut dyn Storage {
&mut self.storage
}
fn read_from_contract_storage<T: DeserializeOwned>(&self, key: impl AsRef<[u8]>) -> Option<T> {
let raw = self.deps().storage.get(key.as_ref())?;
from_json(&raw).ok()
}
fn set_contract_storage(&mut self, key: impl AsRef<[u8]>, value: impl AsRef<[u8]>) {
self.deps_mut().storage.set(key.as_ref(), value.as_ref());
}
fn execute_raw_with_balance(
&mut self,
sender: Addr,
coins: &[Coin],
message: C::ExecuteMsg,
) -> Result<Response, C::ContractError> {
let env = self.env();
let info = message_info(&sender, coins);
C::execute()(self.deps_mut(), env, info, message)
}
}
pub trait ChainOpts: ContractOpts {
fn set_contract_balance(&mut self, balance: Coin);
fn next_block(&mut self);
fn set_block_time(&mut self, time: Timestamp);
fn execute_msg(
&mut self,
sender: Addr,
message: &Self::ExecuteMsg,
) -> anyhow::Result<AppResponse> {
self.execute_msg_with_balance(sender, &[], message)
}
fn execute_msg_with_balance(
&mut self,
sender: Addr,
coins: &[Coin],
message: &Self::ExecuteMsg,
) -> anyhow::Result<AppResponse>;
fn execute_arbitrary_contract<T: Serialize + Debug>(
&mut self,
contract: Addr,
sender: MessageInfo,
message: &T,
) -> anyhow::Result<AppResponse>;
fn query_arbitrary_contract<Q: Serialize + Debug, T: DeserializeOwned>(
&self,
contract: Addr,
message: &Q,
) -> StdResult<T>;
fn query<T: DeserializeOwned>(&self, message: &Self::QueryMsg) -> StdResult<T>;
}
impl<C> ChainOpts for ContractTester<C>
where
C: TestableNymContract,
{
fn set_contract_balance(&mut self, balance: Coin) {
let contract_address = &self.contract_address;
self.app
.router()
.bank
.init_balance(
&mut self.storage.inner_storage(),
contract_address,
vec![balance],
)
.unwrap();
}
fn next_block(&mut self) {
self.app.update_block(next_block)
}
fn set_block_time(&mut self, time: Timestamp) {
self.app.update_block(|b| b.time = time)
}
fn execute_msg(
&mut self,
sender: Addr,
message: &C::ExecuteMsg,
) -> anyhow::Result<AppResponse> {
self.execute_msg_with_balance(sender, &[], message)
}
fn execute_msg_with_balance(
&mut self,
sender: Addr,
coins: &[Coin],
message: &C::ExecuteMsg,
) -> anyhow::Result<AppResponse> {
self.app
.execute_contract(sender, self.contract_address.clone(), message, coins)
}
fn execute_arbitrary_contract<T: Serialize + Debug>(
&mut self,
contract: Addr,
sender: MessageInfo,
message: &T,
) -> anyhow::Result<AppResponse> {
let coins = &sender.funds;
let sender = sender.sender;
self.app.execute_contract(sender, contract, message, coins)
}
fn query_arbitrary_contract<Q: Serialize + Debug, T: DeserializeOwned>(
&self,
contract: Addr,
message: &Q,
) -> StdResult<T> {
self.app.wrap().query_wasm_smart(contract, message)
}
fn query<T: DeserializeOwned>(&self, message: &C::QueryMsg) -> StdResult<T> {
self.app
.wrap()
.query_wasm_smart(self.contract_address.as_str(), message)
}
}
@@ -1,305 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
CommonStorageKeys, ContractOpts, ContractTester, StorageWrapper, TestableNymContract,
TEST_DENOM,
};
use cosmwasm_std::testing::message_info;
use cosmwasm_std::{
coin, coins, from_json, to_json_vec, Addr, Coin, MessageInfo, StdError, StdResult, Storage,
};
use cw_multi_test::Executor;
use cw_storage_plus::{Key, Path, PrimaryKey};
use rand::RngCore;
use rand_chacha::ChaCha20Rng;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::any::type_name;
use std::ops::Deref;
pub trait StorageReader {
fn common_key(&self, key: CommonStorageKeys) -> Option<&[u8]>;
fn read_common_value<T: DeserializeOwned>(&self, key: CommonStorageKeys) -> Option<T> {
self.read_from_contract_storage(self.common_key(key)?)
}
fn unchecked_read_common_value<T: DeserializeOwned>(&self, key: CommonStorageKeys) -> T {
self.unchecked_read_from_contract_storage(
self.common_key(key)
.unwrap_or_else(|| panic!("no key set for {key:?}")),
)
}
fn read_from_contract_storage<T: DeserializeOwned>(&self, key: impl AsRef<[u8]>) -> Option<T>;
fn unchecked_read_from_contract_storage<T: DeserializeOwned>(
&self,
key: impl AsRef<[u8]>,
) -> T {
let typ = type_name::<T>();
self.read_from_contract_storage(key)
.unwrap_or_else(|| panic!("value of type {typ} not present in the storage"))
}
}
// technically it shouldn't rely on `StorageReader` and `common_key` should be extracted
// but this makes it a tad easier and it's only testing code so it's fine
pub trait StorageWriter: StorageReader {
fn set_common_value<T: Serialize>(
&mut self,
key: CommonStorageKeys,
value: &T,
) -> StdResult<()> {
let key = self
.common_key(key)
.ok_or(StdError::not_found("key not found"))?
.to_vec();
self.set_storage_value(key, value)
}
fn set_storage(&mut self, key: impl AsRef<[u8]>, value: impl AsRef<[u8]>);
fn set_storage_value<T: Serialize>(
&mut self,
key: impl AsRef<[u8]>,
value: &T,
) -> StdResult<()> {
self.set_storage(key, &to_json_vec(value)?);
Ok(())
}
}
pub trait ArbitraryContractStorageReader {
fn may_read_from_contract_storage(
&self,
address: impl Into<String>,
key: impl AsRef<[u8]>,
) -> Option<Vec<u8>>;
fn must_read_from_contract_storage(
&self,
address: impl Into<String>,
key: impl AsRef<[u8]>,
) -> StdResult<Vec<u8>> {
let key = key.as_ref();
self.may_read_from_contract_storage(address, key)
.ok_or(StdError::not_found(format!("no data under {key:?}")))
}
fn may_read_value_from_contract_storage<T: DeserializeOwned>(
&self,
address: impl Into<String>,
key: impl AsRef<[u8]>,
) -> StdResult<Option<T>> {
let Some(bytes) = self.may_read_from_contract_storage(address, key) else {
return Ok(None);
};
from_json(&bytes).map(Some)
}
fn must_read_value_from_contract_storage<T: DeserializeOwned>(
&self,
address: impl Into<String>,
key: impl AsRef<[u8]>,
) -> StdResult<T> {
let bytes = self.must_read_from_contract_storage(address, key)?;
from_json(&bytes)
}
}
pub trait ArbitraryContractStorageWriter {
fn set_contract_storage(
&mut self,
address: impl Into<String>,
key: impl AsRef<[u8]>,
value: impl AsRef<[u8]>,
);
fn set_contract_storage_value<T: Serialize>(
&mut self,
address: impl Into<String>,
key: impl AsRef<[u8]>,
value: &T,
) -> StdResult<()> {
self.set_contract_storage(address, key, &to_json_vec(value)?);
Ok(())
}
// attempts to write to an arbitrary contract `cw_storage_plus::Map`
fn set_contract_map_value<'a, K, T>(
&mut self,
address: impl Into<String>,
namespace: impl AsRef<[u8]>,
key: K,
value: &T,
) -> StdResult<()>
where
K: PrimaryKey<'a>,
T: Serialize + DeserializeOwned,
{
let key_path: Path<T> = Path::new(
namespace.as_ref(),
&key.key().iter().map(Key::as_ref).collect::<Vec<_>>(),
);
let storage_key = key_path.deref();
self.set_contract_storage_value(address, storage_key, value)
}
}
// contract that has an admin
pub trait AdminExt: StorageReader + StorageWriter {
fn admin(&self) -> Option<Addr> {
self.read_common_value(CommonStorageKeys::Admin)
}
fn update_admin(&mut self, admin: &Option<Addr>) -> StdResult<()> {
self.set_common_value(CommonStorageKeys::Admin, admin)
}
fn admin_unchecked(&self) -> Addr {
self.admin().expect("no admin set")
}
fn admin_msg(&self) -> MessageInfo {
message_info(&self.admin_unchecked(), &[])
}
}
// contract that operates on some specific coin denom
pub trait DenomExt: StorageReader {
fn denom(&self) -> String {
self.unchecked_read_common_value(CommonStorageKeys::Denom)
}
fn coin(&self, amount: u128) -> Coin {
coin(amount, self.denom())
}
fn coins(&self, amount: u128) -> Vec<Coin> {
coins(amount, self.denom())
}
}
pub trait RandExt {
fn raw_rng(&mut self) -> &mut ChaCha20Rng;
fn generate_account(&mut self) -> Addr;
fn generate_account_with_balance(&mut self) -> Addr
where
Self: BankExt;
}
pub trait BankExt {
fn send_tokens(&mut self, to: Addr, amount: Coin) -> anyhow::Result<()>;
}
impl<T> AdminExt for T where T: StorageReader + StorageWriter {}
impl<T> DenomExt for T where T: StorageReader {}
impl<C: TestableNymContract> StorageReader for ContractTester<C> {
fn common_key(&self, key: CommonStorageKeys) -> Option<&[u8]> {
self.common_storage_keys.get(&key).map(|v| &**v)
}
fn read_from_contract_storage<T: DeserializeOwned>(&self, key: impl AsRef<[u8]>) -> Option<T> {
<Self as ContractOpts>::read_from_contract_storage(self, key)
}
}
impl<C: TestableNymContract> StorageWriter for ContractTester<C> {
fn set_storage(&mut self, key: impl AsRef<[u8]>, value: impl AsRef<[u8]>) {
<Self as ContractOpts>::set_contract_storage(self, key, value)
}
}
impl<C: TestableNymContract> BankExt for ContractTester<C> {
fn send_tokens(&mut self, to: Addr, amount: Coin) -> anyhow::Result<()> {
self.app
.send_tokens(self.master_address.clone(), to, &[amount])?;
Ok(())
}
}
impl<C: TestableNymContract> RandExt for ContractTester<C> {
fn raw_rng(&mut self) -> &mut ChaCha20Rng {
&mut self.rng
}
fn generate_account(&mut self) -> Addr {
self.app
.api()
.addr_make(&format!("foomp{}", self.rng.next_u64()))
}
fn generate_account_with_balance(&mut self) -> Addr
where
Self: BankExt,
{
let addr = self.generate_account();
let million = 1_000_000_000_000;
self.send_tokens(addr.clone(), coin(million, TEST_DENOM))
.unwrap();
addr
}
}
impl ArbitraryContractStorageReader for StorageWrapper {
fn may_read_from_contract_storage(
&self,
address: impl Into<String>,
key: impl AsRef<[u8]>,
) -> Option<Vec<u8>> {
self.contract_storage_wrapper(&Addr::unchecked(address))
.get(key.as_ref())
}
}
impl ArbitraryContractStorageWriter for StorageWrapper {
fn set_contract_storage(
&mut self,
address: impl Into<String>,
key: impl AsRef<[u8]>,
value: impl AsRef<[u8]>,
) {
// yeah, we're unnecessarily cloning a Rc pointer, but this is a test code, so this inefficiency is fine
let mut wrapped_storage = self
.clone()
.contract_storage_wrapper(&Addr::unchecked(address));
wrapped_storage.set(key.as_ref(), value.as_ref());
}
}
impl<C> ArbitraryContractStorageReader for ContractTester<C>
where
C: TestableNymContract,
{
fn may_read_from_contract_storage(
&self,
address: impl Into<String>,
key: impl AsRef<[u8]>,
) -> Option<Vec<u8>> {
self.storage
.as_inner_storage()
.may_read_from_contract_storage(address, key)
}
}
impl<C> ArbitraryContractStorageWriter for ContractTester<C>
where
C: TestableNymContract,
{
fn set_contract_storage(
&mut self,
address: impl Into<String>,
key: impl AsRef<[u8]>,
value: impl AsRef<[u8]>,
) {
self.storage
.as_inner_storage_mut()
.set_contract_storage(address, key, value);
}
}
@@ -1,276 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{mock_api, test_rng, TEST_DENOM};
use cosmwasm_std::testing::MockApi;
use cosmwasm_std::{
coin, coins, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Order, QuerierWrapper,
Record, Response, Storage,
};
use cw_multi_test::{App, AppBuilder, BankKeeper, Contract, ContractWrapper, Executor};
use rand_chacha::ChaCha20Rng;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::marker::PhantomData;
pub use basic_traits::*;
pub use extensions::*;
pub use crate::tester::storage_wrapper::{ContractStorageWrapper, StorageWrapper};
mod basic_traits;
mod extensions;
mod storage_wrapper;
// copied from cw-multi-test (but removed generics for custom messages and querier for we don't need them for now)
pub type ContractFn<T, E> =
fn(deps: DepsMut, env: Env, info: MessageInfo, msg: T) -> Result<Response, E>;
pub type QueryFn<T, E> = fn(deps: Deps, env: Env, msg: T) -> Result<Binary, E>;
pub type PermissionedFn<T, E> = fn(deps: DepsMut, env: Env, msg: T) -> Result<Response, E>;
pub type ContractClosure<T, E> = Box<dyn Fn(DepsMut, Env, MessageInfo, T) -> Result<Response, E>>;
pub type QueryClosure<T, E> = Box<dyn Fn(Deps, Env, T) -> Result<Binary, E>>;
pub trait TestableNymContract {
const NAME: &'static str;
type InitMsg: DeserializeOwned + Serialize + Debug + 'static;
type ExecuteMsg: DeserializeOwned + Serialize + Debug + 'static;
type QueryMsg: DeserializeOwned + Serialize + Debug + 'static;
type MigrateMsg: DeserializeOwned + Serialize + Debug + 'static;
type ContractError: Display + Debug + Send + Sync + 'static;
fn instantiate() -> ContractFn<Self::InitMsg, Self::ContractError>;
fn execute() -> ContractFn<Self::ExecuteMsg, Self::ContractError>;
fn query() -> QueryFn<Self::QueryMsg, Self::ContractError>;
fn migrate() -> PermissionedFn<Self::MigrateMsg, Self::ContractError>;
fn base_init_msg() -> Self::InitMsg;
// // for now we don't care about custom queriers
// fn contract_wrapper() -> ContractWrapper<
// Self::ExecuteMsg,
// Self::InitMsg,
// Self::QueryMsg,
// Self::ContractError,
// anyhow::Error,
// anyhow::Error,
// Empty,
// Empty,
// Empty,
// Self::ContractError,
// Self::ContractError,
// Self::MigrateMsg,
// Self::ContractError,
// > {
// ContractWrapper::new(Self::execute(), Self::instantiate(), Self::query())
// .with_migrate(Self::migrate())
// }
fn dyn_contract() -> Box<dyn Contract<Empty>> {
Box::new(
ContractWrapper::new(Self::execute(), Self::instantiate(), Self::query())
.with_migrate(Self::migrate()),
)
}
fn init() -> ContractTester<Self>
where
Self: Sized,
{
ContractTesterBuilder::new()
.instantiate::<Self>(None)
.build()
}
}
pub struct ContractTesterBuilder<C> {
contract: PhantomData<C>,
master_address: Addr,
app: App<BankKeeper, MockApi, StorageWrapper>,
storage: StorageWrapper,
pub well_known_contracts: HashMap<&'static str, Addr>,
}
impl<C> ContractTesterBuilder<C> {
#[allow(clippy::new_without_default)]
pub fn new() -> Self
where
C: TestableNymContract,
{
let storage = StorageWrapper::new();
let api = mock_api();
let master_address = api.addr_make("master-owner");
let app = AppBuilder::new()
.with_api(api)
.with_storage(storage.clone())
.build(|router, _api, storage| {
router
.bank
.init_balance(
storage,
&master_address,
coins(1000000000000000, TEST_DENOM),
)
.unwrap()
});
ContractTesterBuilder {
contract: Default::default(),
master_address,
app,
storage,
well_known_contracts: Default::default(),
}
}
pub fn instantiate<D: TestableNymContract>(
mut self,
custom_init_msg: Option<D::InitMsg>,
) -> ContractTesterBuilder<C> {
let code_id = self.app.store_code(D::dyn_contract());
let contract_address = self
.app
.instantiate_contract(
code_id,
self.master_address.clone(),
&custom_init_msg.unwrap_or(D::base_init_msg()),
&[],
D::NAME,
Some(self.master_address.to_string()),
)
.unwrap();
// send some tokens to the contract
self.app
.send_tokens(
self.master_address.clone(),
contract_address.clone(),
&[coin(100000000, TEST_DENOM)],
)
.unwrap();
self.well_known_contracts.insert(D::NAME, contract_address);
self
}
pub fn build(self) -> ContractTester<C>
where
C: TestableNymContract,
{
if !self.well_known_contracts.contains_key(C::NAME) {
panic!("{} contract has not been instantiated", C::NAME);
}
let contract_address = self.well_known_contracts[C::NAME].clone();
ContractTester {
contract: self.contract,
app: self.app,
rng: test_rng(),
master_address: self.master_address,
storage: self.storage.contract_storage_wrapper(&contract_address),
contract_address,
common_storage_keys: Default::default(),
well_known_contracts: self.well_known_contracts,
}
}
pub fn contract_storage_wrapper(&self, contract: &Addr) -> ContractStorageWrapper {
self.storage.contract_storage_wrapper(contract)
}
pub fn api(&self) -> MockApi {
*self.app.api()
}
pub fn querier(&self) -> QuerierWrapper {
self.app.wrap()
}
}
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum CommonStorageKeys {
Admin,
Denom,
}
pub struct ContractTester<C: TestableNymContract> {
contract: PhantomData<C>,
pub app: App<BankKeeper, MockApi, StorageWrapper>,
pub rng: ChaCha20Rng,
pub contract_address: Addr,
pub master_address: Addr,
pub(crate) storage: ContractStorageWrapper,
pub common_storage_keys: HashMap<CommonStorageKeys, Vec<u8>>,
// TODO: limitation: doesn't allow multiple contracts of the same type (but that's fine for the time being)
pub well_known_contracts: HashMap<&'static str, Addr>,
}
impl<C> ContractTester<C>
where
C: TestableNymContract,
{
pub fn insert_common_storage_key(&mut self, key: CommonStorageKeys, value: impl AsRef<[u8]>) {
self.common_storage_keys
.insert(key, value.as_ref().to_vec());
}
pub fn with_common_storage_key(
mut self,
key: CommonStorageKeys,
value: impl AsRef<[u8]>,
) -> Self {
self.insert_common_storage_key(key, value);
self
}
}
impl<C> Storage for ContractTester<C>
where
C: TestableNymContract,
{
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
self.storage.get(key)
}
fn range<'a>(
&'a self,
start: Option<&[u8]>,
end: Option<&[u8]>,
order: Order,
) -> Box<dyn Iterator<Item = Record> + 'a> {
self.storage.range(start, end, order)
}
fn range_keys<'a>(
&'a self,
start: Option<&[u8]>,
end: Option<&[u8]>,
order: Order,
) -> Box<dyn Iterator<Item = Vec<u8>> + 'a> {
self.storage.range_keys(start, end, order)
}
fn range_values<'a>(
&'a self,
start: Option<&[u8]>,
end: Option<&[u8]>,
order: Order,
) -> Box<dyn Iterator<Item = Vec<u8>> + 'a> {
self.storage.range_values(start, end, order)
}
fn set(&mut self, key: &[u8], value: &[u8]) {
self.storage.set(key, value)
}
fn remove(&mut self, key: &[u8]) {
self.storage.remove(key)
}
}
@@ -18,7 +18,6 @@ serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
[dev-dependencies]
anyhow = { workspace = true }
serde_json = { workspace = true }
[build-dependencies]
@@ -35,7 +35,7 @@ pub enum ContractsCommonError {
/// Percent represents a value between 0 and 100%
/// (i.e. between 0.0 and 1.0)
#[cw_serde]
#[derive(Copy, Default, PartialOrd, Ord, Eq)]
#[derive(Copy, Default, PartialOrd)]
pub struct Percent(#[serde(deserialize_with = "de_decimal_percent")] Decimal);
impl Percent {
@@ -80,44 +80,6 @@ impl Percent {
pub fn checked_pow(&self, exp: u32) -> Result<Self, OverflowError> {
self.0.checked_pow(exp).map(Percent)
}
// truncate provided percent to only have 2 decimal places,
// e.g. convert "0.1234567" into "0.12"
// the purpose of it is to reduce storage space, in particular for the performance contract
// since that extra precision gains us nothing
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn round_to_two_decimal_places(&self) -> Self {
let raw = self.0;
const DECIMAL_FRACTIONAL: Uint128 = Uint128::new(1_000_000_000_000_000_000u128); // 1*10**18
const THRESHOLD: Decimal = Decimal::permille(5); // 0.005
// in case it ever changes since it's not exposed in the public API
debug_assert_eq!(
DECIMAL_FRACTIONAL,
Uint128::new(10).pow(Decimal::DECIMAL_PLACES)
);
let int = (raw.atomics() * Uint128::new(100)) / DECIMAL_FRACTIONAL;
#[allow(clippy::unwrap_used)]
let floored = Decimal::from_atomics(int, 2).unwrap();
let diff = raw - floored;
let rounded = if diff >= THRESHOLD {
// ceil
floored + Decimal::percent(1)
} else {
floored
};
Percent(rounded)
}
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn average(&self, other: &Self) -> Self {
let sum = self.0 + other.0;
let inner = Decimal::from_ratio(sum.numerator(), sum.denominator() * Uint128::new(2));
Percent(inner)
}
}
impl Display for Percent {
@@ -372,7 +334,6 @@ mod tests {
}
#[test]
#[cfg(feature = "naive_float")]
fn naive_float_conversion() {
// around 15 decimal places is the maximum precision we can handle
// which is still way more than enough for what we use it for
@@ -386,41 +347,4 @@ mod tests {
assert!(converted.0 - converted.0 < epsilon);
}
#[test]
fn rounding_percent() {
let test_cases = vec![
("0", "0"),
("0.1", "0.1"),
("0.12", "0.12"),
("0.12", "0.123"),
("0.12", "0.123456789"),
("0.13", "0.125"),
("0.13", "0.126"),
("0.13", "0.126436545676"),
("0.99", "0.99"),
("0.99", "0.994"),
("1", "0.999"),
("1", "0.995"),
];
for (expected, input) in test_cases {
let expected: Percent = expected.parse().unwrap();
let pre_truncated: Percent = input.parse().unwrap();
assert_eq!(expected, pre_truncated.round_to_two_decimal_places())
}
}
#[test]
fn calculating_average() -> anyhow::Result<()> {
fn p(raw: &str) -> Percent {
raw.parse().unwrap()
}
assert_eq!(p("0.1").average(&p("0.1")), p("0.1"));
assert_eq!(p("0.1").average(&p("0.2")), p("0.15"));
assert_eq!(p("1").average(&p("0")), p("0.5"));
assert_eq!(p("0.123").average(&p("0.456")), p("0.2895"));
Ok(())
}
}
@@ -23,6 +23,7 @@ semver = { workspace = true, features = ["serde"] }
schemars = { workspace = true }
thiserror = { workspace = true }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
serde-json-wasm = { workspace = true }
humantime-serde = { workspace = true }
utoipa = { workspace = true, optional = true }
@@ -3,7 +3,7 @@
use crate::{IdentityKey, NodeId, SphinxKey};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{to_json_string, Addr, Coin};
use cosmwasm_std::{Addr, Coin};
use std::cmp::Ordering;
use std::fmt::Display;
@@ -154,7 +154,7 @@ pub struct GatewayConfigUpdate {
impl GatewayConfigUpdate {
pub fn to_inline_json(&self) -> String {
to_json_string(self).unwrap_or_else(|_| "serialisation failure".into())
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
}
}
@@ -16,7 +16,7 @@ use crate::{
Percent, ProfitMarginRange, SphinxKey,
};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{to_json_string, Addr, Coin, Decimal, StdResult, Uint128};
use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
@@ -604,7 +604,7 @@ pub struct NodeCostParams {
impl NodeCostParams {
pub fn to_inline_json(&self) -> String {
to_json_string(self).unwrap_or_else(|_| "serialisation failure".into())
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
}
}
@@ -773,7 +773,7 @@ pub struct MixNodeConfigUpdate {
impl MixNodeConfigUpdate {
pub fn to_inline_json(&self) -> String {
to_json_string(self).unwrap_or_else(|_| "serialisation failure".into())
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
}
}
@@ -5,7 +5,7 @@ use crate::helpers::IntoBaseDecimal;
use crate::nym_node::Role;
use crate::{error::MixnetContractError, Percent};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{to_json_string, Decimal};
use cosmwasm_std::Decimal;
pub type Performance = Percent;
pub type WorkFactor = Decimal;
@@ -84,7 +84,7 @@ pub struct IntervalRewardParams {
impl IntervalRewardParams {
pub fn to_inline_json(&self) -> String {
to_json_string(self).unwrap_or_else(|_| "serialisation failure".into())
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
}
}
@@ -410,7 +410,7 @@ impl IntervalRewardingParamsUpdate {
}
pub fn to_inline_json(&self) -> String {
to_json_string(self).unwrap_or_else(|_| "serialisation failure".into())
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
}
}
@@ -44,17 +44,6 @@ pub struct RewardEstimate {
pub operating_cost: Decimal,
}
impl RewardEstimate {
pub const fn zero() -> RewardEstimate {
RewardEstimate {
total_node_reward: Decimal::zero(),
operator: Decimal::zero(),
delegates: Decimal::zero(),
operating_cost: Decimal::zero(),
}
}
}
#[cw_serde]
#[derive(Copy, Default)]
pub struct RewardDistribution {
@@ -1,29 +0,0 @@
[package]
name = "nym-performance-contract-common"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
[dependencies]
thiserror = { workspace = true }
serde = { workspace = true }
schemars = { workspace = true }
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
cw-controllers = { workspace = true }
nym-contracts-common = { path = "../contracts-common" }
[features]
schema = []
[lints]
workspace = true
@@ -1,14 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod storage_keys {
pub const CONTRACT_ADMIN: &str = "contract-admin";
pub const INITIAL_EPOCH_ID: &str = "initial-epoch-id";
pub const LAST_SUBMISSION: &str = "last-submission";
pub const MIXNET_CONTRACT: &str = "mixnet-contract";
pub const AUTHORISED_COUNT: &str = "authorised-count";
pub const AUTHORISED: &str = "authorised";
pub const RETIRED: &str = "retired";
pub const PERFORMANCE_RESULTS: &str = "performance-results";
pub const SUBMISSION_METADATA: &str = "submission-metadata";
}
@@ -1,39 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{EpochId, NodeId};
use cosmwasm_std::Addr;
use cw_controllers::AdminError;
use thiserror::Error;
#[derive(Error, Debug, PartialEq)]
pub enum NymPerformanceContractError {
#[error("could not perform contract migration: {comment}")]
FailedMigration { comment: String },
#[error(transparent)]
Admin(#[from] AdminError),
#[error(transparent)]
StdErr(#[from] cosmwasm_std::StdError),
#[error("{address} is already an authorised network monitor")]
AlreadyAuthorised { address: Addr },
#[error("{address} is not an authorised network monitor")]
NotAuthorised { address: Addr },
#[error("attempted to submit performance data for epoch {epoch_id} and node {node_id} whilst last submitted was {last_epoch_id} for node {last_node_id}")]
StalePerformanceSubmission {
epoch_id: EpochId,
node_id: NodeId,
last_epoch_id: EpochId,
last_node_id: NodeId,
},
#[error("the batch performance data has not been sorted")]
UnsortedBatchSubmission,
#[error("node {node_id} does not appear to be bonded")]
NodeNotBonded { node_id: NodeId },
}
@@ -1,12 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod constants;
pub mod error;
pub mod helpers;
pub mod msg;
pub mod types;
pub use error::*;
pub use msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
pub use types::*;
@@ -1,125 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{EpochId, NodeId, NodePerformance};
use cosmwasm_schema::cw_serde;
#[cfg(feature = "schema")]
use crate::types::{
EpochMeasurementsPagedResponse, EpochPerformancePagedResponse,
FullHistoricalPerformancePagedResponse, LastSubmission, NetworkMonitorResponse,
NetworkMonitorsPagedResponse, NodeMeasurementsResponse, NodePerformancePagedResponse,
NodePerformanceResponse, RetiredNetworkMonitorsPagedResponse,
};
#[cw_serde]
pub struct InstantiateMsg {
pub mixnet_contract_address: String,
pub authorised_network_monitors: Vec<String>,
}
#[cw_serde]
pub enum ExecuteMsg {
/// Change the admin
UpdateAdmin { admin: String },
/// Attempt to submit performance data of a particular node for given epoch
Submit {
epoch: EpochId,
data: NodePerformance,
},
/// Attempt to submit performance data of a batch of nodes for given epoch
BatchSubmit {
epoch: EpochId,
data: Vec<NodePerformance>,
},
/// Attempt to authorise new network monitor for submitting performance data
AuthoriseNetworkMonitor { address: String },
/// Attempt to retire an existing network monitor and forbid it from submitting any future performance data
RetireNetworkMonitor { address: String },
/// An admin method to remove submitted node measurements. Used as an escape hatch should
/// the data stored get too unwieldy.
RemoveNodeMeasurements { epoch_id: EpochId, node_id: NodeId },
/// An admin method to remove submitted nodes measurements. Used as an escape hatch should
/// the data stored get too unwieldy. Note: it is expected to get called multiple times
/// until the response indicates all the epoch data has been removed.
RemoveEpochMeasurements { epoch_id: EpochId },
}
#[cw_serde]
#[cfg_attr(feature = "schema", derive(cosmwasm_schema::QueryResponses))]
pub enum QueryMsg {
#[cfg_attr(feature = "schema", returns(cw_controllers::AdminResponse))]
Admin {},
/// Returns performance of particular node for the provided epoch
#[cfg_attr(feature = "schema", returns(NodePerformanceResponse))]
NodePerformance { epoch_id: EpochId, node_id: NodeId },
/// Returns historical performance for particular node
#[cfg_attr(feature = "schema", returns(NodePerformancePagedResponse))]
NodePerformancePaged {
node_id: NodeId,
start_after: Option<EpochId>,
limit: Option<u32>,
},
/// Returns all submitted measurements for the particular node
#[cfg_attr(feature = "schema", returns(NodeMeasurementsResponse))]
NodeMeasurements { epoch_id: EpochId, node_id: NodeId },
/// Returns (paged) measurements for particular epoch
#[cfg_attr(feature = "schema", returns(EpochMeasurementsPagedResponse))]
EpochMeasurementsPaged {
epoch_id: EpochId,
start_after: Option<NodeId>,
limit: Option<u32>,
},
/// Returns (paged) performance for particular epoch
#[cfg_attr(feature = "schema", returns(EpochPerformancePagedResponse))]
EpochPerformancePaged {
epoch_id: EpochId,
start_after: Option<NodeId>,
limit: Option<u32>,
},
/// Returns full (paged) historical performance of the whole network
#[cfg_attr(feature = "schema", returns(FullHistoricalPerformancePagedResponse))]
FullHistoricalPerformancePaged {
start_after: Option<(EpochId, NodeId)>,
limit: Option<u32>,
},
/// Returns information about particular network monitor
#[cfg_attr(feature = "schema", returns(NetworkMonitorResponse))]
NetworkMonitor { address: String },
/// Returns information about all network monitors
#[cfg_attr(feature = "schema", returns(NetworkMonitorsPagedResponse))]
NetworkMonitorsPaged {
start_after: Option<String>,
limit: Option<u32>,
},
/// Returns information about all retired network monitors
#[cfg_attr(feature = "schema", returns(RetiredNetworkMonitorsPagedResponse))]
RetiredNetworkMonitorsPaged {
start_after: Option<String>,
limit: Option<u32>,
},
/// Returns information regarding the latest submitted performance data
#[cfg_attr(feature = "schema", returns(LastSubmission))]
LastSubmittedMeasurement {},
}
#[cw_serde]
pub struct MigrateMsg {
//
}
@@ -1,258 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Env, Timestamp};
use nym_contracts_common::Percent;
pub type EpochId = u32;
pub type NodeId = u32;
#[cw_serde]
pub struct LastSubmission {
pub block_height: u64,
pub block_time: Timestamp,
// not as relevant, but might as well store it
pub data: Option<LastSubmittedData>,
}
#[cw_serde]
pub struct LastSubmittedData {
pub sender: Addr,
pub epoch_id: EpochId,
pub data: NodePerformance,
}
#[cw_serde]
pub struct NetworkMonitorDetails {
pub address: Addr,
pub authorised_by: Addr,
pub authorised_at_height: u64,
}
impl NetworkMonitorDetails {
pub fn retire(self, env: &Env, sender: &Addr) -> RetiredNetworkMonitor {
RetiredNetworkMonitor {
details: self,
retired_by: sender.clone(),
retired_at_height: env.block.height,
}
}
}
#[cw_serde]
pub struct RetiredNetworkMonitor {
pub details: NetworkMonitorDetails,
pub retired_by: Addr,
pub retired_at_height: u64,
}
#[cw_serde]
#[derive(Copy)]
pub struct NodePerformance {
#[serde(rename = "n")]
pub node_id: NodeId,
// note: value is rounded to 2 decimal places.
#[serde(rename = "p")]
pub performance: Percent,
}
#[cw_serde]
pub struct NetworkMonitorSubmissionMetadata {
pub last_submitted_epoch_id: EpochId,
pub last_submitted_node_id: NodeId,
}
// the internal values are always sorted
#[cw_serde]
pub struct NodeResults(Vec<Percent>);
impl NodeResults {
pub fn new(initial: Percent) -> NodeResults {
NodeResults(vec![initial.round_to_two_decimal_places()])
}
// ASSUMPTION: number of NM will be relatively small, so loading the whole vector of values
// to insert new one and resave is cheap
pub fn insert_new(&mut self, result: Percent) {
let result = result.round_to_two_decimal_places();
let pos = self.0.binary_search(&result).unwrap_or_else(|e| e);
self.0.insert(pos, result);
}
// SAFETY: there are no codepaths that allow constructing empty struct
pub fn median(&self) -> Percent {
let len = self.0.len();
if len % 2 == 1 {
// odd number of elements: return the middle one
self.0[len / 2]
} else {
// even number: average the two middle elements
let mid1 = self.0[len / 2 - 1];
let mid2 = self.0[len / 2];
mid1.average(&mid2).round_to_two_decimal_places()
}
}
pub fn inner(&self) -> &[Percent] {
&self.0
}
}
#[cw_serde]
pub struct NodePerformanceResponse {
pub performance: Option<Percent>,
}
#[cw_serde]
pub struct NodeMeasurementsResponse {
pub measurements: Option<NodeResults>,
}
#[cw_serde]
#[derive(Copy)]
pub struct EpochNodePerformance {
pub epoch: EpochId,
pub performance: Option<Percent>,
}
#[cw_serde]
pub struct NodePerformancePagedResponse {
pub node_id: NodeId,
pub performance: Vec<EpochNodePerformance>,
pub start_next_after: Option<EpochId>,
}
#[cw_serde]
pub struct EpochPerformancePagedResponse {
pub epoch_id: EpochId,
pub performance: Vec<NodePerformance>,
pub start_next_after: Option<NodeId>,
}
#[cw_serde]
pub struct NodeMeasurement {
pub node_id: NodeId,
pub measurements: NodeResults,
}
#[cw_serde]
pub struct EpochMeasurementsPagedResponse {
pub epoch_id: EpochId,
pub measurements: Vec<NodeMeasurement>,
pub start_next_after: Option<NodeId>,
}
#[cw_serde]
#[derive(Copy)]
pub struct HistoricalPerformance {
pub epoch_id: EpochId,
pub node_id: NodeId,
pub performance: Percent,
}
#[cw_serde]
pub struct FullHistoricalPerformancePagedResponse {
pub performance: Vec<HistoricalPerformance>,
pub start_next_after: Option<(EpochId, NodeId)>,
}
#[cw_serde]
pub struct NetworkMonitorInformation {
pub details: NetworkMonitorDetails,
pub current_submission_metadata: NetworkMonitorSubmissionMetadata,
}
#[cw_serde]
pub struct NetworkMonitorResponse {
pub info: Option<NetworkMonitorInformation>,
}
#[cw_serde]
pub struct NetworkMonitorsPagedResponse {
pub info: Vec<NetworkMonitorInformation>,
pub start_next_after: Option<String>,
}
#[cw_serde]
pub struct RetiredNetworkMonitorsPagedResponse {
pub info: Vec<RetiredNetworkMonitor>,
pub start_next_after: Option<String>,
}
#[cw_serde]
pub struct RemoveEpochMeasurementsResponse {
pub additional_entries_to_remove_remaining: bool,
}
#[cw_serde]
#[derive(Default)]
pub struct BatchSubmissionResult {
pub accepted_scores: u64,
pub non_existent_nodes: Vec<NodeId>,
}
#[cfg(test)]
mod tests {
use super::*;
fn p(raw: impl AsRef<str>) -> Percent {
raw.as_ref().parse().unwrap()
}
fn ps(raw: &[&str]) -> Vec<Percent> {
raw.iter().map(p).collect()
}
#[test]
fn node_results_insertion() {
let initial = NodeResults::new(p("0.5"));
let mut smaller = initial.clone();
let mut greater = initial.clone();
smaller.insert_new(p("0.4"));
greater.insert_new(p("0.6"));
assert_eq!(smaller.0, ps(&["0.4", "0.5"]));
assert_eq!(greater.0, ps(&["0.5", "0.6"]));
let mut another = NodeResults(ps(&["0.1", "0.4", "0.5", "0.6", "0.6", "1.0"]));
another.insert_new(p("0.6"));
another.insert_new(p("0.2"));
another.insert_new(p("0.7"));
another.insert_new(p("0.3"));
another.insert_new(p("0.3"));
another.insert_new(p("0.55"));
assert_eq!(
another.0,
ps(&[
"0.1", "0.2", "0.3", "0.3", "0.4", "0.5", "0.55", "0.6", "0.6", "0.6", "0.7", "1.0"
])
);
}
#[test]
fn node_results_median() {
let results = NodeResults(ps(&["0.1"]));
assert_eq!(results.median(), p("0.1"));
let results = NodeResults(ps(&["0.1", "0.2"]));
assert_eq!(results.median(), p("0.15"));
let results = NodeResults(ps(&["0.1", "0.2", "0.3"]));
assert_eq!(results.median(), p("0.2"));
let results = NodeResults(ps(&["0.1", "0.2", "0.3", "0.4"]));
assert_eq!(results.median(), p("0.25"));
let results = NodeResults(ps(&["0.1", "0.2", "0.3", "0.4", "0.5"]));
assert_eq!(results.median(), p("0.3"));
let results = NodeResults(ps(&["0", "0", "1", "1", "1", "1", "1"]));
assert_eq!(results.median(), p("1"));
}
}

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