Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f766549e8 | |||
| 98131d1277 | |||
| 42c2d4de7a | |||
| 688ffaf59a | |||
| 270d065661 | |||
| 25d66b5241 | |||
| a5d8a5071f | |||
| 6c51696cfd | |||
| f2196366e1 | |||
| ac22a9d4d9 | |||
| d77bcbb241 | |||
| a6f0fbff3d | |||
| 07e6d7750a | |||
| fcce6b435c | |||
| c7a66fc451 | |||
| 6dc7fc92bd | |||
| 215f927f0d | |||
| 7477337807 | |||
| 44db2b8d71 | |||
| 3260ba84fe | |||
| 89cddc62cc | |||
| fe3b47f103 | |||
| 636bbd9d12 | |||
| 2a338458b9 | |||
| ee2026e53d | |||
| 8f2cb95ffa | |||
| 79ef88dd49 | |||
| 216b32811c | |||
| baf7178dc8 | |||
| 00cdc5010a | |||
| 04bf153701 | |||
| e6eb83f350 | |||
| e8eb031fbd | |||
| 0ce5ba2668 | |||
| e8d601df67 | |||
| ca6b09ce8d | |||
| b8e27c67d9 | |||
| 8dbb72296b | |||
| c2893e5ca0 | |||
| 0489e5aa65 | |||
| eda8b6bf85 | |||
| a1aff09b91 | |||
| 5de218bca9 | |||
| 50960e7bf3 | |||
| 723e729f3b |
Generated
+183
-49
@@ -2111,6 +2111,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"curve25519-dalek-derive",
|
||||
"digest 0.10.7",
|
||||
"fiat-crypto",
|
||||
"platforms",
|
||||
"rustc_version 0.4.0",
|
||||
@@ -2706,6 +2707,20 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-dalek"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980"
|
||||
dependencies = [
|
||||
"curve25519-dalek 4.1.0",
|
||||
"ed25519 2.2.2",
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"sha2 0.10.8",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-zebra"
|
||||
version = "3.1.0"
|
||||
@@ -2868,7 +2883,7 @@ dependencies = [
|
||||
"libp2p 0.51.3",
|
||||
"libp2p-identity",
|
||||
"log",
|
||||
"lru 0.10.0",
|
||||
"lru 0.10.1",
|
||||
"nym-config",
|
||||
"nym-ephemera-common",
|
||||
"nym-task",
|
||||
@@ -3847,6 +3862,20 @@ dependencies = [
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-api-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"url",
|
||||
"wasmtimer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.5"
|
||||
@@ -3858,6 +3887,12 @@ dependencies = [
|
||||
"pin-project-lite 0.2.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.8.0"
|
||||
@@ -4109,6 +4144,7 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4244,7 +4280,7 @@ dependencies = [
|
||||
"socket2 0.4.9",
|
||||
"widestring",
|
||||
"winapi",
|
||||
"winreg",
|
||||
"winreg 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4610,7 +4646,7 @@ source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c
|
||||
dependencies = [
|
||||
"asn1_der",
|
||||
"bs58 0.4.0",
|
||||
"ed25519-dalek",
|
||||
"ed25519-dalek 1.0.1",
|
||||
"either",
|
||||
"fnv",
|
||||
"futures",
|
||||
@@ -4777,12 +4813,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libp2p-identity"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1"
|
||||
checksum = "276bb57e7af15d8f100d3c11cbdd32c6752b7eef4ba7a18ecf464972c07abcce"
|
||||
dependencies = [
|
||||
"bs58 0.4.0",
|
||||
"ed25519-dalek",
|
||||
"ed25519-dalek 2.0.0",
|
||||
"log",
|
||||
"multiaddr 0.17.1",
|
||||
"multihash",
|
||||
@@ -5384,9 +5420,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e"
|
||||
checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670"
|
||||
dependencies = [
|
||||
"hashbrown 0.13.2",
|
||||
]
|
||||
@@ -5535,13 +5571,14 @@ dependencies = [
|
||||
name = "mix-fetch-wasm"
|
||||
version = "1.2.0-rc.10"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
"http-api-client",
|
||||
"js-sys",
|
||||
"nym-ordered-buffer",
|
||||
"nym-service-providers-common",
|
||||
"nym-socks5-requests",
|
||||
"rand 0.7.3",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"thiserror",
|
||||
@@ -5968,6 +6005,7 @@ dependencies = [
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-multisig-contract-common",
|
||||
"nym-name-service-common",
|
||||
"nym-node-requests",
|
||||
"nym-node-tester-utils",
|
||||
"nym-pemstore",
|
||||
"nym-service-provider-directory-common",
|
||||
@@ -5978,7 +6016,6 @@ dependencies = [
|
||||
"nym-vesting-contract-common",
|
||||
"okapi",
|
||||
"pin-project",
|
||||
"pretty_env_logger",
|
||||
"rand 0.7.3",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
@@ -6012,6 +6049,7 @@ dependencies = [
|
||||
"getset",
|
||||
"nym-coconut-interface",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-node-requests",
|
||||
"schemars",
|
||||
"serde",
|
||||
"ts-rs",
|
||||
@@ -6045,12 +6083,14 @@ dependencies = [
|
||||
"opentelemetry",
|
||||
"opentelemetry-jaeger",
|
||||
"pretty_env_logger",
|
||||
"schemars",
|
||||
"semver 0.11.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing-opentelemetry",
|
||||
"tracing-subscriber",
|
||||
"tracing-tree",
|
||||
"utoipa",
|
||||
"vergen",
|
||||
]
|
||||
|
||||
@@ -6390,7 +6430,7 @@ dependencies = [
|
||||
"cipher 0.4.4",
|
||||
"ctr 0.9.2",
|
||||
"digest 0.10.7",
|
||||
"ed25519-dalek",
|
||||
"ed25519-dalek 1.0.1",
|
||||
"generic-array 0.14.7",
|
||||
"hkdf 0.12.3",
|
||||
"hmac 0.12.1",
|
||||
@@ -6507,6 +6547,7 @@ dependencies = [
|
||||
"nym-mixnode-common",
|
||||
"nym-network-defaults",
|
||||
"nym-network-requester",
|
||||
"nym-node",
|
||||
"nym-pemstore",
|
||||
"nym-sphinx",
|
||||
"nym-statistics-common",
|
||||
@@ -6598,21 +6639,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-http-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytecodec",
|
||||
"bytes",
|
||||
"http",
|
||||
"httpcodec",
|
||||
"nym-ordered-buffer",
|
||||
"nym-service-providers-common",
|
||||
"nym-socks5-requests",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-inclusion-probability"
|
||||
version = "0.1.0"
|
||||
@@ -6837,6 +6863,57 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"bytes",
|
||||
"colored",
|
||||
"fastrand 2.0.0",
|
||||
"hmac 0.12.1",
|
||||
"hyper",
|
||||
"mime",
|
||||
"nym-config",
|
||||
"nym-crypto",
|
||||
"nym-node-requests",
|
||||
"nym-task",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"utoipa",
|
||||
"utoipa-swagger-ui",
|
||||
"x25519-dalek 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.4",
|
||||
"hmac 0.12.1",
|
||||
"http-api-client",
|
||||
"nym-bin-common",
|
||||
"nym-crypto",
|
||||
"rand 0.7.3",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"utoipa",
|
||||
"x25519-dalek 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-tester-utils"
|
||||
version = "0.1.0"
|
||||
@@ -7326,6 +7403,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"bs58 0.4.0",
|
||||
"log",
|
||||
"nym-api-requests",
|
||||
"nym-bin-common",
|
||||
"nym-config",
|
||||
"nym-crypto",
|
||||
@@ -7386,6 +7464,7 @@ dependencies = [
|
||||
"eyre",
|
||||
"flate2",
|
||||
"futures",
|
||||
"http-api-client",
|
||||
"itertools",
|
||||
"log",
|
||||
"nym-api-requests",
|
||||
@@ -8712,9 +8791,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "refinery"
|
||||
version = "0.8.10"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb0436d0dd7bd8d4fce1e828751fa79742b08e35f27cfea7546f8a322b5ef24"
|
||||
checksum = "529664dbccc0a296947615c997a857912d72d1c44be1fafb7bae54ecfa7a8c24"
|
||||
dependencies = [
|
||||
"refinery-core",
|
||||
"refinery-macros",
|
||||
@@ -8722,9 +8801,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "refinery-core"
|
||||
version = "0.8.10"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19206547cd047e8f4dfa6b20c30d3ecaf24be05841b6aa0aa926a47a3d0662bb"
|
||||
checksum = "e895cb870cf06e92318cbbeb701f274d022d5ca87a16fa8244e291cd035ef954"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cfg-if",
|
||||
@@ -8743,9 +8822,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "refinery-macros"
|
||||
version = "0.8.10"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d94d4b9241859ba19eaa5c04c86e782eb3aa0aae2c5868e0cfa90c856e58a174"
|
||||
checksum = "123e8b80f8010c3ae38330c81e76938fc7adf6cdbfbaad20295bb8c22718b4f1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -8800,9 +8879,9 @@ checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.18"
|
||||
version = "0.11.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
|
||||
checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
|
||||
dependencies = [
|
||||
"base64 0.21.4",
|
||||
"bytes",
|
||||
@@ -8825,6 +8904,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-socks",
|
||||
@@ -8833,7 +8913,7 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg",
|
||||
"winreg 0.50.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9639,6 +9719,19 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574"
|
||||
dependencies = [
|
||||
"indexmap 2.0.0",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.9.8"
|
||||
@@ -9757,9 +9850,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
checksum = "54ac45299ccbd390721be55b412d41931911f654fa99e2cb8bfb57184b2061fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
@@ -10515,18 +10608,18 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.44"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
|
||||
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.44"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
||||
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -10885,6 +10978,31 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-range-header",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project-lite 0.2.12",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.2"
|
||||
@@ -11316,6 +11434,12 @@ dependencies = [
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
|
||||
|
||||
[[package]]
|
||||
name = "unsigned-varint"
|
||||
version = "0.7.1"
|
||||
@@ -11395,11 +11519,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "utoipa"
|
||||
version = "3.3.0"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ae74ef183fae36d650f063ae7bde1cacbe1cd7e72b617cbe1e985551878b98"
|
||||
checksum = "d82b1bc5417102a73e8464c686eef947bdfb99fcdfc0a4f228e81afa9526470a"
|
||||
dependencies = [
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.0.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa-gen",
|
||||
@@ -11407,11 +11531,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-gen"
|
||||
version = "3.3.0"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ea8ac818da7e746a63285594cce8a96f5e00ee31994e655bd827569cb8b137b"
|
||||
checksum = "05d96dcd6fc96f3df9b3280ef480770af1b7c5d14bc55192baa9b067976d920c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -11421,11 +11544,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-swagger-ui"
|
||||
version = "3.1.3"
|
||||
version = "3.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "062bba5a3568e126ac72049a63254f4cb1da2eb713db0c1ab2a4c76be191db8c"
|
||||
checksum = "84614caa239fb25b2bb373a52859ffd94605ceb256eeb1d63436325cf81e3653"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"axum",
|
||||
"mime_guess",
|
||||
"regex",
|
||||
"rust-embed",
|
||||
@@ -12236,6 +12360,16 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "with_builtin_macros"
|
||||
version = "0.0.3"
|
||||
|
||||
+12
-3
@@ -46,7 +46,7 @@ members = [
|
||||
"common/crypto",
|
||||
"common/dkg",
|
||||
"common/execute",
|
||||
"common/http-requests",
|
||||
"common/http-api-client",
|
||||
"common/inclusion-probability",
|
||||
"common/ledger",
|
||||
"common/mixnode-common",
|
||||
@@ -93,6 +93,8 @@ members = [
|
||||
"nym-api",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-node",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-outfox",
|
||||
"tools/internal/ssl-inject",
|
||||
"tools/internal/sdk-version-bump",
|
||||
@@ -129,6 +131,8 @@ license = "Apache-2.0"
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.71"
|
||||
async-trait = "0.1.68"
|
||||
axum = "0.6.20"
|
||||
base64 = "0.21.4"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
cfg-if = "1.0.0"
|
||||
cosmwasm-derive = "=1.3.0"
|
||||
@@ -151,22 +155,27 @@ dotenvy = "0.15.6"
|
||||
futures = "0.3.28"
|
||||
generic-array = "0.14.7"
|
||||
getrandom = "0.2.10"
|
||||
hyper = "0.14.27"
|
||||
k256 = "0.13"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
once_cell = "1.7.2"
|
||||
parking_lot = "0.12.1"
|
||||
rand = "0.8.5"
|
||||
reqwest = "0.11.18"
|
||||
reqwest = "0.11.22"
|
||||
schemars = "0.8.1"
|
||||
serde = "1.0.152"
|
||||
serde_json = "1.0.91"
|
||||
tap = "1.0.1"
|
||||
tendermint-rpc = "0.32" # same version as used by cosmrs
|
||||
thiserror = "1.0.38"
|
||||
thiserror = "1.0.48"
|
||||
tokio = "1.24.1"
|
||||
tokio-tungstenite = "0.20.1"
|
||||
tracing = "0.1.37"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
ts-rs = "7.0.0"
|
||||
utoipa = "3.5.0"
|
||||
utoipa-swagger-ui = "3.1.5"
|
||||
url = "2.4"
|
||||
zeroize = "1.6.0"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
bip39 = { workspace = true }
|
||||
rand = "0.7.3"
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
nym-coconut-interface = { path = "../coconut-interface" }
|
||||
|
||||
@@ -15,6 +15,7 @@ clap_complete_fig = "4.0"
|
||||
log = { workspace = true }
|
||||
pretty_env_logger = "0.4.0"
|
||||
semver = "0.11"
|
||||
schemars = { workspace = true, features = ["preserve_order"], optional = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
|
||||
@@ -29,6 +30,7 @@ opentelemetry-jaeger = { version = "0.18.0", optional = true, features = [
|
||||
"isahc_collector_client",
|
||||
] }
|
||||
tracing-opentelemetry = { version = "0.19.0", optional = true }
|
||||
utoipa = { workspace = true, optional = true }
|
||||
opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
|
||||
|
||||
|
||||
@@ -42,7 +44,9 @@ vergen = { version = "=7.4.3", default-features = false, features = [
|
||||
|
||||
[features]
|
||||
default = []
|
||||
openapi = ["utoipa"]
|
||||
output_format = ["serde_json"]
|
||||
bin_info_schema = ["schemars"]
|
||||
tracing = [
|
||||
"tracing-subscriber",
|
||||
"tracing-tree",
|
||||
|
||||
@@ -81,6 +81,8 @@ impl BinaryBuildInformation {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
#[cfg_attr(feature = "bin_info_schema", derive(schemars::JsonSchema))]
|
||||
pub struct BinaryBuildInformationOwned {
|
||||
/// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable.
|
||||
pub binary_name: String,
|
||||
|
||||
@@ -69,7 +69,7 @@ impl NymApiTopologyProvider {
|
||||
Ok(mixes) => mixes,
|
||||
};
|
||||
|
||||
let gateways = match self.validator_client.get_cached_gateways().await {
|
||||
let gateways = match self.validator_client.get_cached_described_gateways().await {
|
||||
Err(err) => {
|
||||
error!("failed to get network gateways - {err}");
|
||||
return None;
|
||||
|
||||
@@ -280,29 +280,24 @@ impl GatewayEndpointConfig {
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
|
||||
}
|
||||
|
||||
pub fn from_node(node: nym_topology::gateway::Node, use_tls: bool) -> Self {
|
||||
// TODO: in the future this shall return a Result and explicit `use_tls` will be removed in favour of the tls info being available on the struct
|
||||
if use_tls {
|
||||
Self::from_topology_node_tls(node)
|
||||
pub fn from_node(
|
||||
node: nym_topology::gateway::Node,
|
||||
must_use_tls: bool,
|
||||
) -> Result<Self, ClientCoreError> {
|
||||
let gateway_listener = if must_use_tls {
|
||||
node.clients_address_tls()
|
||||
.ok_or(ClientCoreError::UnsupportedWssProtocol {
|
||||
gateway: node.identity_key.to_base58_string(),
|
||||
})?
|
||||
} else {
|
||||
Self::from_topology_node_no_tls(node)
|
||||
}
|
||||
}
|
||||
node.clients_address()
|
||||
};
|
||||
|
||||
pub fn from_topology_node_no_tls(node: nym_topology::gateway::Node) -> Self {
|
||||
GatewayEndpointConfig {
|
||||
Ok(GatewayEndpointConfig {
|
||||
gateway_id: node.identity_key.to_base58_string(),
|
||||
gateway_listener: node.clients_address(),
|
||||
gateway_listener,
|
||||
gateway_owner: node.owner,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_topology_node_tls(node: nym_topology::gateway::Node) -> Self {
|
||||
GatewayEndpointConfig {
|
||||
gateway_id: node.identity_key.to_base58_string(),
|
||||
gateway_listener: node.clients_address_tls(),
|
||||
gateway_owner: node.owner,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,12 @@ pub enum ClientCoreError {
|
||||
|
||||
#[error("this client has performed gateway initialisation in another session")]
|
||||
NoInitClientPresent,
|
||||
|
||||
#[error("there are no gateways supporting the wss protocol available")]
|
||||
NoWssGateways,
|
||||
|
||||
#[error("the specified gateway '{gateway}' does not support the wss protocol")]
|
||||
UnsupportedWssProtocol { gateway: String },
|
||||
}
|
||||
|
||||
/// Set of messages that the client can send to listeners via the task manager
|
||||
|
||||
@@ -174,7 +174,10 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
|
||||
pub async fn choose_gateway_by_latency<R: Rng>(
|
||||
rng: &mut R,
|
||||
gateways: &[gateway::Node],
|
||||
must_use_tls: bool,
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
let gateways = filter_by_tls(gateways, must_use_tls)?;
|
||||
|
||||
info!(
|
||||
"choosing gateway by latency, pinging {} gateways ...",
|
||||
gateways.len()
|
||||
@@ -210,28 +213,57 @@ pub async fn choose_gateway_by_latency<R: Rng>(
|
||||
Ok(chosen.gateway.clone())
|
||||
}
|
||||
|
||||
fn filter_by_tls(
|
||||
gateways: &[gateway::Node],
|
||||
must_use_tls: bool,
|
||||
) -> Result<Vec<&gateway::Node>, ClientCoreError> {
|
||||
if must_use_tls {
|
||||
let filtered = gateways
|
||||
.iter()
|
||||
.filter(|g| g.clients_wss_port.is_some())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if filtered.is_empty() {
|
||||
return Err(ClientCoreError::NoWssGateways);
|
||||
}
|
||||
|
||||
Ok(filtered)
|
||||
} else {
|
||||
Ok(gateways.iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn uniformly_random_gateway<R: Rng>(
|
||||
rng: &mut R,
|
||||
gateways: &[gateway::Node],
|
||||
must_use_tls: bool,
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
gateways
|
||||
filter_by_tls(gateways, must_use_tls)?
|
||||
.choose(rng)
|
||||
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
|
||||
.cloned()
|
||||
.map(|&r| r.clone())
|
||||
}
|
||||
|
||||
pub(super) fn get_specified_gateway(
|
||||
gateway_identity: IdentityKeyRef,
|
||||
gateways: &[gateway::Node],
|
||||
must_use_tls: bool,
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||
|
||||
gateways
|
||||
let gateway = gateways
|
||||
.iter()
|
||||
.find(|gateway| gateway.identity_key == user_gateway)
|
||||
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
|
||||
.cloned()
|
||||
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
|
||||
|
||||
if must_use_tls && gateway.clients_wss_port.is_none() {
|
||||
return Err(ClientCoreError::UnsupportedWssProtocol {
|
||||
gateway: gateway_identity.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(gateway.clone())
|
||||
}
|
||||
|
||||
pub(super) async fn register_with_gateway(
|
||||
|
||||
@@ -108,19 +108,20 @@ where
|
||||
|
||||
let gateway_details = match selection_specification {
|
||||
GatewaySelectionSpecification::UniformRemote { must_use_tls } => {
|
||||
let gateway = uniformly_random_gateway(&mut rng, &available_gateways)?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
|
||||
let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
|
||||
}
|
||||
GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => {
|
||||
let gateway = choose_gateway_by_latency(&mut rng, &available_gateways).await?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
|
||||
let gateway =
|
||||
choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
|
||||
}
|
||||
GatewaySelectionSpecification::Specified {
|
||||
must_use_tls,
|
||||
identity,
|
||||
} => {
|
||||
let gateway = get_specified_gateway(&identity, &available_gateways)?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
|
||||
let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
|
||||
}
|
||||
GatewaySelectionSpecification::Custom {
|
||||
gateway_identity,
|
||||
|
||||
@@ -24,6 +24,7 @@ nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
http-api-client = { path = "../../../common/http-api-client"}
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use nym_api_requests::models::MixNodeBondAnnotated;
|
||||
use nym_api_requests::models::{DescribedGateway, MixNodeBondAnnotated};
|
||||
use nym_api_requests::models::{
|
||||
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse,
|
||||
@@ -19,6 +19,7 @@ use nym_api_requests::models::{
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use url::Url;
|
||||
|
||||
pub use crate::nym_api::NymApiClientExt;
|
||||
pub use nym_mixnet_contract_common::{
|
||||
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, MixId,
|
||||
};
|
||||
@@ -147,7 +148,7 @@ impl Client<ReqwestRpcClient> {
|
||||
|
||||
impl<C> Client<C> {
|
||||
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self {
|
||||
let nym_api_client = nym_api::Client::new(config.api_url.clone());
|
||||
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
|
||||
|
||||
Client {
|
||||
nym_api: nym_api_client,
|
||||
@@ -161,7 +162,7 @@ impl<C, S> Client<C, S> {
|
||||
where
|
||||
S: OfflineSigner,
|
||||
{
|
||||
let nym_api_client = nym_api::Client::new(config.api_url.clone());
|
||||
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
|
||||
|
||||
Client {
|
||||
nym_api: nym_api_client,
|
||||
@@ -177,7 +178,7 @@ impl<C, S> Client<C, S> {
|
||||
}
|
||||
|
||||
pub fn change_nym_api(&mut self, new_endpoint: Url) {
|
||||
self.nym_api.change_url(new_endpoint)
|
||||
self.nym_api.change_base_url(new_endpoint)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
@@ -241,7 +242,7 @@ pub struct NymApiClient {
|
||||
|
||||
impl NymApiClient {
|
||||
pub fn new(api_url: Url) -> Self {
|
||||
let nym_api = nym_api::Client::new(api_url);
|
||||
let nym_api = nym_api::Client::new(api_url, None);
|
||||
|
||||
NymApiClient { nym_api }
|
||||
}
|
||||
@@ -251,7 +252,7 @@ impl NymApiClient {
|
||||
}
|
||||
|
||||
pub fn change_nym_api(&mut self, new_endpoint: Url) {
|
||||
self.nym_api.change_url(new_endpoint);
|
||||
self.nym_api.change_base_url(new_endpoint);
|
||||
}
|
||||
|
||||
pub async fn get_cached_active_mixnodes(
|
||||
@@ -274,6 +275,12 @@ impl NymApiClient {
|
||||
Ok(self.nym_api.get_gateways().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_described_gateways(
|
||||
&self,
|
||||
) -> Result<Vec<DescribedGateway>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_gateways_described().await?)
|
||||
}
|
||||
|
||||
pub async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use http_api_client::HttpClientError;
|
||||
use nym_api_requests::models::RequestError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NymAPIError {
|
||||
#[error("There was an issue with the REST request - {source}")]
|
||||
ReqwestClientError {
|
||||
#[from]
|
||||
source: reqwest::Error,
|
||||
},
|
||||
|
||||
#[error("Not found")]
|
||||
NotFound,
|
||||
|
||||
#[error("Request failed with error message - {0}")]
|
||||
GenericRequestFailure(String),
|
||||
|
||||
#[error("The nym API has failed to resolve our request. It returned status code {status} and additional error message: {}", error.message())]
|
||||
ApiRequestFailure { status: u16, error: RequestError },
|
||||
}
|
||||
pub type NymAPIError = HttpClientError<RequestError>;
|
||||
|
||||
@@ -3,140 +3,38 @@
|
||||
|
||||
use crate::nym_api::error::NymAPIError;
|
||||
use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||
use async_trait::async_trait;
|
||||
use http_api_client::{ApiClient, NO_PARAMS};
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
ComputeRewardEstParam, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
|
||||
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
|
||||
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse,
|
||||
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse, UptimeResponse,
|
||||
};
|
||||
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
|
||||
use nym_name_service_common::response::NamesListResponse;
|
||||
use nym_service_provider_directory_common::response::ServicesListResponse;
|
||||
use reqwest::{Response, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
pub mod error;
|
||||
pub mod routes;
|
||||
|
||||
type PathSegments<'a> = &'a [&'a str];
|
||||
type Params<'a, K, V> = &'a [(K, V)];
|
||||
pub use http_api_client::Client;
|
||||
|
||||
const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Client {
|
||||
url: Url,
|
||||
reqwest_client: reqwest::Client,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(url: Url) -> Self {
|
||||
let reqwest_client = reqwest::Client::new();
|
||||
Self {
|
||||
url,
|
||||
reqwest_client,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_url(&mut self, new_url: Url) {
|
||||
self.url = new_url
|
||||
}
|
||||
|
||||
pub fn current_url(&self) -> &Url {
|
||||
&self.url
|
||||
}
|
||||
|
||||
async fn send_get_request<K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<Response, NymAPIError>
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = create_api_url(&self.url, path, params);
|
||||
Ok(self.reqwest_client.get(url).send().await?)
|
||||
}
|
||||
|
||||
async fn query_nym_api<T, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, NymAPIError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let res = self.send_get_request(path, params).await?;
|
||||
if res.status().is_success() {
|
||||
Ok(res.json().await?)
|
||||
} else if res.status() == StatusCode::NOT_FOUND {
|
||||
Err(NymAPIError::NotFound)
|
||||
} else {
|
||||
Err(NymAPIError::GenericRequestFailure(res.text().await?))
|
||||
}
|
||||
}
|
||||
|
||||
// This works for endpoints returning Result<Json<T>, ErrorResponse>
|
||||
async fn query_nym_api_fallible<T, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, NymAPIError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let res = self.send_get_request(path, params).await?;
|
||||
let status = res.status();
|
||||
if res.status().is_success() {
|
||||
Ok(res.json().await?)
|
||||
} else {
|
||||
let request_error: RequestError = res.json().await?;
|
||||
Err(NymAPIError::ApiRequestFailure {
|
||||
status: status.as_u16(),
|
||||
error: request_error,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn post_nym_api<B, T, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
json_body: &B,
|
||||
) -> Result<T, NymAPIError>
|
||||
where
|
||||
B: Serialize + ?Sized,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = create_api_url(&self.url, path, params);
|
||||
let response = self.reqwest_client.post(url).json(json_body).send().await?;
|
||||
if response.status().is_success() {
|
||||
Ok(response.json().await?)
|
||||
} else {
|
||||
Err(NymAPIError::GenericRequestFailure(response.text().await?))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait NymApiClientExt: ApiClient {
|
||||
async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.get_json(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -148,8 +46,8 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -161,10 +59,10 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes_detailed_unfiltered(
|
||||
async fn get_mixnodes_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -176,23 +74,29 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
|
||||
self.get_json(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
async fn get_gateways_described(&self) -> Result<Vec<DescribedGateway>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::GATEWAYS, routes::DESCRIBED],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_active_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
async fn get_active_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -205,19 +109,19 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_report(
|
||||
async fn get_mixnode_report(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixnodeStatusReportResponse, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -230,11 +134,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateway_report(
|
||||
async fn get_gateway_report(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<GatewayStatusReportResponse, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -247,11 +151,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_history(
|
||||
async fn get_mixnode_history(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixnodeUptimeHistoryResponse, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -264,11 +168,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateway_history(
|
||||
async fn get_gateway_history(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<GatewayUptimeHistoryResponse, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -281,10 +185,10 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarded_mixnodes_detailed(
|
||||
async fn get_rewarded_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -297,13 +201,13 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateway_core_status_count(
|
||||
async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<GatewayCoreStatusResponse, NymAPIError> {
|
||||
if let Some(since) = since {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -315,7 +219,7 @@ impl Client {
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -328,13 +232,13 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_core_status_count(
|
||||
async fn get_mixnode_core_status_count(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
since: Option<i64>,
|
||||
) -> Result<MixnodeCoreStatusResponse, NymAPIError> {
|
||||
if let Some(since) = since {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -346,7 +250,7 @@ impl Client {
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -360,11 +264,11 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_status(
|
||||
async fn get_mixnode_status(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixnodeStatusResponse, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -377,11 +281,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_reward_estimation(
|
||||
async fn get_mixnode_reward_estimation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<RewardEstimationResponse, NymAPIError> {
|
||||
self.query_nym_api_fallible(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -394,12 +298,12 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn compute_mixnode_reward_estimation(
|
||||
async fn compute_mixnode_reward_estimation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
request_body: &ComputeRewardEstParam,
|
||||
) -> Result<RewardEstimationResponse, NymAPIError> {
|
||||
self.post_nym_api(
|
||||
self.post_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -413,11 +317,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_stake_saturation(
|
||||
async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<StakeSaturationResponse, NymAPIError> {
|
||||
self.query_nym_api_fallible(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -430,11 +334,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_inclusion_probability(
|
||||
async fn get_mixnode_inclusion_probability(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<InclusionProbabilityResponse, NymAPIError> {
|
||||
self.query_nym_api_fallible(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -447,11 +351,8 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_avg_uptime(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<UptimeResponse, NymAPIError> {
|
||||
self.query_nym_api_fallible(
|
||||
async fn get_mixnode_avg_uptime(&self, mix_id: MixId) -> Result<UptimeResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -464,11 +365,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
async fn blind_sign(
|
||||
&self,
|
||||
request_body: &BlindSignRequestBody,
|
||||
) -> Result<BlindedSignatureResponse, NymAPIError> {
|
||||
self.post_nym_api(
|
||||
self.post_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
@@ -481,11 +382,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
) -> Result<VerifyCredentialResponse, NymAPIError> {
|
||||
self.post_nym_api(
|
||||
self.post_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
@@ -498,118 +399,20 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
|
||||
async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
|
||||
log::trace!("Getting service providers");
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
|
||||
self.get_json(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
//pub async fn get_registered_names(&self) -> Result<Vec<NameEntry>, NymAPIError> {
|
||||
pub async fn get_registered_names(&self) -> Result<NamesListResponse, NymAPIError> {
|
||||
//async fn get_registered_names(&self) -> Result<Vec<NameEntry>, NymAPIError> {
|
||||
async fn get_registered_names(&self) -> Result<NamesListResponse, NymAPIError> {
|
||||
log::trace!("Getting registered names");
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::REGISTERED_NAMES], NO_PARAMS)
|
||||
self.get_json(&[routes::API_VERSION, routes::REGISTERED_NAMES], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// utility function that should solve the double slash problem in validator API forever.
|
||||
fn create_api_url<K: AsRef<str>, V: AsRef<str>>(
|
||||
base: &Url,
|
||||
segments: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Url {
|
||||
let mut url = base.clone();
|
||||
let mut path_segments = url
|
||||
.path_segments_mut()
|
||||
.expect("provided validator url does not have a base!");
|
||||
for segment in segments {
|
||||
let segment = segment.strip_prefix('/').unwrap_or(segment);
|
||||
let segment = segment.strip_suffix('/').unwrap_or(segment);
|
||||
|
||||
path_segments.push(segment);
|
||||
}
|
||||
// I don't understand why compiler couldn't figure out that it's no longer used
|
||||
// and can be dropped
|
||||
drop(path_segments);
|
||||
|
||||
if !params.is_empty() {
|
||||
url.query_pairs_mut().extend_pairs(params);
|
||||
}
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn creating_api_path() {
|
||||
let base_url: Url = "http://foomp.com".parse().unwrap();
|
||||
|
||||
// works with 1 segment
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with 2 segments
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with leading slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["/foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with trailing slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with both leading and trailing slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// adds params
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar?foomp=baz",
|
||||
create_api_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
|
||||
create_api_url(
|
||||
&base_url,
|
||||
&["/foo/", "/bar/"],
|
||||
&[("arg1", "val1"), ("arg2", "val2")]
|
||||
)
|
||||
.as_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl NymApiClientExt for Client {}
|
||||
|
||||
@@ -6,6 +6,7 @@ use nym_network_defaults::NYM_API_VERSION;
|
||||
pub const API_VERSION: &str = NYM_API_VERSION;
|
||||
pub const MIXNODES: &str = "mixnodes";
|
||||
pub const GATEWAYS: &str = "gateways";
|
||||
pub const DESCRIBED: &str = "described";
|
||||
|
||||
pub const DETAILED: &str = "detailed";
|
||||
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
|
||||
|
||||
@@ -8,6 +8,6 @@ description = "Crutch library until there is proper SerDe support for coconut st
|
||||
bs58 = "0.4.0"
|
||||
getset = "0.1.1"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = "1"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-coconut = {path = "../nymcoconut" }
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
|
||||
use crate::context::QueryClientWithNyxd;
|
||||
use crate::utils::{pretty_cosmwasm_coin, show_error};
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
|
||||
use crate::context::QueryClientWithNyxd;
|
||||
use crate::utils::{pretty_decimal_with_denom, show_error};
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::nym_api::error::NymAPIError;
|
||||
|
||||
use crate::context::QueryClientWithNyxd;
|
||||
use crate::utils::show_error;
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::nym_api::error::NymAPIError;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::nym_api::error::NymAPIError;
|
||||
|
||||
use crate::context::QueryClientWithNyxd;
|
||||
use crate::utils::show_error;
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::nym_api::error::NymAPIError;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
|
||||
@@ -18,7 +18,7 @@ serde_repr = "0.1"
|
||||
|
||||
# we still have to preserve that import for `JsonSchema` for `Layer` type (since we can't use cw_serde macro due to custom serde impl)
|
||||
schemars = "0.8"
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
|
||||
serde-json-wasm = { workspace = true }
|
||||
humantime-serde = "1.1.1"
|
||||
|
||||
@@ -14,4 +14,4 @@ cosmwasm-schema = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -14,7 +14,7 @@ cw2 = { workspace = true, optional = true }
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.6.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
ts-rs = { workspace = true, optional = true}
|
||||
|
||||
[features]
|
||||
|
||||
@@ -9,7 +9,7 @@ edition = "2021"
|
||||
async-trait = { workspace = true }
|
||||
|
||||
log = { workspace = true }
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = ["sync"]}
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
|
||||
@@ -23,7 +23,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"], optional = true }
|
||||
serde_bytes = { version = "0.11.6", optional = true }
|
||||
serde_crate = { version = "1.0", optional = true, default_features = false, features = ["derive"], package = "serde" }
|
||||
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
|
||||
thiserror = "1.0.37"
|
||||
thiserror = { workspace = true }
|
||||
zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] }
|
||||
|
||||
# internal
|
||||
|
||||
@@ -145,8 +145,12 @@ impl PublicKey {
|
||||
Self::from_bytes(&bytes)
|
||||
}
|
||||
|
||||
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
|
||||
self.0.verify(message, &signature.0)
|
||||
pub fn verify<M: AsRef<[u8]>>(
|
||||
&self,
|
||||
message: M,
|
||||
signature: &Signature,
|
||||
) -> Result<(), SignatureError> {
|
||||
self.0.verify(message.as_ref(), &signature.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,16 +243,16 @@ impl PrivateKey {
|
||||
Self::from_bytes(&bytes)
|
||||
}
|
||||
|
||||
pub fn sign(&self, message: &[u8]) -> Signature {
|
||||
pub fn sign<M: AsRef<[u8]>>(&self, message: M) -> Signature {
|
||||
let expanded_secret_key = ed25519_dalek::ExpandedSecretKey::from(&self.0);
|
||||
let public_key: PublicKey = self.into();
|
||||
let sig = expanded_secret_key.sign(message, &public_key.0);
|
||||
let sig = expanded_secret_key.sign(message.as_ref(), &public_key.0);
|
||||
Signature(sig)
|
||||
}
|
||||
|
||||
/// Signs text with the provided Ed25519 private key, returning a base58 signature
|
||||
pub fn sign_text(&self, text: &str) -> String {
|
||||
let signature_bytes = self.sign(text.as_ref()).to_bytes();
|
||||
let signature_bytes = self.sign(text).to_bytes();
|
||||
bs58::encode(signature_bytes).into_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ rand = { version = "0.8.5", default-features = false}
|
||||
rand_chacha = "0.3"
|
||||
rand_core = "0.6.3"
|
||||
sha2 = "0.9"
|
||||
serde = "1.0"
|
||||
serde = { workspace = true }
|
||||
serde_derive = "1.0"
|
||||
thiserror = "1.0"
|
||||
zeroize = { version = "1.4", features = ["zeroize_derive"] }
|
||||
thiserror = { workspace = true }
|
||||
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
|
||||
nym-pemstore = { path = "../pemstore" }
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "http-api-client"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
url = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
# for request timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
|
||||
workspace = true
|
||||
features = ["tokio"]
|
||||
@@ -0,0 +1,512 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use reqwest::{IntoUrl, Response, StatusCode};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use tracing::warn;
|
||||
use url::Url;
|
||||
|
||||
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
|
||||
|
||||
pub type PathSegments<'a> = &'a [&'a str];
|
||||
pub type Params<'a, K, V> = &'a [(K, V)];
|
||||
|
||||
pub const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum HttpClientError<E: Display = String> {
|
||||
#[error("there was an issue with the REST request: {source}")]
|
||||
ReqwestClientError {
|
||||
#[from]
|
||||
source: reqwest::Error,
|
||||
},
|
||||
|
||||
#[error("provided url is malformed: {source}")]
|
||||
MalformedUrl {
|
||||
#[from]
|
||||
source: url::ParseError,
|
||||
},
|
||||
|
||||
#[error("the requested resource could not be found")]
|
||||
NotFound,
|
||||
|
||||
#[error("request failed with error message: {0}")]
|
||||
GenericRequestFailure(String),
|
||||
|
||||
#[error("the request failed with status '{status}'. no additional error message provided")]
|
||||
RequestFailure { status: StatusCode },
|
||||
|
||||
#[error("the returned response was empty. status: '{status}'")]
|
||||
EmptyResponse { status: StatusCode },
|
||||
|
||||
#[error("failed to resolve request. status: '{status}', additional error message: {error}")]
|
||||
EndpointFailure { status: StatusCode, error: E },
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[error("the request has timed out")]
|
||||
RequestTimeout,
|
||||
}
|
||||
|
||||
/// A simple extendable client wrapper for http request with extra url sanitization.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Client {
|
||||
base_url: Url,
|
||||
reqwest_client: reqwest::Client,
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
request_timeout: Duration,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
// no timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
|
||||
pub fn new(base_url: Url, timeout: Option<Duration>) -> Self {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let reqwest_client = reqwest::Client::new();
|
||||
|
||||
// TODO: we should probably be propagating the error rather than panicking,
|
||||
// but that'd break bunch of things due to type changes
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let reqwest_client = reqwest::ClientBuilder::new()
|
||||
.timeout(timeout.unwrap_or(DEFAULT_TIMEOUT))
|
||||
.user_agent(format!("nym-http-api-client/{}", env!("CARGO_PKG_VERSION")))
|
||||
.build()
|
||||
.expect("Client::new()");
|
||||
|
||||
Client {
|
||||
base_url,
|
||||
reqwest_client,
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
request_timeout: timeout.unwrap_or(DEFAULT_TIMEOUT),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_url<U, E>(url: U, timeout: Option<Duration>) -> Result<Self, HttpClientError<E>>
|
||||
where
|
||||
U: IntoUrl,
|
||||
E: Display,
|
||||
{
|
||||
// a naive check: if the provided URL does not start with http(s), add that scheme
|
||||
let str_url = url.as_str();
|
||||
|
||||
if !str_url.starts_with("http") {
|
||||
let alt = format!("http://{str_url}");
|
||||
warn!("the provided url ('{str_url}') does not contain scheme information. Changing it to '{alt}' ...");
|
||||
// TODO: or should we maybe default to https?
|
||||
Self::new_url(alt, timeout)
|
||||
} else {
|
||||
Ok(Self::new(url.into_url()?, timeout))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_base_url(&mut self, new_url: Url) {
|
||||
self.base_url = new_url
|
||||
}
|
||||
|
||||
pub fn current_url(&self) -> &Url {
|
||||
&self.base_url
|
||||
}
|
||||
|
||||
async fn send_get_request<K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<Response, HttpClientError<E>>
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
E: Display,
|
||||
{
|
||||
let url = sanitize_url(&self.base_url, path, params);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
Ok(
|
||||
wasmtimer::tokio::timeout(
|
||||
self.request_timeout,
|
||||
self.reqwest_client.get(url).send(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
Ok(self.reqwest_client.get(url).send().await?)
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_post_request<B, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
json_body: &B,
|
||||
) -> Result<Response, HttpClientError<E>>
|
||||
where
|
||||
B: Serialize + ?Sized,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
E: Display,
|
||||
{
|
||||
let url = sanitize_url(&self.base_url, path, params);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
Ok(wasmtimer::tokio::timeout(
|
||||
self.request_timeout,
|
||||
self.reqwest_client.post(url).json(json_body).send(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
Ok(self.reqwest_client.post(url).json(json_body).send().await?)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_json<T, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
E: Display + DeserializeOwned,
|
||||
{
|
||||
let res = self.send_get_request(path, params).await?;
|
||||
parse_response(res, false).await
|
||||
}
|
||||
|
||||
pub async fn post_json<B, T, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
json_body: &B,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
B: Serialize + ?Sized,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
E: Display + DeserializeOwned,
|
||||
{
|
||||
let res = self.send_post_request(path, params, json_body).await?;
|
||||
parse_response(res, true).await
|
||||
}
|
||||
|
||||
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
E: Display + DeserializeOwned,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let res = {
|
||||
wasmtimer::tokio::timeout(
|
||||
self.request_timeout,
|
||||
self.reqwest_client
|
||||
.get(self.base_url.join(endpoint.as_ref())?)
|
||||
.send(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let res = {
|
||||
self.reqwest_client
|
||||
.get(self.base_url.join(endpoint.as_ref())?)
|
||||
.send()
|
||||
.await?
|
||||
};
|
||||
|
||||
parse_response(res, false).await
|
||||
}
|
||||
|
||||
pub async fn post_json_endpoint<B, T, S, E>(
|
||||
&self,
|
||||
endpoint: S,
|
||||
json_body: &B,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
B: Serialize + ?Sized,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
E: Display + DeserializeOwned,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let res = {
|
||||
wasmtimer::tokio::timeout(
|
||||
self.request_timeout,
|
||||
self.reqwest_client
|
||||
.post(self.base_url.join(endpoint.as_ref())?)
|
||||
.json(json_body)
|
||||
.send(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let res = {
|
||||
self.reqwest_client
|
||||
.post(self.base_url.join(endpoint.as_ref())?)
|
||||
.json(json_body)
|
||||
.send()
|
||||
.await?
|
||||
};
|
||||
|
||||
parse_response(res, true).await
|
||||
}
|
||||
}
|
||||
|
||||
// define those methods on the trait for nicer extensions (and not having to type the thing twice)
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait ApiClient {
|
||||
/// 'get' json data from the segment-defined path, i.e. for example `["api", "v1", "mixnodes"]`,
|
||||
/// with tuple defined key-value parameters, i.e. for example `[("since", "12345")]`
|
||||
async fn get_json<T, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str> + Sync,
|
||||
V: AsRef<str> + Sync,
|
||||
E: Display + DeserializeOwned;
|
||||
|
||||
async fn post_json<B, T, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
json_body: &B,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
B: Serialize + ?Sized + Sync,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str> + Sync,
|
||||
V: AsRef<str> + Sync,
|
||||
E: Display + DeserializeOwned;
|
||||
|
||||
/// `get` json data from the provided absolute endpoint, i.e. for example `"/api/v1/mixnodes?since=12345"`
|
||||
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
E: Display + DeserializeOwned,
|
||||
S: AsRef<str> + Sync + Send;
|
||||
|
||||
async fn post_json_data_to<B, T, S, E>(
|
||||
&self,
|
||||
endpoint: S,
|
||||
json_body: &B,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
B: Serialize + ?Sized + Sync,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
E: Display + DeserializeOwned,
|
||||
S: AsRef<str> + Sync + Send;
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl ApiClient for Client {
|
||||
async fn get_json<T, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str> + Sync,
|
||||
V: AsRef<str> + Sync,
|
||||
E: Display + DeserializeOwned,
|
||||
{
|
||||
self.get_json(path, params).await
|
||||
}
|
||||
|
||||
async fn post_json<B, T, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
json_body: &B,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
B: Serialize + ?Sized + Sync,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str> + Sync,
|
||||
V: AsRef<str> + Sync,
|
||||
E: Display + DeserializeOwned,
|
||||
{
|
||||
self.post_json(path, params, json_body).await
|
||||
}
|
||||
|
||||
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
E: Display + DeserializeOwned,
|
||||
S: AsRef<str> + Sync + Send,
|
||||
{
|
||||
self.get_json_endpoint(endpoint).await
|
||||
}
|
||||
|
||||
async fn post_json_data_to<B, T, S, E>(
|
||||
&self,
|
||||
endpoint: S,
|
||||
json_body: &B,
|
||||
) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
B: Serialize + ?Sized + Sync,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
E: Display + DeserializeOwned,
|
||||
S: AsRef<str> + Sync + Send,
|
||||
{
|
||||
self.post_json_endpoint(endpoint, json_body).await
|
||||
}
|
||||
}
|
||||
|
||||
// utility function that should solve the double slash problem in API urls forever.
|
||||
pub fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
|
||||
base: &Url,
|
||||
segments: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Url {
|
||||
let mut url = base.clone();
|
||||
let mut path_segments = url
|
||||
.path_segments_mut()
|
||||
.expect("provided validator url does not have a base!");
|
||||
for segment in segments {
|
||||
let segment = segment.strip_prefix('/').unwrap_or(segment);
|
||||
let segment = segment.strip_suffix('/').unwrap_or(segment);
|
||||
|
||||
path_segments.push(segment);
|
||||
}
|
||||
// I don't understand why compiler couldn't figure out that it's no longer used
|
||||
// and can be dropped
|
||||
drop(path_segments);
|
||||
|
||||
if !params.is_empty() {
|
||||
url.query_pairs_mut().extend_pairs(params);
|
||||
}
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
E: DeserializeOwned + Display,
|
||||
{
|
||||
let status = res.status();
|
||||
|
||||
if !allow_empty {
|
||||
if let Some(0) = res.content_length() {
|
||||
return Err(HttpClientError::EmptyResponse { status });
|
||||
}
|
||||
}
|
||||
|
||||
if res.status().is_success() {
|
||||
Ok(res.json().await?)
|
||||
} else if res.status() == StatusCode::NOT_FOUND {
|
||||
Err(HttpClientError::NotFound)
|
||||
} else {
|
||||
let Ok(plaintext) = res.text().await else {
|
||||
return Err(HttpClientError::RequestFailure { status });
|
||||
};
|
||||
|
||||
if let Ok(request_error) = serde_json::from_str(&plaintext) {
|
||||
Err(HttpClientError::EndpointFailure {
|
||||
status,
|
||||
error: request_error,
|
||||
})
|
||||
} else {
|
||||
Err(HttpClientError::GenericRequestFailure(plaintext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sanitizing_urls() {
|
||||
let base_url: Url = "http://foomp.com".parse().unwrap();
|
||||
|
||||
// works with 1 segment
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
sanitize_url(&base_url, &["foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with 2 segments
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with leading slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
sanitize_url(&base_url, &["/foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with trailing slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
sanitize_url(&base_url, &["foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with both leading and trailing slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
sanitize_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// adds params
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar?foomp=baz",
|
||||
sanitize_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
|
||||
sanitize_url(
|
||||
&base_url,
|
||||
&["/foo/", "/bar/"],
|
||||
&[("arg1", "val1"), ("arg2", "val2")]
|
||||
)
|
||||
.as_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
[package]
|
||||
name = "nym-http-requests"
|
||||
version = "0.1.0"
|
||||
description = "Helper library for sending HTTP requesters over the Nym mixnet"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
nym-socks5-requests = { path = "../socks5/requests" }
|
||||
nym-ordered-buffer = { path = "../socks5/ordered-buffer" }
|
||||
nym-service-providers-common = { path = "../../service-providers/common" }
|
||||
bytecodec = "0.4.15"
|
||||
httpcodec = "0.2.3"
|
||||
bytes = "1"
|
||||
http = "0.2.9"
|
||||
thiserror = "1"
|
||||
url = "2"
|
||||
@@ -1,23 +0,0 @@
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum MixHttpRequestError {
|
||||
#[error("invalid Socks5 response")]
|
||||
InvalidSocks5Response,
|
||||
|
||||
#[error("the received Socks5 response was empty")]
|
||||
EmptySocks5Response,
|
||||
|
||||
#[error("bytecodec Error: {0}")]
|
||||
ByteCodecError(#[from] bytecodec::Error),
|
||||
|
||||
#[error("Url parse error: {0}")]
|
||||
UrlParseError(#[from] url::ParseError),
|
||||
|
||||
#[error("could not resolve socket address from the provided URL")]
|
||||
SocketAddrResolveError {
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bytecodec::bytes::BytesEncoder;
|
||||
use bytecodec::io::IoEncodeExt;
|
||||
use bytecodec::Encode;
|
||||
use httpcodec::{BodyEncoder, Request, RequestEncoder};
|
||||
|
||||
pub mod error;
|
||||
pub mod socks;
|
||||
|
||||
pub fn encode_http_request_as_string(
|
||||
request: Request<Vec<u8>>,
|
||||
) -> Result<String, error::MixHttpRequestError> {
|
||||
// Encode HTTP request as bytes
|
||||
let mut encoder = RequestEncoder::new(BodyEncoder::new(BytesEncoder::new()));
|
||||
encoder.start_encoding(request)?;
|
||||
let mut buf = Vec::new();
|
||||
encoder.encode_all(&mut buf)?;
|
||||
|
||||
Ok(String::from_utf8_lossy(&buf).to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod http_requests_tests {
|
||||
use super::*;
|
||||
use httpcodec::{HeaderField, HttpVersion, Method, RequestTarget};
|
||||
|
||||
fn create_http_get_request() -> Request<Vec<u8>> {
|
||||
let mut request = Request::new(
|
||||
Method::new("GET").unwrap(),
|
||||
RequestTarget::new("/.wellknown/wallet/validators.json").unwrap(),
|
||||
HttpVersion::V1_1,
|
||||
b"".to_vec(),
|
||||
);
|
||||
let mut headers = request.header_mut();
|
||||
headers.add_field(HeaderField::new("Host", "nymtech.net").unwrap());
|
||||
|
||||
request
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn http_request_ok() {
|
||||
// Encode HTTP request as bytes
|
||||
let request = create_http_get_request();
|
||||
let mut encoder = RequestEncoder::new(BodyEncoder::new(BytesEncoder::new()));
|
||||
encoder.start_encoding(request).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
encoder.encode_all(&mut buf).unwrap();
|
||||
|
||||
let body_as_string = String::from_utf8(buf).unwrap();
|
||||
|
||||
// replace newlines with \r\n
|
||||
let expected = r"GET /.wellknown/wallet/validators.json HTTP/1.1
|
||||
Host: nymtech.net
|
||||
Content-Length: 0
|
||||
|
||||
"
|
||||
.replace('\n', "\r\n");
|
||||
|
||||
assert_eq!(expected, body_as_string);
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error;
|
||||
use bytecodec::bytes::BytesEncoder;
|
||||
use bytecodec::bytes::RemainingBytesDecoder;
|
||||
use bytecodec::io::IoEncodeExt;
|
||||
use bytecodec::{DecodeExt, Encode};
|
||||
use httpcodec::{BodyDecoder, ResponseDecoder};
|
||||
use httpcodec::{BodyEncoder, Request, RequestEncoder};
|
||||
use nym_service_providers_common::interface::ProviderInterfaceVersion;
|
||||
use nym_socks5_requests::{SocketData, Socks5ProtocolVersion, Socks5ProviderRequest};
|
||||
|
||||
pub fn encode_http_request_as_socks_send_request(
|
||||
provider_interface: ProviderInterfaceVersion,
|
||||
socks5_protocol: Socks5ProtocolVersion,
|
||||
conn_id: u64,
|
||||
request: Request<Vec<u8>>,
|
||||
seq: Option<u64>,
|
||||
local_closed: bool,
|
||||
) -> Result<nym_socks5_requests::Socks5ProviderRequest, error::MixHttpRequestError> {
|
||||
// Encode HTTP request as bytes
|
||||
let mut encoder = RequestEncoder::new(BodyEncoder::new(BytesEncoder::new()));
|
||||
encoder.start_encoding(request)?;
|
||||
let mut buf = Vec::new();
|
||||
encoder.encode_all(&mut buf)?;
|
||||
|
||||
// Wrap it as SOCKS send request
|
||||
let request_content = nym_socks5_requests::request::Socks5Request::new_send(
|
||||
socks5_protocol,
|
||||
SocketData::new(seq.unwrap_or_default(), conn_id, local_closed, buf),
|
||||
);
|
||||
|
||||
// and wrap it in provider request
|
||||
Ok(Socks5ProviderRequest::new_provider_data(
|
||||
provider_interface,
|
||||
request_content,
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MixHttpResponse {
|
||||
// pub connection_id: u64,
|
||||
// #[deprecated]
|
||||
// pub is_closed: bool,
|
||||
pub http_response: httpcodec::Response<Vec<u8>>,
|
||||
// #[deprecated]
|
||||
// pub seq: u64,
|
||||
}
|
||||
|
||||
impl MixHttpResponse {
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<MixHttpResponse, error::MixHttpRequestError> {
|
||||
if b.is_empty() {
|
||||
Err(error::MixHttpRequestError::EmptySocks5Response)
|
||||
} else {
|
||||
let mut decoder = ResponseDecoder::<BodyDecoder<RemainingBytesDecoder>>::default();
|
||||
let http_response = decoder.decode_from_bytes(b)?;
|
||||
|
||||
Ok(MixHttpResponse { http_response })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl TryFrom<Socks5Response> for MixHttpResponse {
|
||||
// type Error = error::MixHttpRequestError;
|
||||
//
|
||||
// fn try_from(value: Socks5Response) -> Result<Self, Self::Error> {
|
||||
// if let Socks5ResponseContent::NetworkData { content } = value.content {
|
||||
// content.try_into()
|
||||
// } else {
|
||||
// Err(error::MixHttpRequestError::InvalidSocks5Response)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl TryFrom<SocketData> for MixHttpResponse {
|
||||
// type Error = error::MixHttpRequestError;
|
||||
//
|
||||
// fn try_from(value: SocketData) -> Result<Self, Self::Error> {
|
||||
// if value.data.is_empty() {
|
||||
// Err(error::MixHttpRequestError::EmptySocks5Response)
|
||||
// } else {
|
||||
// let mut decoder = ResponseDecoder::<BodyDecoder<RemainingBytesDecoder>>::default();
|
||||
// let http_response = decoder.decode_from_bytes(value.data.as_ref())?;
|
||||
//
|
||||
// Ok(MixHttpResponse {
|
||||
// connection_id: value.header.connection_id,
|
||||
// is_closed: value.header.local_socket_closed,
|
||||
// http_response,
|
||||
// seq: value.header.seq,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn decode_socks_response_as_http_response(
|
||||
// socks5_response: Socks5Response,
|
||||
// ) -> Result<MixHttpResponse, error::MixHttpRequestError> {
|
||||
// socks5_response.try_into()
|
||||
// }
|
||||
//
|
||||
// #[cfg(test)]
|
||||
// mod http_requests_tests {
|
||||
// use super::*;
|
||||
// use httpcodec::{HeaderField, HttpVersion, Method, RequestTarget};
|
||||
// use nym_service_providers_common::interface::Serializable;
|
||||
// use nym_socks5_requests::Socks5Response;
|
||||
//
|
||||
// fn create_http_get_request() -> Request<Vec<u8>> {
|
||||
// let mut request = Request::new(
|
||||
// Method::new("GET").unwrap(),
|
||||
// RequestTarget::new("/.wellknown/wallet/validators.json").unwrap(),
|
||||
// HttpVersion::V1_1,
|
||||
// b"".to_vec(),
|
||||
// );
|
||||
// let mut headers = request.header_mut();
|
||||
// headers.add_field(HeaderField::new("Host", "nymtech.net").unwrap());
|
||||
//
|
||||
// request
|
||||
// }
|
||||
//
|
||||
// fn create_socks5_request_buffer() -> Vec<u8> {
|
||||
// let request = create_http_get_request();
|
||||
// let socks5_request = encode_http_request_as_socks_send_request(
|
||||
// ProviderInterfaceVersion::new_current(),
|
||||
// Socks5ProtocolVersion::new_current(),
|
||||
// 99u64,
|
||||
// request,
|
||||
// Some(42u64),
|
||||
// true,
|
||||
// )
|
||||
// .unwrap();
|
||||
// socks5_request.into_bytes()
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn request_http_request_content_ok() {
|
||||
// let buffer = create_socks5_request_buffer();
|
||||
//
|
||||
// // HTTP request string content is as expected
|
||||
// assert_eq!(
|
||||
// [71u8, 69u8, 84u8, 32u8, 47u8, 46u8, 119u8, 101u8],
|
||||
// buffer[19..27]
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// /// This test will fail if the framing of the request buffer changes, e.g. when OrderedMessage
|
||||
// /// changes to have the `index` value as a field, instead of packed with the `data`
|
||||
// #[test]
|
||||
// fn request_size_as_expected_ok() {
|
||||
// let buffer = create_socks5_request_buffer();
|
||||
// // println!("{:?}", buffer) // uncomment and run `cargo test -- --nocapture` to view
|
||||
//
|
||||
// assert_eq!(108, buffer.len()); // version set to SOCKS5
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn request_socks5_headers_ok() {
|
||||
// let buffer = create_socks5_request_buffer();
|
||||
//
|
||||
// assert_eq!(5u8, buffer[0]); // version set to SOCKS5
|
||||
// assert_eq!(1u8, buffer[1]); // type is SEND
|
||||
// assert_eq!(99u8, buffer[9]); // ConnectionId is correct
|
||||
// assert_eq!(1u8, buffer[10]); // local_closed is true
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn request_ordered_message_ok() {
|
||||
// let buffer = create_socks5_request_buffer();
|
||||
//
|
||||
// // OrderedMessage index is correct
|
||||
// assert_eq!(42u8, buffer[18]);
|
||||
// }
|
||||
//
|
||||
// fn create_socks_response() -> Socks5Response {
|
||||
// // HTTP response is just a string
|
||||
// let http_response_string = "HTTP/1.1 200 OK\r\nServer: foo/0.0.1\r\n\r\n";
|
||||
//
|
||||
// let data = http_response_string.as_bytes().to_vec();
|
||||
//
|
||||
// // wrap in `NetworkData`, then Socks5Response
|
||||
// Socks5Response::new(
|
||||
// Socks5ProtocolVersion::new_current(),
|
||||
// Socks5ResponseContent::NetworkData {
|
||||
// content: SocketData::new(42, 99u64, false, data),
|
||||
// },
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// /// This test will fail is anything in the framing of the socks5_response byte
|
||||
// /// representation changes
|
||||
// #[test]
|
||||
// fn response_byte_size_is_as_expected() {
|
||||
// let socks5_response = create_socks_response();
|
||||
// let buf = socks5_response.into_bytes();
|
||||
//
|
||||
// assert_eq!(57, buf.len());
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn response_parses() {
|
||||
// unimplemented!()
|
||||
// // let socks5_response = create_socks_response();
|
||||
// // let response = decode_socks_response_as_http_response(socks5_response).unwrap();
|
||||
// //
|
||||
// // assert_eq!(42u64, response.seq); // OrderedMessage index as expected
|
||||
// // assert_eq!(HttpVersion::V1_1, response.http_response.http_version());
|
||||
// // assert_eq!(200u16, response.http_response.status_code().as_u16());
|
||||
// // assert_eq!(
|
||||
// // "foo/0.0.1",
|
||||
// // response.http_response.header().get_field("Server").unwrap()
|
||||
// // );
|
||||
// }
|
||||
// }
|
||||
@@ -10,4 +10,4 @@ bip32 = "0.5.1"
|
||||
k256 = { workspace = true }
|
||||
ledger-transport = "0.10.0"
|
||||
ledger-transport-hid = "0.10.0"
|
||||
thiserror = "1"
|
||||
thiserror = { workspace = true }
|
||||
@@ -23,7 +23,7 @@ impl EchoPacket {
|
||||
.chain(keys.public_key().to_bytes().iter().cloned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let signature = keys.private_key().sign(&bytes_to_sign);
|
||||
let signature = keys.private_key().sign(bytes_to_sign);
|
||||
|
||||
EchoPacket {
|
||||
sequence_number,
|
||||
@@ -67,7 +67,7 @@ impl EchoPacket {
|
||||
|
||||
pub(crate) fn construct_reply(self, private_key: &identity::PrivateKey) -> ReplyPacket {
|
||||
let bytes = self.to_bytes();
|
||||
let signature = private_key.sign(&bytes);
|
||||
let signature = private_key.sign(bytes);
|
||||
ReplyPacket {
|
||||
base_packet: self,
|
||||
signature,
|
||||
|
||||
@@ -7,5 +7,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ cfg-if = { workspace = true }
|
||||
dotenvy = { workspace = true }
|
||||
hex-literal = "0.3.3"
|
||||
once_cell = { workspace = true }
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true, features = ["derive"]}
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
@@ -462,6 +462,9 @@ pub const DEFAULT_NYM_API_PORT: u16 = 8080;
|
||||
|
||||
pub const NYM_API_VERSION: &str = "v1";
|
||||
|
||||
// NYM-NODE
|
||||
pub const DEFAULT_NYM_NODE_HTTP_PORT: u16 = 8080;
|
||||
|
||||
// REWARDING
|
||||
|
||||
/// We'll be assuming a few more things, profit margin and cost function. Since we don't have reliable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
|
||||
|
||||
@@ -11,8 +11,8 @@ bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="gt-seriali
|
||||
itertools = "0.10"
|
||||
digest = "0.9"
|
||||
rand = "0.8"
|
||||
thiserror = "1.0"
|
||||
serde = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_derive = "1.0"
|
||||
bs58 = "0.4.0"
|
||||
sha2 = "0.9"
|
||||
|
||||
@@ -11,7 +11,7 @@ repository = { workspace = true }
|
||||
nym-crypto = { path = "../../crypto", features = ["asymmetric"] } # all addresses are expressed in terms on their crypto keys
|
||||
nym-sphinx-types = { path = "../types", features = ["sphinx"] } # we need to be able to refer to some types defined inside sphinx crate
|
||||
serde = "1.0" # implementing serialization/deserialization for some types, like `Recipient`
|
||||
thiserror = "1.0.37"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.7"
|
||||
|
||||
@@ -10,8 +10,8 @@ repository = { workspace = true }
|
||||
[dependencies]
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
bs58 = "0.4"
|
||||
serde = "1.0"
|
||||
thiserror = "1"
|
||||
serde = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
|
||||
nym-sphinx-addressing = { path = "../addressing" }
|
||||
|
||||
@@ -12,7 +12,7 @@ repository = { workspace = true }
|
||||
[dependencies]
|
||||
log = { workspace = true }
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
thiserror = "1.0.37"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-sphinx-addressing = { path = "../addressing" }
|
||||
nym-sphinx-params = { path = "../params" }
|
||||
|
||||
@@ -9,7 +9,7 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
thiserror = "1.0.37"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-crypto = { path = "../../crypto" }
|
||||
nym-sphinx-acknowledgements = { path = "../acknowledgements" }
|
||||
|
||||
@@ -12,4 +12,4 @@ nym-sphinx-addressing = { path = "../addressing" }
|
||||
nym-sphinx-params = { path = "../params" }
|
||||
nym-sphinx-types = { path = "../types", features = ["sphinx", "outfox"] }
|
||||
nym-outfox = { path = "../../../nym-outfox" }
|
||||
thiserror = "1"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -10,7 +10,7 @@ repository = { workspace = true }
|
||||
[dependencies]
|
||||
bytes = "1.0"
|
||||
tokio-util = { version = "0.7.4", features = ["codec"] }
|
||||
thiserror = "1.0.37"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-sphinx-types = { path = "../types", features = ["sphinx", "outfox"] }
|
||||
nym-sphinx-params = { path = "../params", features = ["sphinx", "outfox"] }
|
||||
|
||||
@@ -8,7 +8,7 @@ license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0.37"
|
||||
thiserror = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
nym-crypto = { path = "../../crypto", features = ["hashing", "symmetric"] }
|
||||
|
||||
@@ -294,7 +294,8 @@ mod message_receiver {
|
||||
owner: "foomp4".to_string(),
|
||||
host: "1.2.3.4".parse().unwrap(),
|
||||
mix_host: "1.2.3.4:1789".parse().unwrap(),
|
||||
clients_port: 9000,
|
||||
clients_ws_port: 9000,
|
||||
clients_wss_port: None,
|
||||
identity_key: identity::PublicKey::from_base58_string(
|
||||
"FioFa8nMmPpQnYi7JyojoTuwGLeyNS8BF4ChPr29zUML",
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ repository = { workspace = true }
|
||||
[dependencies]
|
||||
sphinx-packet = { version = "0.1.0", optional = true }
|
||||
nym-outfox = { path = "../../../nym-outfox", optional = true }
|
||||
thiserror = "1"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["sphinx"]
|
||||
|
||||
@@ -51,7 +51,11 @@ where
|
||||
if T::pem_type() != key_pem.tag {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"unexpected key pem tag",
|
||||
format!(
|
||||
"unexpected key pem tag. Got '{}', expected: '{}'",
|
||||
key_pem.tag,
|
||||
T::pem_type()
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ log = { workspace = true }
|
||||
pin-project = "1.0"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
reqwest = { workspace = true }
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
|
||||
tap = "1.0.1"
|
||||
thiserror = "1.0.34"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
url = { workspace = true }
|
||||
|
||||
|
||||
@@ -8,4 +8,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log = { workspace = true }
|
||||
thiserror = "1.0.37"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -15,5 +15,5 @@ reqwest = { workspace = true, features = ["json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "chrono"]}
|
||||
thiserror = "1"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = [ "time" ] }
|
||||
|
||||
@@ -15,7 +15,7 @@ documentation = { workspace = true }
|
||||
bs58 = "0.4"
|
||||
log = { workspace = true }
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
thiserror = "1.0.37"
|
||||
thiserror = { workspace = true }
|
||||
async-trait = { workspace = true, optional = true }
|
||||
semver = "0.11"
|
||||
|
||||
@@ -35,6 +35,10 @@ nym-sphinx-types = { path = "../nymsphinx/types", features = ["sphinx", "outfox"
|
||||
nym-sphinx-routing = { path = "../nymsphinx/routing" }
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
|
||||
# I'm not sure how to feel about pulling in this dependency here...
|
||||
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
|
||||
|
||||
# 'serializable' feature
|
||||
nym-config = { path = "../config", optional = true }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{filter, NetworkAddress, NodeVersion};
|
||||
use nym_api_requests::models::DescribedGateway;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_mixnet_contract_common::GatewayBond;
|
||||
use nym_sphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress};
|
||||
@@ -10,6 +11,7 @@ use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use std::io;
|
||||
use std::net::AddrParseError;
|
||||
use std::net::SocketAddr;
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -27,6 +29,17 @@ pub enum GatewayConversionError {
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
|
||||
#[error("'{gateway}' has not provided any valid ip addresses")]
|
||||
NoIpAddressesProvided { gateway: String },
|
||||
|
||||
#[error("'{gateway}' has provided a malformed ip address: {err}")]
|
||||
MalformedIpAddress {
|
||||
gateway: String,
|
||||
|
||||
#[source]
|
||||
err: AddrParseError,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -36,7 +49,13 @@ pub struct Node {
|
||||
// we're keeping this as separate resolved field since we do not want to be resolving the potential
|
||||
// hostname every time we want to construct a path via this node
|
||||
pub mix_host: SocketAddr,
|
||||
pub clients_port: u16,
|
||||
|
||||
// #[serde(alias = "clients_port")]
|
||||
pub clients_ws_port: u16,
|
||||
|
||||
// #[serde(default)]
|
||||
pub clients_wss_port: Option<u16>,
|
||||
|
||||
pub identity_key: identity::PublicKey,
|
||||
pub sphinx_key: encryption::PublicKey, // TODO: or nymsphinx::PublicKey? both are x25519
|
||||
pub version: NodeVersion,
|
||||
@@ -48,7 +67,8 @@ impl std::fmt::Debug for Node {
|
||||
.field("host", &self.host)
|
||||
.field("owner", &self.owner)
|
||||
.field("mix_host", &self.mix_host)
|
||||
.field("clients_port", &self.clients_port)
|
||||
.field("clients_ws_port", &self.clients_ws_port)
|
||||
.field("clients_wss_port", &self.clients_wss_port)
|
||||
.field("identity_key", &self.identity_key.to_base58_string())
|
||||
.field("sphinx_key", &self.sphinx_key.to_base58_string())
|
||||
.field("version", &self.version)
|
||||
@@ -82,11 +102,17 @@ impl Node {
|
||||
}
|
||||
|
||||
pub fn clients_address(&self) -> String {
|
||||
format!("ws://{}:{}", self.host, self.clients_port)
|
||||
self.clients_address_tls()
|
||||
.unwrap_or_else(|| self.clients_address_no_tls())
|
||||
}
|
||||
|
||||
pub fn clients_address_tls(&self) -> String {
|
||||
format!("wss://{}:443", self.host)
|
||||
pub fn clients_address_no_tls(&self) -> String {
|
||||
format!("ws://{}:{}", self.host, self.clients_ws_port)
|
||||
}
|
||||
|
||||
pub fn clients_address_tls(&self) -> Option<String> {
|
||||
self.clients_wss_port
|
||||
.map(|p| format!("wss://{}:{p}", self.host))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +157,8 @@ impl<'a> TryFrom<&'a GatewayBond> for Node {
|
||||
owner: bond.owner.as_str().to_owned(),
|
||||
host,
|
||||
mix_host,
|
||||
clients_port: bond.gateway.clients_port,
|
||||
clients_ws_port: bond.gateway.clients_port,
|
||||
clients_wss_port: None,
|
||||
identity_key: identity::PublicKey::from_base58_string(&bond.gateway.identity_key)?,
|
||||
sphinx_key: encryption::PublicKey::from_base58_string(&bond.gateway.sphinx_key)?,
|
||||
version: bond.gateway.version.as_str().into(),
|
||||
@@ -146,3 +173,56 @@ impl TryFrom<GatewayBond> for Node {
|
||||
Node::try_from(&bond)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a DescribedGateway> for Node {
|
||||
type Error = GatewayConversionError;
|
||||
|
||||
fn try_from(value: &'a DescribedGateway) -> Result<Self, Self::Error> {
|
||||
let Some(ref self_described) = value.self_described else {
|
||||
return (&value.bond).try_into();
|
||||
};
|
||||
|
||||
let ips = &self_described.host_information.ip_address;
|
||||
if ips.is_empty() {
|
||||
return Err(GatewayConversionError::NoIpAddressesProvided {
|
||||
gateway: value.bond.gateway.identity_key.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
let host = match &self_described.host_information.hostname {
|
||||
None => NetworkAddress::IpAddr(ips[0]),
|
||||
Some(hostname) => NetworkAddress::Hostname(hostname.clone()),
|
||||
};
|
||||
|
||||
// get ip from the self-reported values so we wouldn't need to do any hostname resolution
|
||||
// (which doesn't really work in wasm)
|
||||
let mix_host = SocketAddr::new(ips[0], value.bond.gateway.mix_port);
|
||||
|
||||
Ok(Node {
|
||||
owner: value.bond.owner.as_str().to_owned(),
|
||||
host,
|
||||
mix_host,
|
||||
clients_ws_port: self_described.mixnet_websockets.ws_port,
|
||||
clients_wss_port: self_described.mixnet_websockets.wss_port,
|
||||
identity_key: identity::PublicKey::from_base58_string(
|
||||
&self_described.host_information.keys.ed25519,
|
||||
)?,
|
||||
sphinx_key: encryption::PublicKey::from_base58_string(
|
||||
&self_described.host_information.keys.x25519,
|
||||
)?,
|
||||
version: self_described
|
||||
.build_information
|
||||
.build_version
|
||||
.as_str()
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<DescribedGateway> for Node {
|
||||
type Error = GatewayConversionError;
|
||||
|
||||
fn try_from(value: DescribedGateway) -> Result<Self, Self::Error> {
|
||||
Node::try_from(&value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use std::str::FromStr;
|
||||
|
||||
#[cfg(feature = "serializable")]
|
||||
use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use nym_api_requests::models::DescribedGateway;
|
||||
|
||||
pub mod error;
|
||||
pub mod filter;
|
||||
@@ -403,10 +404,33 @@ impl<'de> Deserialize<'de> for NymTopology {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nym_topology_from_detailed(
|
||||
pub trait IntoGatewayNode: TryInto<gateway::Node>
|
||||
where
|
||||
<Self as TryInto<gateway::Node>>::Error: Display,
|
||||
{
|
||||
fn identity(&self) -> IdentityKeyRef;
|
||||
}
|
||||
|
||||
impl IntoGatewayNode for GatewayBond {
|
||||
fn identity(&self) -> IdentityKeyRef {
|
||||
&self.gateway.identity_key
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoGatewayNode for DescribedGateway {
|
||||
fn identity(&self) -> IdentityKeyRef {
|
||||
&self.bond.gateway.identity_key
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nym_topology_from_detailed<G>(
|
||||
mix_details: Vec<MixNodeDetails>,
|
||||
gateway_bonds: Vec<GatewayBond>,
|
||||
) -> NymTopology {
|
||||
gateway_bonds: Vec<G>,
|
||||
) -> NymTopology
|
||||
where
|
||||
G: IntoGatewayNode,
|
||||
<G as TryInto<gateway::Node>>::Error: Display,
|
||||
{
|
||||
let mut mixes = BTreeMap::new();
|
||||
for bond in mix_details
|
||||
.into_iter()
|
||||
@@ -435,7 +459,7 @@ pub fn nym_topology_from_detailed(
|
||||
|
||||
let mut gateways = Vec::with_capacity(gateway_bonds.len());
|
||||
for bond in gateway_bonds.into_iter() {
|
||||
let gate_id = bond.gateway.identity_key.clone();
|
||||
let gate_id = bond.identity().to_owned();
|
||||
match bond.try_into() {
|
||||
Ok(gate) => gateways.push(gate),
|
||||
Err(err) => {
|
||||
|
||||
@@ -190,7 +190,12 @@ pub struct SerializableGateway {
|
||||
|
||||
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||
#[serde(alias = "clients_port")]
|
||||
pub clients_port: Option<u16>,
|
||||
#[serde(alias = "clients_ws_port")]
|
||||
pub clients_ws_port: Option<u16>,
|
||||
|
||||
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||
#[serde(alias = "clients_wss_port")]
|
||||
pub clients_wss_port: Option<u16>,
|
||||
|
||||
#[serde(alias = "identity_key")]
|
||||
pub identity_key: String,
|
||||
@@ -209,7 +214,9 @@ impl TryFrom<SerializableGateway> for gateway::Node {
|
||||
let host = gateway::Node::parse_host(&value.host)?;
|
||||
|
||||
let mix_port = value.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT);
|
||||
let clients_port = value.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT);
|
||||
let clients_ws_port = value
|
||||
.clients_ws_port
|
||||
.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT);
|
||||
let version = value.version.map(|v| v.as_str().into()).unwrap_or_default();
|
||||
|
||||
// try to completely resolve the host in the mix situation to avoid doing it every
|
||||
@@ -224,7 +231,8 @@ impl TryFrom<SerializableGateway> for gateway::Node {
|
||||
owner: value.owner,
|
||||
host,
|
||||
mix_host,
|
||||
clients_port,
|
||||
clients_ws_port,
|
||||
clients_wss_port: value.clients_wss_port,
|
||||
identity_key: identity::PublicKey::from_base58_string(&value.identity_key)
|
||||
.map_err(GatewayConversionError::from)?,
|
||||
sphinx_key: encryption::PublicKey::from_base58_string(&value.sphinx_key)
|
||||
@@ -241,7 +249,8 @@ impl<'a> From<&'a gateway::Node> for SerializableGateway {
|
||||
host: value.host.to_string(),
|
||||
explicit_ip: Some(value.mix_host.ip()),
|
||||
mix_port: Some(value.mix_host.port()),
|
||||
clients_port: Some(value.clients_port),
|
||||
clients_ws_port: Some(value.clients_ws_port),
|
||||
clients_wss_port: value.clients_wss_port,
|
||||
identity_key: value.identity_key.to_base58_string(),
|
||||
sphinx_key: value.sphinx_key.to_base58_string(),
|
||||
version: Some(value.version.to_string()),
|
||||
|
||||
@@ -14,7 +14,7 @@ js-sys = { workspace = true }
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde-wasm-bindgen = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tsify = { workspace = true, features = ["js"] }
|
||||
url = { workspace = true }
|
||||
wasm-bindgen = { workspace = true }
|
||||
|
||||
Generated
+13
-13
@@ -810,7 +810,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.12",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1628,9 +1628,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1894,7 +1894,7 @@ checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.12",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1927,7 +1927,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.12",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2041,9 +2041,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.12"
|
||||
version = "2.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
|
||||
checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2065,22 +2065,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.12",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2427,5 +2427,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.12",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
@@ -48,3 +48,5 @@ cw3 = "=1.1.0"
|
||||
cw3-fixed-multisig = "=1.1.0"
|
||||
cw4 = "=1.1.0"
|
||||
cw20 = "=1.1.0"
|
||||
|
||||
thiserror = "1.0.48"
|
||||
|
||||
@@ -23,7 +23,7 @@ cw-storage-plus = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.23"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[features]
|
||||
schema-gen = ["nym-coconut-bandwidth-contract-common/schema", "cosmwasm-schema"]
|
||||
@@ -22,7 +22,7 @@ cw-storage-plus = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.23"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
cw-multi-test = { workspace = true }
|
||||
|
||||
@@ -22,7 +22,7 @@ cw-utils = { workspace = true }
|
||||
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.23"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-coconut-bandwidth = { path = "../coconut-bandwidth" }
|
||||
nym-coconut-dkg = { path = "../coconut-dkg" }
|
||||
|
||||
@@ -23,7 +23,7 @@ cw-controllers = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.23"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
cw-multi-test = { workspace = true }
|
||||
|
||||
@@ -243,7 +243,7 @@ impl TestSetup {
|
||||
ContractMessageContent::new(Addr::unchecked(owner), proxy, vec![stake], payload);
|
||||
let sign_payload = SignableMixNodeBondingMsg::new(signing_nonce, content);
|
||||
let plaintext = sign_payload.to_plaintext().unwrap();
|
||||
let signature = keypair.private_key().sign(&plaintext);
|
||||
let signature = keypair.private_key().sign(plaintext);
|
||||
let msg_signature = MessageSignature::from(signature.to_bytes().as_ref());
|
||||
|
||||
(mixnode, msg_signature)
|
||||
|
||||
@@ -39,7 +39,7 @@ cw-storage-plus = { workspace = true }
|
||||
|
||||
bs58 = "0.4.0"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
thiserror = { workspace = true }
|
||||
time = { version = "0.3", features = ["macros"] }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ pub mod test_helpers {
|
||||
|
||||
let sig_bytes = family_owner_keys
|
||||
.private_key()
|
||||
.sign(&msg.to_plaintext().unwrap())
|
||||
.sign(msg.to_plaintext().unwrap())
|
||||
.to_bytes();
|
||||
MessageSignature::from(sig_bytes.as_ref())
|
||||
}
|
||||
@@ -956,7 +956,7 @@ pub mod test_helpers {
|
||||
match message.algorithm {
|
||||
SigningAlgorithm::Ed25519 => {
|
||||
let plaintext = message.to_plaintext().unwrap();
|
||||
let signature = private_key.sign(&plaintext);
|
||||
let signature = private_key.sign(plaintext);
|
||||
MessageSignature::from(signature.to_bytes().as_ref())
|
||||
}
|
||||
SigningAlgorithm::Secp256k1 => {
|
||||
|
||||
@@ -41,5 +41,5 @@ cosmwasm-schema = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts
|
||||
nym-name-service-common = { path = "../../common/cosmwasm-smart-contracts/name-service" }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.39"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "=7.4.3", default-features = false, features = ["build", "git", "rustc"] }
|
||||
|
||||
@@ -29,7 +29,7 @@ pub fn ed25519_sign_message<T: Serialize + SigningPurpose>(
|
||||
match message.algorithm {
|
||||
SigningAlgorithm::Ed25519 => {
|
||||
let plaintext = message.to_plaintext().unwrap();
|
||||
let signature = private_key.sign(&plaintext);
|
||||
let signature = private_key.sign(plaintext);
|
||||
MessageSignature::from(signature.to_bytes().as_ref())
|
||||
}
|
||||
SigningAlgorithm::Secp256k1 => {
|
||||
|
||||
@@ -22,7 +22,7 @@ nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts
|
||||
nym-service-provider-directory-common = { path = "../../common/cosmwasm-smart-contracts/service-provider-directory" }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.39"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "=7.4.3", default-features = false, features = ["build", "git", "rustc"] }
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn ed25519_sign_message<T: Serialize + SigningPurpose>(
|
||||
match message.algorithm {
|
||||
SigningAlgorithm::Ed25519 => {
|
||||
let plaintext = message.to_plaintext().unwrap();
|
||||
let signature = private_key.sign(&plaintext);
|
||||
let signature = private_key.sign(plaintext);
|
||||
MessageSignature::from(signature.to_bytes().as_ref())
|
||||
}
|
||||
SigningAlgorithm::Secp256k1 => {
|
||||
|
||||
@@ -35,7 +35,7 @@ cw2 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true, features = ["iterator"] }
|
||||
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0" }
|
||||
thiserror ={ workspace = true }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
+4
-4
@@ -36,7 +36,7 @@ nym-config = { path = "../common/config" }
|
||||
nym-ephemera-common = { path = "../common/cosmwasm-smart-contracts/ephemera" }
|
||||
pretty_env_logger = "0.4"
|
||||
refinery = { version = "0.8.7", features = ["rusqlite"], optional = true }
|
||||
reqwest = { version = "0.11.6", features = ["json"] }
|
||||
reqwest = { version = "0.11.22", features = ["json"] }
|
||||
# Rocksdb kills compilation times and we're not currently using it. The reason
|
||||
# we comment it out is that rust-analyzer runs with --all-features
|
||||
#rocksdb = { version = "0.21.0", optional = true }
|
||||
@@ -44,14 +44,14 @@ rusqlite = { version = "0.27.0", features = ["bundled"], optional = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_derive = "1.0.149"
|
||||
serde_json = "1.0.91"
|
||||
thiserror = "1.0.37"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { version = "1", features = ["macros", "net","rt-multi-thread"] }
|
||||
tokio-tungstenite = { workspace = true }
|
||||
tokio-util = { version = "0.7.4", features = ["full"] }
|
||||
toml = "0.7.0"
|
||||
unsigned-varint = "0.7.1"
|
||||
utoipa = { version = "3.0.1", features = ["actix_extras"] }
|
||||
utoipa-swagger-ui = { version = "3.0.2", features = ["actix-web"] }
|
||||
utoipa = { workspace = true, features = ["actix_extras"] }
|
||||
utoipa-swagger-ui = { workspace = true, features = ["actix-web"] }
|
||||
uuid = { version = "1.2.2", features = ["v4"] }
|
||||
|
||||
# Temporary fix to https://github.com/bluejekyll/trust-dns/issues/1946
|
||||
|
||||
@@ -24,10 +24,10 @@ reqwest = { workspace = true }
|
||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||
rocket_cors = { git="https://github.com/lawliet89/rocket_cors", rev="dfd3662c49e2f6fc37df35091cb94d82f7fb5915" }
|
||||
rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] }
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
serde = "1.0.126"
|
||||
serde_json = "1.0.66"
|
||||
thiserror = "1.0.29"
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = {version = "1.21.2", features = ["full"] }
|
||||
|
||||
nym-bin-common = { path = "../common/bin-common"}
|
||||
|
||||
@@ -7,6 +7,6 @@ edition = "2021"
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" }
|
||||
nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::helpers::best_effort_small_dec_to_f64;
|
||||
use crate::mix_node::models::EconomicDynamicsStats;
|
||||
use nym_contracts_common::truncate_decimal;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
|
||||
pub(crate) async fn retrieve_mixnode_econ_stats(
|
||||
client: &ThreadsafeValidatorClient,
|
||||
|
||||
+5
-1
@@ -22,6 +22,8 @@ axum-macros = "0.3.8" # Useful for debugging axum Handler trait errors
|
||||
fastrand = "2"
|
||||
x25519-dalek = { version = "2.0.0", features = ["static_secrets"] }
|
||||
base64 = "0.21.4"
|
||||
|
||||
|
||||
anyhow = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
atty = "0.2"
|
||||
@@ -48,7 +50,7 @@ sqlx = { version = "0.5", features = [
|
||||
"migrate",
|
||||
] }
|
||||
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
|
||||
thiserror = "1"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = [
|
||||
"rt-multi-thread",
|
||||
"net",
|
||||
@@ -63,6 +65,8 @@ url = { version = "2.2", features = ["serde"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
# internal
|
||||
nym-node = { path = "../nym-node" }
|
||||
|
||||
nym-api-requests = { path = "../nym-api/nym-api-requests" }
|
||||
nym-bin-common = { path = "../common/bin-common", features = ["output_format"] }
|
||||
nym-coconut-interface = { path = "../common/coconut-interface" }
|
||||
|
||||
@@ -17,7 +17,7 @@ log = { workspace = true }
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
|
||||
@@ -137,7 +137,7 @@ impl<'a, S> State<'a, S> {
|
||||
.chain(remote_ephemeral_key.to_bytes().iter().cloned())
|
||||
.collect();
|
||||
|
||||
let signature = self.identity.private_key().sign(&message);
|
||||
let signature = self.identity.private_key().sign(message);
|
||||
let zero_iv = stream_cipher::zero_iv::<GatewayEncryptionAlgorithm>();
|
||||
stream_cipher::encrypt::<GatewayEncryptionAlgorithm>(
|
||||
self.derived_shared_keys.as_ref().unwrap().encryption_key(),
|
||||
@@ -191,7 +191,7 @@ impl<'a, S> State<'a, S> {
|
||||
self.remote_pubkey
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.verify(&signed_payload, &signature)
|
||||
.verify(signed_payload, &signature)
|
||||
.map_err(|_| HandshakeError::InvalidSignature)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ pub(crate) struct OverrideConfig {
|
||||
impl OverrideConfig {
|
||||
pub(crate) fn do_override(self, mut config: Config) -> Result<Config, GatewayError> {
|
||||
config = config
|
||||
.with_optional(Config::with_listening_address, self.host)
|
||||
.with_optional(Config::with_host, self.host)
|
||||
.with_optional(Config::with_mix_port, self.mix_port)
|
||||
.with_optional(Config::with_clients_port, self.clients_port)
|
||||
.with_optional_custom_env(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
|
||||
use crate::config::old_config_v1_1_28::ConfigV1_1_28;
|
||||
use crate::config::old_config_v1_1_29::ConfigV1_1_29;
|
||||
use crate::config::{default_config_filepath, Config};
|
||||
use crate::error::GatewayError;
|
||||
use log::info;
|
||||
@@ -20,7 +21,8 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, GatewayError> {
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_28 = old_config.into();
|
||||
let updated: Config = updated_step1.into();
|
||||
let updated_step2: ConfigV1_1_29 = updated_step1.into();
|
||||
let updated: Config = updated_step2.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
.map_err(|err| GatewayError::ConfigSaveFailure {
|
||||
@@ -42,6 +44,29 @@ fn try_upgrade_v1_1_28_config(id: &str) -> Result<bool, GatewayError> {
|
||||
info!("It seems the gateway is using <= v1.1.28 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_29 = old_config.into();
|
||||
let updated: Config = updated_step1.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
.map_err(|err| GatewayError::ConfigSaveFailure {
|
||||
path: default_config_filepath(id),
|
||||
id: id.to_string(),
|
||||
source: err,
|
||||
})?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_29_config(id: &str) -> Result<bool, GatewayError> {
|
||||
// explicitly load it as v1.1.29 (which is incompatible with the current, i.e. 1.1.30+)
|
||||
let Ok(old_config) = ConfigV1_1_29::read_from_default_path(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(false);
|
||||
};
|
||||
info!("It seems the gateway is using <= v1.1.29 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated: Config = old_config.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
@@ -61,6 +86,9 @@ pub(crate) fn try_upgrade_config(id: &str) -> Result<(), GatewayError> {
|
||||
if try_upgrade_v1_1_28_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_29_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -12,9 +12,10 @@ use nym_config::{
|
||||
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
|
||||
};
|
||||
use nym_network_defaults::mainnet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use nym_node::config;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::io;
|
||||
use std::net::IpAddr;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
@@ -22,6 +23,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
pub(crate) mod old_config_v1_1_20;
|
||||
pub(crate) mod old_config_v1_1_28;
|
||||
pub(crate) mod old_config_v1_1_29;
|
||||
pub mod persistence;
|
||||
mod template;
|
||||
|
||||
@@ -38,6 +40,18 @@ const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
|
||||
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
|
||||
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
|
||||
|
||||
fn de_maybe_port<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let port = u16::deserialize(deserializer)?;
|
||||
if port == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(port))
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive default path to gateway's config directory.
|
||||
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
|
||||
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
@@ -71,8 +85,17 @@ pub struct Config {
|
||||
#[serde(skip)]
|
||||
pub(crate) save_path: Option<PathBuf>,
|
||||
|
||||
pub host: config::Host,
|
||||
|
||||
#[serde(default)]
|
||||
pub http: config::Http,
|
||||
|
||||
pub gateway: Gateway,
|
||||
|
||||
#[serde(default)]
|
||||
// currently not really used for anything useful
|
||||
pub wireguard: config::Wireguard,
|
||||
|
||||
pub storage_paths: GatewayPaths,
|
||||
|
||||
pub network_requester: NetworkRequester,
|
||||
@@ -92,9 +115,16 @@ impl NymConfigTemplate for Config {
|
||||
|
||||
impl Config {
|
||||
pub fn new<S: AsRef<str>>(id: S) -> Self {
|
||||
let default_gateway = Gateway::new_default(id.as_ref());
|
||||
Config {
|
||||
save_path: None,
|
||||
gateway: Gateway::new_default(id.as_ref()),
|
||||
host: config::Host {
|
||||
public_ips: vec![default_gateway.listening_address],
|
||||
..Default::default()
|
||||
},
|
||||
http: Default::default(),
|
||||
gateway: default_gateway,
|
||||
wireguard: Default::default(),
|
||||
storage_paths: GatewayPaths::new_default(id.as_ref()),
|
||||
network_requester: Default::default(),
|
||||
logging: Default::default(),
|
||||
@@ -182,8 +212,16 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_listening_address(mut self, listening_address: IpAddr) -> Self {
|
||||
pub fn with_host(mut self, listening_address: IpAddr) -> Self {
|
||||
self.gateway.listening_address = listening_address;
|
||||
|
||||
// temporary workaround
|
||||
self.host.public_ips = vec![listening_address];
|
||||
let http_port = self.http.bind_address.port();
|
||||
self.http.bind_address = SocketAddr::new(listening_address, http_port);
|
||||
let wg_port = self.wireguard.bind_address.port();
|
||||
self.wireguard.bind_address = SocketAddr::new(listening_address, wg_port);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
@@ -245,6 +283,11 @@ pub struct Gateway {
|
||||
/// (default: 9000)
|
||||
pub clients_port: u16,
|
||||
|
||||
/// If applicable, announced port for listening for secure websocket client traffic.
|
||||
/// (default: None)
|
||||
#[serde(deserialize_with = "de_maybe_port")]
|
||||
pub clients_wss_port: Option<u16>,
|
||||
|
||||
/// Whether gateway collects and sends anonymized statistics
|
||||
pub enabled_statistics: bool,
|
||||
|
||||
@@ -279,6 +322,7 @@ impl Gateway {
|
||||
listening_address: inaddr_any(),
|
||||
mix_port: DEFAULT_MIX_LISTENING_PORT,
|
||||
clients_port: DEFAULT_CLIENT_LISTENING_PORT,
|
||||
clients_wss_port: None,
|
||||
enabled_statistics: false,
|
||||
statistics_service_url: mainnet::STATISTICS_SERVICE_DOMAIN_ADDRESS
|
||||
.parse()
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::paths::{GatewayPaths, KeysPaths};
|
||||
use crate::config::{Config, Debug, Gateway};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use crate::config::old_config_v1_1_29::{
|
||||
ConfigV1_1_29, DebugV1_1_29, GatewayPathsV1_1_29, GatewayV1_1_29, KeysPathsV1_1_29,
|
||||
LoggingSettingsV1_1_29,
|
||||
};
|
||||
use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
|
||||
};
|
||||
@@ -84,11 +85,13 @@ impl ConfigV1_1_28 {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_28> for Config {
|
||||
impl From<ConfigV1_1_28> for ConfigV1_1_29 {
|
||||
fn from(value: ConfigV1_1_28) -> Self {
|
||||
Config {
|
||||
ConfigV1_1_29 {
|
||||
// \/ ADDED
|
||||
save_path: None,
|
||||
gateway: Gateway {
|
||||
// /\ ADDED
|
||||
gateway: GatewayV1_1_29 {
|
||||
version: value.gateway.version,
|
||||
id: value.gateway.id,
|
||||
only_coconut_credentials: value.gateway.only_coconut_credentials,
|
||||
@@ -101,19 +104,25 @@ impl From<ConfigV1_1_28> for Config {
|
||||
statistics_service_url: value.gateway.statistics_service_url,
|
||||
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
|
||||
},
|
||||
storage_paths: GatewayPaths {
|
||||
keys: KeysPaths {
|
||||
storage_paths: GatewayPathsV1_1_29 {
|
||||
keys: KeysPathsV1_1_29 {
|
||||
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
|
||||
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
|
||||
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
|
||||
public_sphinx_key_file: value.storage_paths.keys.public_sphinx_key_file,
|
||||
},
|
||||
clients_storage: value.storage_paths.clients_storage,
|
||||
|
||||
// \/ ADDED
|
||||
network_requester_config: None,
|
||||
// /\ ADDED
|
||||
},
|
||||
|
||||
// \/ ADDED
|
||||
network_requester: Default::default(),
|
||||
logging: LoggingSettings {},
|
||||
debug: Debug {
|
||||
// /\ ADDED
|
||||
logging: LoggingSettingsV1_1_29 {},
|
||||
debug: DebugV1_1_29 {
|
||||
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
|
||||
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
|
||||
initial_connection_timeout: value.debug.initial_connection_timeout,
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::paths::{GatewayPaths, KeysPaths};
|
||||
use crate::config::{Config, Debug, Gateway, NetworkRequester};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::io;
|
||||
use std::net::IpAddr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
|
||||
|
||||
// 'DEBUG'
|
||||
// where applicable, the below are defined in milliseconds
|
||||
const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000);
|
||||
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
|
||||
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
|
||||
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
|
||||
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
|
||||
|
||||
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
|
||||
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
|
||||
|
||||
/// Derive default path to gateway's config directory.
|
||||
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
|
||||
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
must_get_home()
|
||||
.join(NYM_DIR)
|
||||
.join(DEFAULT_GATEWAYS_DIR)
|
||||
.join(id)
|
||||
.join(DEFAULT_CONFIG_DIR)
|
||||
}
|
||||
|
||||
/// Derive default path to gateways's config file.
|
||||
/// It should get resolved to `$HOME/.nym/gateways/<id>/config/config.toml`
|
||||
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct KeysPathsV1_1_29 {
|
||||
pub private_identity_key_file: PathBuf,
|
||||
pub public_identity_key_file: PathBuf,
|
||||
pub private_sphinx_key_file: PathBuf,
|
||||
pub public_sphinx_key_file: PathBuf,
|
||||
}
|
||||
|
||||
fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let path = PathBuf::deserialize(deserializer)?;
|
||||
if path.as_os_str().is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(path))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct GatewayPathsV1_1_29 {
|
||||
pub keys: KeysPathsV1_1_29,
|
||||
|
||||
#[serde(alias = "persistent_storage")]
|
||||
pub clients_storage: PathBuf,
|
||||
|
||||
#[serde(deserialize_with = "de_maybe_path")]
|
||||
pub network_requester_config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct LoggingSettingsV1_1_29 {}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_29 {
|
||||
#[serde(skip)]
|
||||
pub save_path: Option<PathBuf>,
|
||||
|
||||
pub gateway: GatewayV1_1_29,
|
||||
|
||||
pub storage_paths: GatewayPathsV1_1_29,
|
||||
|
||||
pub network_requester: NetworkRequesterV1_1_29,
|
||||
|
||||
#[serde(default)]
|
||||
pub logging: LoggingSettingsV1_1_29,
|
||||
|
||||
#[serde(default)]
|
||||
pub debug: DebugV1_1_29,
|
||||
}
|
||||
|
||||
impl ConfigV1_1_29 {
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
read_config_from_toml_file(default_config_filepath(id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_29> for Config {
|
||||
fn from(value: ConfigV1_1_29) -> Self {
|
||||
Config {
|
||||
save_path: value.save_path,
|
||||
|
||||
// \/ ADDED
|
||||
host: nym_node::config::Host {
|
||||
public_ips: vec![value.gateway.listening_address],
|
||||
..Default::default()
|
||||
},
|
||||
// /\ ADDED
|
||||
|
||||
// \/ ADDED
|
||||
http: Default::default(),
|
||||
// /\ ADDED
|
||||
gateway: Gateway {
|
||||
version: value.gateway.version,
|
||||
id: value.gateway.id,
|
||||
only_coconut_credentials: value.gateway.only_coconut_credentials,
|
||||
listening_address: value.gateway.listening_address,
|
||||
mix_port: value.gateway.mix_port,
|
||||
clients_port: value.gateway.clients_port,
|
||||
|
||||
// \/ ADDED
|
||||
clients_wss_port: None,
|
||||
// /\ ADDED
|
||||
enabled_statistics: value.gateway.enabled_statistics,
|
||||
nym_api_urls: value.gateway.nym_api_urls,
|
||||
nyxd_urls: value.gateway.nyxd_urls,
|
||||
statistics_service_url: value.gateway.statistics_service_url,
|
||||
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
|
||||
},
|
||||
// \/ ADDED
|
||||
wireguard: Default::default(),
|
||||
// /\ ADDED
|
||||
storage_paths: GatewayPaths {
|
||||
keys: KeysPaths {
|
||||
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
|
||||
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
|
||||
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
|
||||
public_sphinx_key_file: value.storage_paths.keys.public_sphinx_key_file,
|
||||
},
|
||||
clients_storage: value.storage_paths.clients_storage,
|
||||
network_requester_config: value.storage_paths.network_requester_config,
|
||||
},
|
||||
network_requester: NetworkRequester {
|
||||
enabled: value.network_requester.enabled,
|
||||
},
|
||||
logging: LoggingSettings {},
|
||||
debug: Debug {
|
||||
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
|
||||
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
|
||||
initial_connection_timeout: value.debug.initial_connection_timeout,
|
||||
maximum_connection_buffer_size: value.debug.maximum_connection_buffer_size,
|
||||
presence_sending_delay: value.debug.presence_sending_delay,
|
||||
stored_messages_filename_length: value.debug.stored_messages_filename_length,
|
||||
message_retrieval_limit: value.debug.message_retrieval_limit,
|
||||
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct GatewayV1_1_29 {
|
||||
/// Version of the gateway for which this configuration was created.
|
||||
pub version: String,
|
||||
|
||||
/// ID specifies the human readable ID of this particular gateway.
|
||||
pub id: String,
|
||||
|
||||
/// Indicates whether this gateway is accepting only coconut credentials for accessing the
|
||||
/// the mixnet, or if it also accepts non-paying clients
|
||||
#[serde(default)]
|
||||
pub only_coconut_credentials: bool,
|
||||
|
||||
/// Address to which this mixnode will bind to and will be listening for packets.
|
||||
pub listening_address: IpAddr,
|
||||
|
||||
/// Port used for listening for all mixnet traffic.
|
||||
/// (default: 1789)
|
||||
pub mix_port: u16,
|
||||
|
||||
/// Port used for listening for all client-related traffic.
|
||||
/// (default: 9000)
|
||||
pub clients_port: u16,
|
||||
|
||||
/// Whether gateway collects and sends anonymized statistics
|
||||
pub enabled_statistics: bool,
|
||||
|
||||
/// Domain address of the statistics service
|
||||
pub statistics_service_url: Url,
|
||||
|
||||
/// Addresses to APIs from which the node gets the view of the network.
|
||||
#[serde(alias = "validator_api_urls")]
|
||||
pub nym_api_urls: Vec<Url>,
|
||||
|
||||
/// Addresses to validators which the node uses to check for double spending of ERC20 tokens.
|
||||
#[serde(alias = "validator_nymd_urls")]
|
||||
pub nyxd_urls: Vec<Url>,
|
||||
|
||||
/// Mnemonic of a cosmos wallet used in checking for double spending.
|
||||
// #[deprecated(note = "move to storage")]
|
||||
// TODO: I don't think this should be stored directly in the config...
|
||||
pub cosmos_mnemonic: bip39::Mnemonic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct NetworkRequesterV1_1_29 {
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for NetworkRequesterV1_1_29 {
|
||||
fn default() -> Self {
|
||||
NetworkRequesterV1_1_29 { enabled: false }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct DebugV1_1_29 {
|
||||
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
|
||||
/// forwarding sphinx packets.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub packet_forwarding_initial_backoff: Duration,
|
||||
|
||||
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
|
||||
/// forwarding sphinx packets.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub packet_forwarding_maximum_backoff: Duration,
|
||||
|
||||
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub initial_connection_timeout: Duration,
|
||||
|
||||
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
|
||||
pub maximum_connection_buffer_size: usize,
|
||||
|
||||
/// Delay between each subsequent presence data being sent.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub presence_sending_delay: Duration,
|
||||
|
||||
/// Length of filenames for new client messages.
|
||||
pub stored_messages_filename_length: u16,
|
||||
|
||||
/// Number of messages from offline client that can be pulled at once from the storage.
|
||||
pub message_retrieval_limit: i64,
|
||||
|
||||
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
|
||||
// it's set to true by default. The reason for that decision is to preserve compatibility with the
|
||||
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
|
||||
// It shall be disabled in the subsequent releases.
|
||||
pub use_legacy_framed_packet_version: bool,
|
||||
}
|
||||
|
||||
impl Default for DebugV1_1_29 {
|
||||
fn default() -> Self {
|
||||
DebugV1_1_29 {
|
||||
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
|
||||
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
|
||||
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
|
||||
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
|
||||
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
|
||||
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
|
||||
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
|
||||
use_legacy_framed_packet_version: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,18 @@ pub(crate) const CONFIG_TEMPLATE: &str = r#"
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
##### main base mixnode config options #####
|
||||
##### main base gateway config options #####
|
||||
|
||||
[host]
|
||||
# Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections.
|
||||
public_ips = [
|
||||
{{#each host.public_ips }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
# (temporary) Optional hostname of this node, for example nymtech.net.
|
||||
hostname = '{{ host.hostname }}'
|
||||
|
||||
[gateway]
|
||||
# Version of the gateway for which this configuration was created.
|
||||
@@ -33,6 +44,10 @@ mix_port = {{ gateway.mix_port }}
|
||||
# (default: 9000)
|
||||
clients_port = {{ gateway.clients_port }}
|
||||
|
||||
# If applicable, announced port for listening for secure websocket client traffic.
|
||||
# (default: 0 - disabled)
|
||||
clients_wss_port ={{#if gateway.clients_wss_port }} {{ gateway.clients_wss_port }} {{else}} 0 {{/if}}
|
||||
|
||||
# Wheather gateway collects and sends anonymized statistics
|
||||
enabled_statistics = {{ gateway.enabled_statistics }}
|
||||
|
||||
@@ -55,6 +70,14 @@ nyxd_urls = [
|
||||
|
||||
cosmos_mnemonic = '{{ gateway.cosmos_mnemonic }}'
|
||||
|
||||
[http]
|
||||
# Socket address this node will use for binding its http API.
|
||||
# default: `0.0.0.0:8080`
|
||||
bind_address = '{{ http.bind_address }}'
|
||||
|
||||
# Path to assets directory of custom landing page of this node
|
||||
landing_page_assets_path = '{{ http.landing_page_assets_path }}'
|
||||
|
||||
[network_requester]
|
||||
# Specifies whether network requester service is enabled in this process.
|
||||
enabled = {{ network_requester.enabled }}
|
||||
|
||||
@@ -106,6 +106,11 @@ pub(crate) enum GatewayError {
|
||||
#[from]
|
||||
source: NyxdError,
|
||||
},
|
||||
|
||||
// TODO: in the future this should work the other way, i.e. NymNode depending on Gateway errors
|
||||
#[error(transparent)]
|
||||
NymNodeError(#[from] nym_node::error::NymNodeError),
|
||||
|
||||
#[error("Error verifying hmac digest")]
|
||||
HmacDigestError {
|
||||
#[from]
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::GatewayError;
|
||||
use crate::node::helpers::load_public_key;
|
||||
use nym_bin_common::bin_info_owned;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_node::error::NymNodeError;
|
||||
use nym_node::http::api::api_requests;
|
||||
use nym_node::http::api::api_requests::SignedHostInformation;
|
||||
use nym_node::http::router::WireguardAppState;
|
||||
use nym_node::wireguard::types::ClientRegistry;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_task::TaskClient;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
fn load_gateway_details(
|
||||
config: &Config,
|
||||
) -> Result<api_requests::v1::gateway::models::Gateway, GatewayError> {
|
||||
let wireguard = if config.wireguard.enabled {
|
||||
Some(api_requests::v1::gateway::models::Wireguard {
|
||||
port: config.wireguard.announced_port,
|
||||
public_key: "placeholder key value".to_string(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(api_requests::v1::gateway::models::Gateway {
|
||||
client_interfaces: api_requests::v1::gateway::models::ClientInterfaces {
|
||||
wireguard,
|
||||
mixnet_websockets: Some(api_requests::v1::gateway::models::WebSockets {
|
||||
ws_port: config.gateway.clients_port,
|
||||
wss_port: config.gateway.clients_wss_port,
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn load_host_details(
|
||||
config: &Config,
|
||||
sphinx_key: &encryption::PublicKey,
|
||||
identity_keypair: &identity::KeyPair,
|
||||
) -> Result<api_requests::v1::node::models::SignedHostInformation, GatewayError> {
|
||||
let host_info = api_requests::v1::node::models::HostInformation {
|
||||
// TODO: this should be extracted differently, i.e. it's the issue of the public/private address
|
||||
ip_address: config.host.public_ips.clone(),
|
||||
hostname: config.host.hostname.clone(),
|
||||
keys: api_requests::v1::node::models::HostKeys {
|
||||
ed25519: identity_keypair.public_key().to_base58_string(),
|
||||
x25519: sphinx_key.to_base58_string(),
|
||||
},
|
||||
};
|
||||
|
||||
let signed_info = SignedHostInformation::new(host_info, identity_keypair.private_key())
|
||||
.map_err(NymNodeError::from)?;
|
||||
Ok(signed_info)
|
||||
}
|
||||
|
||||
fn load_network_requester_details(
|
||||
config: &Config,
|
||||
network_requester_config: &nym_network_requester::Config,
|
||||
) -> Result<api_requests::v1::network_requester::models::NetworkRequester, GatewayError> {
|
||||
let identity_public_key: identity::PublicKey = load_public_key(
|
||||
&network_requester_config
|
||||
.storage_paths
|
||||
.common_paths
|
||||
.keys
|
||||
.public_identity_key_file,
|
||||
"network requester identity",
|
||||
)?;
|
||||
|
||||
let dh_public_key: encryption::PublicKey = load_public_key(
|
||||
&network_requester_config
|
||||
.storage_paths
|
||||
.common_paths
|
||||
.keys
|
||||
.public_encryption_key_file,
|
||||
"network requester diffie hellman",
|
||||
)?;
|
||||
|
||||
let gateway_identity_public_key: identity::PublicKey = load_public_key(
|
||||
&config.storage_paths.keys.public_identity_key_file,
|
||||
"gateway identity",
|
||||
)?;
|
||||
|
||||
Ok(
|
||||
api_requests::v1::network_requester::models::NetworkRequester {
|
||||
encoded_identity_key: identity_public_key.to_base58_string(),
|
||||
encoded_x25519_key: dh_public_key.to_base58_string(),
|
||||
address: Recipient::new(
|
||||
identity_public_key,
|
||||
dh_public_key,
|
||||
gateway_identity_public_key,
|
||||
)
|
||||
.to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn start_http_api(
|
||||
gateway_config: &Config,
|
||||
network_requester_config: Option<&nym_network_requester::Config>,
|
||||
client_registry: Arc<RwLock<ClientRegistry>>,
|
||||
identity_keypair: &identity::KeyPair,
|
||||
// TODO: this should be a wg specific key and not re-used sphinx
|
||||
sphinx_keypair: Arc<encryption::KeyPair>,
|
||||
|
||||
task_client: TaskClient,
|
||||
) -> Result<(), GatewayError> {
|
||||
// is it suboptimal to load all the keys, etc for the second time after they've already been
|
||||
// retrieved during startup of the rest of the components?
|
||||
// yes, a bit.
|
||||
// but in the grand scheme of things performance penalty is negligible since it's only happening on startup
|
||||
// and makes the code a bit nicer to manage. on top of it, all of it will refactored anyway at some point
|
||||
// (famous last words, eh? - 22.09.23)
|
||||
let mut config = nym_node::http::Config::new(
|
||||
bin_info_owned!(),
|
||||
load_host_details(
|
||||
gateway_config,
|
||||
sphinx_keypair.public_key(),
|
||||
identity_keypair,
|
||||
)?,
|
||||
)
|
||||
.with_gateway(load_gateway_details(gateway_config)?)
|
||||
.with_landing_page_assets(gateway_config.http.landing_page_assets_path.as_ref());
|
||||
|
||||
if let Some(nr_config) = network_requester_config {
|
||||
config = config
|
||||
.with_network_requester(load_network_requester_details(gateway_config, nr_config)?)
|
||||
}
|
||||
|
||||
let wg_state = WireguardAppState::new(sphinx_keypair, client_registry, Default::default());
|
||||
let router = nym_node::http::NymNodeRouter::new(config, Some(wg_state));
|
||||
|
||||
let server = router
|
||||
.build_server(&gateway_config.http.bind_address)?
|
||||
.with_task_client(task_client);
|
||||
tokio::spawn(async move { server.run().await });
|
||||
Ok(())
|
||||
}
|
||||
+3
-1
@@ -17,6 +17,7 @@ use std::error::Error;
|
||||
mod commands;
|
||||
mod config;
|
||||
pub(crate) mod error;
|
||||
mod http;
|
||||
mod node;
|
||||
pub(crate) mod support;
|
||||
|
||||
@@ -46,13 +47,14 @@ struct Cli {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
setup_logging();
|
||||
|
||||
let args = Cli::parse();
|
||||
setup_env(args.config_env_file.as_ref());
|
||||
|
||||
if !args.no_banner {
|
||||
maybe_print_banner(crate_name!(), crate_version!());
|
||||
}
|
||||
setup_logging();
|
||||
|
||||
commands::execute(args).await.map_err(|err| {
|
||||
if atty::is(atty::Stream::Stdout) {
|
||||
|
||||
@@ -1,181 +1,182 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
net::SocketAddr,
|
||||
ops::Deref,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use hmac::{Hmac, Mac};
|
||||
use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use x25519_dalek::StaticSecret;
|
||||
|
||||
use crate::error::GatewayError;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) enum ClientMessage {
|
||||
Init(InitMessage),
|
||||
Final(Client),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) struct InitMessage {
|
||||
pub_key: ClientPublicKey,
|
||||
}
|
||||
|
||||
impl InitMessage {
|
||||
pub fn pub_key(&self) -> &ClientPublicKey {
|
||||
&self.pub_key
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn new(pub_key: ClientPublicKey) -> Self {
|
||||
InitMessage { pub_key }
|
||||
}
|
||||
}
|
||||
|
||||
// Client that wants to register sends its PublicKey and SocketAddr bytes mac digest encrypted with a DH shared secret.
|
||||
// Gateway can then verify pub_key payload using the sme process
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) struct Client {
|
||||
// base64 encoded public key, using x25519-dalek for impl
|
||||
pub(crate) pub_key: ClientPublicKey,
|
||||
pub(crate) socket: SocketAddr,
|
||||
pub(crate) mac: ClientMac,
|
||||
}
|
||||
|
||||
pub type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
impl Client {
|
||||
// Reusable secret should be gateways Wireguard PK
|
||||
// Client should perform this step when generating its payload, using its own WG PK
|
||||
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), GatewayError> {
|
||||
#[allow(clippy::expect_used)]
|
||||
let static_secret =
|
||||
StaticSecret::try_from(gateway_key.to_bytes()).expect("This is infalliable");
|
||||
let dh = static_secret.diffie_hellman(&self.pub_key);
|
||||
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())?;
|
||||
mac.update(self.pub_key.as_bytes());
|
||||
mac.update(self.socket.ip().to_string().as_bytes());
|
||||
mac.update(self.socket.port().to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
Ok(mac.verify_slice(&self.mac)?)
|
||||
}
|
||||
|
||||
pub fn pub_key(&self) -> &ClientPublicKey {
|
||||
&self.pub_key
|
||||
}
|
||||
|
||||
pub fn socket(&self) -> SocketAddr {
|
||||
self.socket
|
||||
}
|
||||
}
|
||||
|
||||
// This should go into nym-wireguard crate
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ClientPublicKey(x25519_dalek::PublicKey);
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ClientMac(Vec<u8>);
|
||||
|
||||
impl ClientMac {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(mac: Vec<u8>) -> Self {
|
||||
ClientMac(mac)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClientMac {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientPublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", general_purpose::STANDARD.encode(self.0.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for ClientPublicKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.as_bytes().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ClientMac {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mac_bytes: Vec<u8> = general_purpose::STANDARD
|
||||
.decode(s)
|
||||
.map_err(|_| "Could not base64 decode public key mac representation".to_string())?;
|
||||
Ok(ClientMac(mac_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClientMac {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded_key = general_purpose::STANDARD.encode(self.0.clone());
|
||||
serializer.serialize_str(&encoded_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientMac {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let encoded_key = String::deserialize(deserializer)?;
|
||||
ClientMac::from_str(&encoded_key).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientPublicKey {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(key: x25519_dalek::PublicKey) -> Self {
|
||||
ClientPublicKey(key)
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClientPublicKey {
|
||||
type Target = x25519_dalek::PublicKey;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ClientPublicKey {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let key_bytes: [u8; 32] = general_purpose::STANDARD
|
||||
.decode(s)
|
||||
.map_err(|_| "Could not base64 decode public key representation".to_string())?
|
||||
.try_into()
|
||||
.map_err(|_| "Invalid key length".to_string())?;
|
||||
Ok(ClientPublicKey(x25519_dalek::PublicKey::from(key_bytes)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClientPublicKey {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded_key = general_purpose::STANDARD.encode(self.0.as_bytes());
|
||||
serializer.serialize_str(&encoded_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientPublicKey {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let encoded_key = String::deserialize(deserializer)?;
|
||||
Ok(ClientPublicKey::from_str(&encoded_key).map_err(serde::de::Error::custom))?
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ClientRegistry = HashMap<SocketAddr, Client>;
|
||||
// use std::{
|
||||
// collections::HashMap,
|
||||
// fmt,
|
||||
// hash::{Hash, Hasher},
|
||||
// net::SocketAddr,
|
||||
// ops::Deref,
|
||||
// str::FromStr,
|
||||
// };
|
||||
//
|
||||
// use base64::{engine::general_purpose, Engine};
|
||||
// use hmac::{Hmac, Mac};
|
||||
// use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
// use serde::{Deserialize, Serialize};
|
||||
// use sha2::Sha256;
|
||||
// use x25519_dalek::StaticSecret;
|
||||
//
|
||||
// use crate::error::GatewayError;
|
||||
//
|
||||
// #[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
// pub(crate) enum ClientMessage {
|
||||
// Init(InitMessage),
|
||||
// Final(Client),
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
// pub(crate) struct InitMessage {
|
||||
// pub_key: ClientPublicKey,
|
||||
// }
|
||||
//
|
||||
// impl InitMessage {
|
||||
// pub fn pub_key(&self) -> &ClientPublicKey {
|
||||
// &self.pub_key
|
||||
// }
|
||||
// #[allow(dead_code)]
|
||||
// pub fn new(pub_key: ClientPublicKey) -> Self {
|
||||
// InitMessage { pub_key }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Client that wants to register sends its PublicKey and SocketAddr bytes mac digest encrypted with a DH shared secret.
|
||||
// // Gateway can then verify pub_key payload using the sme process
|
||||
// #[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
// pub(crate) struct Client {
|
||||
// // base64 encoded public key, using x25519-dalek for impl
|
||||
// pub(crate) pub_key: ClientPublicKey,
|
||||
// pub(crate) socket: SocketAddr,
|
||||
// pub(crate) mac: ClientMac,
|
||||
// }
|
||||
//
|
||||
// pub type HmacSha256 = Hmac<Sha256>;
|
||||
//
|
||||
// impl Client {
|
||||
// // Reusable secret should be gateways Wireguard PK
|
||||
// // Client should perform this step when generating its payload, using its own WG PK
|
||||
// pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), GatewayError> {
|
||||
// #[allow(clippy::expect_used)]
|
||||
// let static_secret =
|
||||
// StaticSecret::try_from(gateway_key.to_bytes()).expect("This is infalliable");
|
||||
// let dh = static_secret.diffie_hellman(&self.pub_key);
|
||||
// let mut mac = HmacSha256::new_from_slice(dh.as_bytes())?;
|
||||
// mac.update(self.pub_key.as_bytes());
|
||||
// mac.update(self.socket.ip().to_string().as_bytes());
|
||||
// mac.update(self.socket.port().to_string().as_bytes());
|
||||
// mac.update(&nonce.to_le_bytes());
|
||||
// Ok(mac.verify_slice(&self.mac)?)
|
||||
// }
|
||||
//
|
||||
// pub fn pub_key(&self) -> &ClientPublicKey {
|
||||
// &self.pub_key
|
||||
// }
|
||||
//
|
||||
// pub fn socket(&self) -> SocketAddr {
|
||||
// self.socket
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // This should go into nym-wireguard crate
|
||||
// #[derive(Debug, Clone, Eq, PartialEq)]
|
||||
// pub struct ClientPublicKey(x25519_dalek::PublicKey);
|
||||
//
|
||||
// #[derive(Debug, Clone)]
|
||||
// pub(crate) struct ClientMac(Vec<u8>);
|
||||
//
|
||||
// impl ClientMac {
|
||||
// #[allow(dead_code)]
|
||||
// pub fn new(mac: Vec<u8>) -> Self {
|
||||
// ClientMac(mac)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Deref for ClientMac {
|
||||
// type Target = Vec<u8>;
|
||||
//
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// &self.0
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl fmt::Display for ClientPublicKey {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// write!(f, "{}", general_purpose::STANDARD.encode(self.0.as_bytes()))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Hash for ClientPublicKey {
|
||||
// fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// self.0.as_bytes().hash(state)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl FromStr for ClientMac {
|
||||
// type Err = String;
|
||||
//
|
||||
// fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// let mac_bytes: Vec<u8> = general_purpose::STANDARD
|
||||
// .decode(s)
|
||||
// .map_err(|_| "Could not base64 decode public key mac representation".to_string())?;
|
||||
// Ok(ClientMac(mac_bytes))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Serialize for ClientMac {
|
||||
// fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
// let encoded_key = general_purpose::STANDARD.encode(self.0.clone());
|
||||
// serializer.serialize_str(&encoded_key)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<'de> Deserialize<'de> for ClientMac {
|
||||
// fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
// let encoded_key = String::deserialize(deserializer)?;
|
||||
// ClientMac::from_str(&encoded_key).map_err(serde::de::Error::custom)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl ClientPublicKey {
|
||||
// #[allow(dead_code)]
|
||||
// pub fn new(key: x25519_dalek::PublicKey) -> Self {
|
||||
// ClientPublicKey(key)
|
||||
// }
|
||||
//
|
||||
// pub fn as_bytes(&self) -> &[u8] {
|
||||
// self.0.as_bytes()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Deref for ClientPublicKey {
|
||||
// type Target = x25519_dalek::PublicKey;
|
||||
//
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// &self.0
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl FromStr for ClientPublicKey {
|
||||
// type Err = String;
|
||||
//
|
||||
// fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// let key_bytes: [u8; 32] = general_purpose::STANDARD
|
||||
// .decode(s)
|
||||
// .map_err(|_| "Could not base64 decode public key representation".to_string())?
|
||||
// .try_into()
|
||||
// .map_err(|_| "Invalid key length".to_string())?;
|
||||
// Ok(ClientPublicKey(x25519_dalek::PublicKey::from(key_bytes)))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Serialize for ClientPublicKey {
|
||||
// fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
// let encoded_key = general_purpose::STANDARD.encode(self.0.as_bytes());
|
||||
// serializer.serialize_str(&encoded_key)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<'de> Deserialize<'de> for ClientPublicKey {
|
||||
// fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
// let encoded_key = String::deserialize(deserializer)?;
|
||||
// Ok(ClientPublicKey::from_str(&encoded_key).map_err(serde::de::Error::custom))?
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub(crate) type ClientRegistry = HashMap<SocketAddr, Client>;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub(crate) mod v1;
|
||||
@@ -1,105 +0,0 @@
|
||||
use axum::extract::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use axum::{extract::State, Json};
|
||||
use std::str::FromStr;
|
||||
|
||||
// use axum_macros::debug_handler;
|
||||
|
||||
use crate::node::client_handling::client_registration::{
|
||||
Client, ClientMessage, ClientPublicKey, InitMessage,
|
||||
};
|
||||
use crate::node::http::ApiState;
|
||||
|
||||
async fn process_final_message(client: Client, state: Arc<ApiState>) -> StatusCode {
|
||||
let preshared_nonce = {
|
||||
let in_progress_ro = state.registration_in_progress.read().await;
|
||||
if let Some(nonce) = in_progress_ro.get(client.pub_key()) {
|
||||
*nonce
|
||||
} else {
|
||||
return StatusCode::BAD_REQUEST;
|
||||
}
|
||||
};
|
||||
|
||||
if client
|
||||
.verify(state.sphinx_key_pair.private_key(), preshared_nonce)
|
||||
.is_ok()
|
||||
{
|
||||
{
|
||||
let mut in_progress_rw = state.registration_in_progress.write().await;
|
||||
in_progress_rw.remove(client.pub_key());
|
||||
}
|
||||
{
|
||||
let mut registry_rw = state.client_registry.write().await;
|
||||
registry_rw.insert(client.socket(), client);
|
||||
}
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
|
||||
async fn process_init_message(init_message: InitMessage, state: Arc<ApiState>) -> u64 {
|
||||
let nonce: u64 = fastrand::u64(..);
|
||||
let mut registry_rw = state.registration_in_progress.write().await;
|
||||
registry_rw.insert(init_message.pub_key().clone(), nonce);
|
||||
nonce
|
||||
}
|
||||
|
||||
// #[debug_handler]
|
||||
pub(crate) async fn register_client(
|
||||
State(state): State<Arc<ApiState>>,
|
||||
Json(payload): Json<ClientMessage>,
|
||||
) -> (StatusCode, Json<Option<u64>>) {
|
||||
match payload {
|
||||
ClientMessage::Init(i) => (
|
||||
StatusCode::OK,
|
||||
Json(Some(process_init_message(i, Arc::clone(&state)).await)),
|
||||
),
|
||||
ClientMessage::Final(client) => (
|
||||
process_final_message(client, Arc::clone(&state)).await,
|
||||
Json(None),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_all_clients(
|
||||
State(state): State<Arc<ApiState>>,
|
||||
) -> (StatusCode, Json<Vec<ClientPublicKey>>) {
|
||||
let registry_ro = state.client_registry.read().await;
|
||||
(
|
||||
StatusCode::OK,
|
||||
Json(
|
||||
registry_ro
|
||||
.values()
|
||||
.map(|c| c.pub_key().clone())
|
||||
.collect::<Vec<ClientPublicKey>>(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_client(
|
||||
Path(pub_key): Path<String>,
|
||||
State(state): State<Arc<ApiState>>,
|
||||
) -> (StatusCode, Json<Vec<Client>>) {
|
||||
let pub_key = match ClientPublicKey::from_str(&pub_key) {
|
||||
Ok(pub_key) => pub_key,
|
||||
Err(_) => return (StatusCode::BAD_REQUEST, Json(vec![])),
|
||||
};
|
||||
let registry_ro = state.client_registry.read().await;
|
||||
let clients = registry_ro
|
||||
.iter()
|
||||
.filter_map(|(_, c)| {
|
||||
if c.pub_key() == &pub_key {
|
||||
Some(c.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Client>>();
|
||||
if clients.is_empty() {
|
||||
return (StatusCode::NOT_FOUND, Json(clients));
|
||||
}
|
||||
(StatusCode::OK, Json(clients))
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub(crate) mod client_registry;
|
||||
+11
-9
@@ -1,11 +1,11 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use self::client_handling::client_registration::ClientRegistry;
|
||||
use self::storage::PersistentStorage;
|
||||
use crate::commands::helpers::{override_network_requester_config, OverrideNetworkRequesterConfig};
|
||||
use crate::config::Config;
|
||||
use crate::error::GatewayError;
|
||||
use crate::http::start_http_api;
|
||||
use crate::node::client_handling::active_clients::ActiveClientsStore;
|
||||
use crate::node::client_handling::embedded_network_requester::{
|
||||
LocalNetworkRequesterHandle, MessageRouter,
|
||||
@@ -13,7 +13,6 @@ use crate::node::client_handling::embedded_network_requester::{
|
||||
use crate::node::client_handling::websocket;
|
||||
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
|
||||
use crate::node::helpers::{initialise_main_storage, load_network_requester_config};
|
||||
use crate::node::http::start_http_api;
|
||||
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
|
||||
use crate::node::statistics::collector::GatewayStatisticsCollector;
|
||||
use crate::node::storage::Storage;
|
||||
@@ -24,6 +23,7 @@ use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_mixnet_client::forwarder::{MixForwardingSender, PacketForwarder};
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use nym_network_requester::{LocalGateway, NRServiceProviderBuilder};
|
||||
use nym_node::wireguard::types::ClientRegistry;
|
||||
use nym_statistics_common::collector::StatisticsSender;
|
||||
use nym_task::{TaskClient, TaskManager};
|
||||
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient};
|
||||
@@ -38,7 +38,6 @@ use tokio::sync::RwLock;
|
||||
|
||||
pub(crate) mod client_handling;
|
||||
pub(crate) mod helpers;
|
||||
pub(crate) mod http;
|
||||
pub(crate) mod mixnet_handling;
|
||||
pub(crate) mod statistics;
|
||||
pub(crate) mod storage;
|
||||
@@ -333,6 +332,15 @@ impl<St> Gateway<St> {
|
||||
CoconutVerifier::new(nyxd_client)
|
||||
};
|
||||
|
||||
start_http_api(
|
||||
&self.config,
|
||||
self.network_requester_opts.as_ref().map(|o| &o.config),
|
||||
self.client_registry.clone(),
|
||||
self.identity_keypair.as_ref(),
|
||||
self.sphinx_keypair.clone(),
|
||||
shutdown.subscribe().named("http-api"),
|
||||
)?;
|
||||
|
||||
let mix_forwarding_channel =
|
||||
self.start_packet_forwarder(shutdown.subscribe().named("PacketForwarder"));
|
||||
|
||||
@@ -384,12 +392,6 @@ impl<St> Gateway<St> {
|
||||
bail!("{err}")
|
||||
}
|
||||
|
||||
// This should likely be wireguard feature gated, but its easier to test if it hangs in here
|
||||
tokio::spawn(start_http_api(
|
||||
Arc::clone(&self.client_registry),
|
||||
Arc::clone(&self.sphinx_keypair),
|
||||
));
|
||||
|
||||
info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!");
|
||||
|
||||
if let Err(err) = Self::wait_for_interrupt(shutdown).await {
|
||||
|
||||
@@ -7,7 +7,7 @@ rust-version = "1.56"
|
||||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
k256 = { workspace = true, features = ["ecdsa", "sha256"] }
|
||||
eyre = "0.6.5"
|
||||
|
||||
|
||||
+5
-5
@@ -16,7 +16,7 @@ rust-version = "1.56"
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
bs58 = {version = "0.4.0" }
|
||||
bs58 = { version = "0.4.0" }
|
||||
bip39 = { workspace = true }
|
||||
cfg-if = "1.0"
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
@@ -27,7 +27,6 @@ humantime-serde = "1.0"
|
||||
lazy_static = "1.4.0"
|
||||
log = { workspace = true }
|
||||
pin-project = "1.0"
|
||||
pretty_env_logger = "0.4.0"
|
||||
rand = "0.8.5"
|
||||
rand-07 = { package = "rand", version = "0.7.3" } # required for compatibility
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
@@ -49,7 +48,7 @@ url = { workspace = true }
|
||||
|
||||
ts-rs = { workspace = true, optional = true}
|
||||
|
||||
anyhow = "1.0"
|
||||
anyhow = { workspace = true }
|
||||
getset = "0.1.1"
|
||||
|
||||
sqlx = { version = "0.6.2", features = [
|
||||
@@ -61,7 +60,7 @@ sqlx = { version = "0.6.2", features = [
|
||||
|
||||
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
|
||||
rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] }
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
## ephemera-specific
|
||||
@@ -106,13 +105,14 @@ nym-api-requests = { path = "nym-api-requests" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-bin-common = { path = "../common/bin-common" }
|
||||
nym-node-tester-utils = { path = "../common/node-tester-utils" }
|
||||
nym-node-requests = { path = "../nym-node/nym-node-requests" }
|
||||
|
||||
[features]
|
||||
no-reward = []
|
||||
generate-ts = ["ts-rs"]
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { version = "0.6.2", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
|
||||
@@ -10,13 +10,14 @@ bs58 = "0.4.0"
|
||||
cosmrs = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
getset = "0.1.1"
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
|
||||
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||
nym-mixnet-contract-common = { path= "../../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||
nym-node-requests = { path = "../../nym-node/nym-node-requests", default-features = false }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
generate-ts = ["ts-rs"]
|
||||
generate-ts = ["ts-rs", "nym-mixnet-contract-common/generate-ts"]
|
||||
|
||||
@@ -9,8 +9,11 @@ use nym_mixnet_contract_common::rewarding::RewardEstimate;
|
||||
use nym_mixnet_contract_common::{
|
||||
GatewayBond, IdentityKey, Interval, MixId, MixNode, Percent, RewardedSetNodeStatus,
|
||||
};
|
||||
use nym_node_requests::api::v1::gateway::models::WebSockets;
|
||||
use nym_node_requests::api::v1::node::models::{BinaryBuildInformationOwned, HostInformation};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::{fmt, time::Duration};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
@@ -30,6 +33,12 @@ impl RequestError {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RequestError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
self.message.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
@@ -135,6 +144,10 @@ impl MixNodeBondAnnotated {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct GatewayBondAnnotated {
|
||||
pub gateway_bond: GatewayBond,
|
||||
|
||||
#[serde(default)]
|
||||
pub self_described: Option<GatewayDescription>,
|
||||
|
||||
// NOTE: the performance field is deprecated in favour of node_performance
|
||||
pub performance: Performance,
|
||||
pub node_performance: NodePerformance,
|
||||
@@ -151,6 +164,11 @@ impl GatewayBondAnnotated {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct GatewayDescription {
|
||||
// for now only expose what we need. this struct will evolve in the future (or be incorporated into nym-node properly)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ComputeRewardEstParam {
|
||||
pub performance: Option<Performance>,
|
||||
@@ -337,3 +355,29 @@ pub struct CirculatingSupplyResponse {
|
||||
pub vesting_tokens: Coin,
|
||||
pub circulating_supply: Coin,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema)]
|
||||
pub struct NymNodeDescription {
|
||||
pub host_information: HostInformation,
|
||||
|
||||
// TODO: do we really care about ALL build info or just the version?
|
||||
pub build_information: BinaryBuildInformationOwned,
|
||||
|
||||
// for now we only care about their ws/wss situation, nothing more
|
||||
pub mixnet_websockets: WebSockets,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, schemars::JsonSchema)]
|
||||
pub struct DescribedGateway {
|
||||
pub bond: GatewayBond,
|
||||
pub self_described: Option<NymNodeDescription>,
|
||||
}
|
||||
|
||||
impl From<GatewayBond> for DescribedGateway {
|
||||
fn from(bond: GatewayBond) -> Self {
|
||||
DescribedGateway {
|
||||
bond,
|
||||
self_described: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -30,9 +30,9 @@ impl<'a> From<&'a CirculatingSupplyCacheData> for CirculatingSupplyResponse {
|
||||
fn from(value: &'a CirculatingSupplyCacheData) -> Self {
|
||||
CirculatingSupplyResponse {
|
||||
total_supply: value.total_supply.clone().into(),
|
||||
mixmining_reserve: value.mixmining_reserve.clone().into_inner().into(),
|
||||
vesting_tokens: value.vesting_tokens.clone().into_inner().into(),
|
||||
circulating_supply: value.circulating_supply.clone().into_inner().into(),
|
||||
mixmining_reserve: value.mixmining_reserve.clone().into(),
|
||||
vesting_tokens: value.vesting_tokens.clone().into(),
|
||||
circulating_supply: value.circulating_supply.clone().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -85,8 +85,10 @@ impl CirculatingSupplyCache {
|
||||
log::info!("the number of tokens still vesting is now {vesting_tokens}");
|
||||
log::info!("the circulating supply is now {circulating_supply}");
|
||||
|
||||
cache.mixmining_reserve.update(mixmining_reserve);
|
||||
cache.vesting_tokens.update(vesting_tokens);
|
||||
cache.circulating_supply.update(circulating_supply);
|
||||
cache.mixmining_reserve.unchecked_update(mixmining_reserve);
|
||||
cache.vesting_tokens.unchecked_update(vesting_tokens);
|
||||
cache
|
||||
.circulating_supply
|
||||
.unchecked_update(circulating_supply);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ extern crate rocket;
|
||||
|
||||
use crate::epoch_operations::RewardedSetUpdater;
|
||||
use crate::network::models::NetworkDetails;
|
||||
use crate::node_describe_cache::DescribedNodes;
|
||||
use crate::node_status_api::uptime_updater::HistoricalUptimeUpdater;
|
||||
use crate::support::caching::cache::SharedCache;
|
||||
use crate::support::cli;
|
||||
use crate::support::cli::CliArgs;
|
||||
use crate::support::config::Config;
|
||||
@@ -34,8 +36,10 @@ mod ephemera;
|
||||
mod epoch_operations;
|
||||
pub(crate) mod network;
|
||||
mod network_monitor;
|
||||
pub(crate) mod node_describe_cache;
|
||||
pub(crate) mod node_status_api;
|
||||
pub(crate) mod nym_contract_cache;
|
||||
pub(crate) mod nym_nodes;
|
||||
pub(crate) mod support;
|
||||
|
||||
struct ShutdownHandles {
|
||||
@@ -90,6 +94,19 @@ async fn start_nym_api_tasks(
|
||||
let node_status_cache_state = rocket.state::<NodeStatusCache>().unwrap();
|
||||
let circulating_supply_cache_state = rocket.state::<CirculatingSupplyCache>().unwrap();
|
||||
let maybe_storage = rocket.state::<NymApiStorage>();
|
||||
let described_nodes_state = rocket.state::<SharedCache<DescribedNodes>>().unwrap();
|
||||
|
||||
// start note describe cache refresher
|
||||
// we should be doing the below, but can't due to our current startup structure
|
||||
// let refresher = node_describe_cache::new_refresher(&config.topology_cacher);
|
||||
// let cache = refresher.get_shared_cache();
|
||||
node_describe_cache::new_refresher_with_initial_value(
|
||||
&config.topology_cacher,
|
||||
nym_contract_cache_state.clone(),
|
||||
described_nodes_state.to_owned(),
|
||||
)
|
||||
.named("node-self-described-data-refresher")
|
||||
.start(shutdown.subscribe_named("node-self-described-data-refresher"));
|
||||
|
||||
// start all the caches first
|
||||
let nym_contract_cache_listener = nym_contract_cache::start_refresher(
|
||||
@@ -98,6 +115,7 @@ async fn start_nym_api_tasks(
|
||||
nyxd_client.clone(),
|
||||
&shutdown,
|
||||
);
|
||||
|
||||
node_status_api::start_cache_refresh(
|
||||
&config.node_status_api,
|
||||
nym_contract_cache_state,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user