Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f1321e1b0 | |||
| 76953df4bb | |||
| ce2449f86a | |||
| 598014bf30 | |||
| 73fe7618db | |||
| 7a416f8cf5 | |||
| 3077c2ea8d | |||
| be6c63723c | |||
| c5a3cb7707 | |||
| cce9f0b183 | |||
| 84b74703b2 | |||
| 9bf3600e5b | |||
| 9f20c8ed1f | |||
| 84e66c34f2 | |||
| e04df37988 | |||
| c91412f949 | |||
| 0a89f31a29 | |||
| 9badeac832 | |||
| b59c41d9cd | |||
| 8f083ff91e | |||
| 0f44836025 | |||
| 68ee2d747d | |||
| e29c76678d | |||
| a4005c7d81 | |||
| efe6d916e2 | |||
| 910b6a1369 | |||
| 7818658ee8 | |||
| 89e34b4fd3 | |||
| 2f5a00dbda | |||
| 7f87d42f9a | |||
| 93b12bccca | |||
| 89fb4ef03f | |||
| b8ab187db0 | |||
| a9790c1f66 | |||
| b46634b8f7 | |||
| 633e7ffb46 | |||
| dd2077bf12 | |||
| 0323ba2bb9 | |||
| 07cc47a0ff |
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: 1.77.0
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
|
||||
@@ -47,6 +47,17 @@ jobs:
|
||||
- name: Build release contracts
|
||||
run: make contracts
|
||||
|
||||
- name: Install CosmWasm optimizer
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y docker.io
|
||||
|
||||
- name: Optimize WASM contracts
|
||||
run: |
|
||||
docker run --rm -v $(pwd)/contracts/target/wasm32-unknown-unknown/release:/code \
|
||||
--mount type=volume,source=cosmwasm_cache,target=/code/cache \
|
||||
cosmwasm/workspace-optimizer:0.12.9
|
||||
|
||||
- name: Prepare build output
|
||||
shell: bash
|
||||
env:
|
||||
@@ -58,8 +69,7 @@ jobs:
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_ecash.wasm $OUTPUT_DIR
|
||||
|
||||
- name: Deploy branch to CI www
|
||||
continue-on-error: true
|
||||
|
||||
Generated
+73
-18
@@ -714,9 +714,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
|
||||
checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -2203,7 +2203,7 @@ dependencies = [
|
||||
"bip39",
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
"serde-wasm-bindgen",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
@@ -2586,14 +2586,14 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-net"
|
||||
version = "0.3.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620"
|
||||
checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"gloo-utils",
|
||||
"gloo-utils 0.2.0",
|
||||
"http 0.2.12",
|
||||
"js-sys",
|
||||
"pin-project",
|
||||
@@ -2630,6 +2630,19 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-utils"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.13.0"
|
||||
@@ -3677,7 +3690,7 @@ dependencies = [
|
||||
"nym-socks5-requests",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tsify",
|
||||
@@ -4312,7 +4325,7 @@ dependencies = [
|
||||
"nym-node-tester-wasm",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tsify",
|
||||
@@ -4635,7 +4648,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"getrandom 0.2.15",
|
||||
"gloo-utils",
|
||||
"gloo-utils 0.2.0",
|
||||
"log",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-credential-storage",
|
||||
@@ -4702,6 +4715,7 @@ name = "nym-http-api-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"http 1.1.0",
|
||||
"reqwest 0.12.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4770,6 +4784,7 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
@@ -5138,6 +5153,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.7",
|
||||
"celes",
|
||||
"humantime 2.1.0",
|
||||
"humantime-serde",
|
||||
"nym-bin-common",
|
||||
@@ -5183,7 +5199,7 @@ dependencies = [
|
||||
"nym-node-tester-utils",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tsify",
|
||||
@@ -5266,7 +5282,7 @@ dependencies = [
|
||||
"dotenvy",
|
||||
"futures",
|
||||
"hex",
|
||||
"http 0.2.12",
|
||||
"http 1.1.0",
|
||||
"httpcodec",
|
||||
"log",
|
||||
"nym-bandwidth-controller",
|
||||
@@ -6575,9 +6591,9 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
|
||||
|
||||
[[package]]
|
||||
name = "psl"
|
||||
version = "2.1.38"
|
||||
version = "2.1.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a66783ea3ba6140ffd59462fa46585d7cc0797893fbf3835ad2da832319964c"
|
||||
checksum = "7b320cda4ad7e8f4269fa415754418f83b38c666a5e2e99ea48825b274a373f3"
|
||||
dependencies = [
|
||||
"psl-types",
|
||||
]
|
||||
@@ -7605,6 +7621,17 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.14"
|
||||
@@ -9026,9 +9053,9 @@ version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6b26cf145f2f3b9ff84e182c448eaf05468e247f148cf3d2a7d67d78ff023a0"
|
||||
dependencies = [
|
||||
"gloo-utils",
|
||||
"gloo-utils 0.1.7",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde-wasm-bindgen 0.5.0",
|
||||
"serde_json",
|
||||
"tsify-macros",
|
||||
"wasm-bindgen",
|
||||
@@ -9289,6 +9316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -9470,7 +9498,7 @@ dependencies = [
|
||||
"nym-validator-client",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tsify",
|
||||
@@ -9492,7 +9520,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"nym-store-cipher",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"wasm-utils",
|
||||
@@ -9518,7 +9546,7 @@ dependencies = [
|
||||
"futures",
|
||||
"getrandom 0.2.15",
|
||||
"gloo-net",
|
||||
"gloo-utils",
|
||||
"gloo-utils 0.2.0",
|
||||
"js-sys",
|
||||
"tungstenite",
|
||||
"wasm-bindgen",
|
||||
@@ -10019,3 +10047,30 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
"flate2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zknym-lib"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"bs58 0.5.1",
|
||||
"getrandom 0.2.15",
|
||||
"js-sys",
|
||||
"nym-bin-common",
|
||||
"nym-coconut",
|
||||
"nym-credentials",
|
||||
"nym-crypto",
|
||||
"nym-http-api-client",
|
||||
"rand 0.7.3",
|
||||
"reqwest 0.12.4",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tsify",
|
||||
"uuid 1.6.1",
|
||||
"wasm-bindgen",
|
||||
"wasm-utils",
|
||||
"wasmtimer",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
+111
-13
@@ -122,6 +122,7 @@ members = [
|
||||
# "wasm/full-nym-wasm",
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/zknym-lib",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -158,46 +159,142 @@ edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
addr = "0.15.6"
|
||||
aes = "0.8.1"
|
||||
aes-gcm = "0.10.1"
|
||||
anyhow = "1.0.71"
|
||||
argon2 = "0.5.0"
|
||||
async-trait = "0.1.68"
|
||||
axum = "0.7.5"
|
||||
axum-extra = "0.9.3"
|
||||
base64 = "0.21.4"
|
||||
bs58 = "0.5.0"
|
||||
bincode = "1.3.3"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
clap = "4.4.7"
|
||||
bitvec = "1.0.0"
|
||||
blake3 = "1.3.1"
|
||||
bs58 = "0.5.0"
|
||||
bytecodec = "0.4.15"
|
||||
bytes = "1.5.0"
|
||||
cargo_metadata = "0.18.1"
|
||||
celes = "2.4.0"
|
||||
cfg-if = "1.0.0"
|
||||
chacha20 = "0.9.0"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
chrono = "0.4.31"
|
||||
cipher = "0.4.3"
|
||||
clap = "4.4.7"
|
||||
clap_complete = "4.0"
|
||||
clap_complete_fig = "4.0"
|
||||
colored = "2.0"
|
||||
comfy-table = "6.0.0"
|
||||
console-subscriber = "0.1.1"
|
||||
console_error_panic_hook = "0.1"
|
||||
const-str = "0.5.6"
|
||||
const_format = "0.2.32"
|
||||
criterion = "0.4"
|
||||
csv = "1.3.0"
|
||||
ctr = "0.9.1"
|
||||
cupid = "0.6.1"
|
||||
curve25519-dalek = "3.2"
|
||||
dashmap = "5.5.3"
|
||||
defguard_wireguard_rs = { git = "https://github.com/neacsu/wireguard-rs.git", rev = "c2cd0c1119f699f4bc43f5e6ffd6fc242caa42ed" }
|
||||
doc-comment = "0.3"
|
||||
dotenvy = "0.15.6"
|
||||
ecdsa = "0.16"
|
||||
ed25519-dalek = "1.0"
|
||||
etherparse = "0.13.0"
|
||||
eyre = "0.6.9"
|
||||
flate2 = "1.0.28"
|
||||
futures = "0.3.28"
|
||||
generic-array = "0.14.7"
|
||||
getrandom = "0.2.10"
|
||||
getset = "0.1.1"
|
||||
handlebars = "3.5.5"
|
||||
headers = "0.4.0"
|
||||
hex = "0.4.3"
|
||||
hex-literal = "0.3.3"
|
||||
hkdf = "0.12.3"
|
||||
hmac = "0.12.1"
|
||||
httpcodec = "0.2.3"
|
||||
humantime = "2.1.0"
|
||||
humantime-serde = "1.1.1"
|
||||
http = "1"
|
||||
hyper = "1.3.1"
|
||||
indexed_db_futures = "0.3.0"
|
||||
inquire = "0.6.2"
|
||||
ip_network = "0.4.1"
|
||||
isocountry = "0.3.2"
|
||||
k256 = "0.13"
|
||||
lazy_static = "1.4.0"
|
||||
ledger-transport = "0.10.0"
|
||||
ledger-transport-hid = "0.10.0"
|
||||
log = "0.4"
|
||||
maxminddb = "0.23.0"
|
||||
mime = "0.3.17"
|
||||
nix = "0.27.1"
|
||||
notify = "5.1.0"
|
||||
okapi = "0.7.0"
|
||||
once_cell = "1.7.2"
|
||||
opentelemetry = "0.19.0"
|
||||
opentelemetry-jaeger = "0.18.0"
|
||||
parking_lot = "0.12.1"
|
||||
pem = "0.8"
|
||||
pin-project = "1.0"
|
||||
pretty_env_logger = "0.4.0"
|
||||
publicsuffix = "2.2.3"
|
||||
quote = "1"
|
||||
rand = "0.8.5"
|
||||
rand-07 = "0.7.3"
|
||||
rand_chacha_02 = "0.2"
|
||||
rand_core = "0.6.3"
|
||||
rand_distr = "0.3"
|
||||
rand_pcg = "0.3.1"
|
||||
rand_seeder = "0.2.3"
|
||||
rayon = "1.5.1"
|
||||
regex = "1.8.4"
|
||||
reqwest = { version = "0.12.4", default-features = false }
|
||||
rocket = "0.5.0"
|
||||
rocket_cors = "0.6.0"
|
||||
rocket_okapi = "0.8.0"
|
||||
safer-ffi = "0.1.4"
|
||||
schemars = "0.8.1"
|
||||
serde = "1.0.152"
|
||||
serde_bytes = "0.11.6"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0.91"
|
||||
serde_repr = "0.1"
|
||||
serde_with = "3.4.0"
|
||||
serde_yaml = "0.9.25"
|
||||
si-scale = "0.2.2"
|
||||
sphinx-packet = "0.1.0"
|
||||
sqlx = "0.6.3"
|
||||
strum = "0.25"
|
||||
subtle-encoding = "0.5"
|
||||
syn = "1"
|
||||
tap = "1.0.1"
|
||||
time = "0.3.30"
|
||||
tar = "0.4.40"
|
||||
tempfile = "3.5.0"
|
||||
thiserror = "1.0.48"
|
||||
time = "0.3.30"
|
||||
tokio = "1.33.0"
|
||||
tokio-util = "0.7.10"
|
||||
tokio-stream = "0.1.14"
|
||||
tokio-test = "0.4.2"
|
||||
tokio-tungstenite = { version = "0.20.1" }
|
||||
tokio-util = "0.7.10"
|
||||
tower = "0.4.13"
|
||||
tower-http = "0.5.2"
|
||||
tracing = "0.1.37"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
tracing-opentelemetry = "0.19.0"
|
||||
tracing-subscriber = "0.3.16"
|
||||
tracing-tree = "0.2.2"
|
||||
ts-rs = "7.0.0"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
url = "2.4"
|
||||
utoipa = "4.2.0"
|
||||
utoipa-swagger-ui = "6.0.0"
|
||||
url = "2.4"
|
||||
vergen = { version = "=8.2.6", default-features = false }
|
||||
walkdir = "2"
|
||||
wasm-bindgen-test = "0.3.36"
|
||||
zeroize = "1.6.0"
|
||||
|
||||
prometheus = { version = "0.13.0" }
|
||||
@@ -209,7 +306,6 @@ bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch = "feature/g
|
||||
group = "0.13.0"
|
||||
ff = "0.13.0"
|
||||
|
||||
|
||||
# cosmwasm-related
|
||||
cosmwasm-derive = "=1.4.3"
|
||||
cosmwasm-schema = "=1.4.3"
|
||||
@@ -237,14 +333,16 @@ tendermint-rpc = "0.34" # same version as used by cosmrs
|
||||
prost = "0.12"
|
||||
|
||||
# wasm-related dependencies
|
||||
gloo-utils = "0.1.7"
|
||||
js-sys = "0.3.63"
|
||||
serde-wasm-bindgen = "0.5.0"
|
||||
gloo-utils = "0.2.0"
|
||||
gloo-net = "0.5.0"
|
||||
js-sys = "0.3.69"
|
||||
serde-wasm-bindgen = "0.6.5"
|
||||
tsify = "0.4.5"
|
||||
wasm-bindgen = "0.2.86"
|
||||
wasm-bindgen-futures = "0.4.37"
|
||||
wasm-bindgen = "0.2.92"
|
||||
wasm-bindgen-futures = "0.4.39"
|
||||
wasmtimer = "0.2.0"
|
||||
web-sys = "0.3.63"
|
||||
web-sys = "0.3.69"
|
||||
|
||||
|
||||
# Profile settings for individual crates
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ sdk-wasm-build:
|
||||
$(MAKE) -C wasm/client
|
||||
$(MAKE) -C wasm/node-tester
|
||||
$(MAKE) -C wasm/mix-fetch
|
||||
$(MAKE) -C wasm/zknym-lib
|
||||
#$(MAKE) -C wasm/full-nym-wasm
|
||||
|
||||
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
|
||||
@@ -115,7 +116,7 @@ sdk-typescript-build:
|
||||
yarn --cwd sdk/typescript/codegen/contract-clients build
|
||||
|
||||
# NOTE: These targets are part of the main workspace (but not as wasm32-unknown-unknown)
|
||||
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm
|
||||
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm zknym-lib
|
||||
|
||||
sdk-wasm-test:
|
||||
#cargo test $(addprefix -p , $(WASM_CRATES)) --target wasm32-unknown-unknown -- -Dwarnings
|
||||
|
||||
@@ -29,7 +29,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits +
|
||||
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tap = "1.0.1"
|
||||
tap = { workspace = true }
|
||||
time = { workspace = true }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||
tokio-tungstenite = { workspace = true }
|
||||
|
||||
@@ -13,9 +13,9 @@ clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
log = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
|
||||
serde_json = { workspace = true }
|
||||
tap = "1.0.1"
|
||||
tap = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] }
|
||||
rand = "0.7.3"
|
||||
time = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
@@ -8,6 +8,6 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
futures = { workspace = true }
|
||||
log = "0.4"
|
||||
notify = "5.1.0"
|
||||
log = { workspace = true }
|
||||
notify = { workspace = true }
|
||||
tokio = { workspace = true, features = ["time"] }
|
||||
|
||||
@@ -8,40 +8,28 @@ license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
const-str = "0.5.6"
|
||||
const-str = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
clap_complete = "4.0"
|
||||
clap_complete_fig = "4.0"
|
||||
clap_complete = { workspace = true }
|
||||
clap_complete_fig = { workspace = true }
|
||||
log = { workspace = true }
|
||||
pretty_env_logger = "0.4.0"
|
||||
pretty_env_logger = { workspace = true }
|
||||
semver = "0.11"
|
||||
schemars = { workspace = true, features = ["preserve_order"], optional = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
|
||||
## tracing
|
||||
tracing-subscriber = { version = "0.3.16", features = [
|
||||
"env-filter",
|
||||
], optional = true }
|
||||
tracing-tree = { version = "0.2.2", optional = true }
|
||||
opentelemetry-jaeger = { version = "0.18.0", optional = true, features = [
|
||||
"rt-tokio",
|
||||
"collector_client",
|
||||
"isahc_collector_client",
|
||||
] }
|
||||
tracing-opentelemetry = { version = "0.19.0", optional = true }
|
||||
tracing-subscriber = { workspace = true, features = ["env-filter"], optional = true }
|
||||
tracing-tree = { workspace = true, optional = true }
|
||||
opentelemetry-jaeger = { workspace = true, features = ["rt-tokio", "collector_client", "isahc_collector_client"], optional = true }
|
||||
tracing-opentelemetry = { workspace = true, optional = true }
|
||||
utoipa = { workspace = true, optional = true }
|
||||
opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
|
||||
opentelemetry = { workspace = true, features = ["rt-tokio"], optional = true }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "=8.2.6", default-features = false, features = [
|
||||
"build",
|
||||
"git",
|
||||
"gitcl",
|
||||
"rustc",
|
||||
"cargo",
|
||||
] }
|
||||
vergen = { workspace = true, features = ["build", "git", "gitcl", "rustc", "cargo"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -12,7 +12,7 @@ license.workspace = true
|
||||
async-trait = { workspace = true }
|
||||
base64 = "0.21.2"
|
||||
bs58 = { workspace = true }
|
||||
cfg-if = "1.0.0"
|
||||
cfg-if = { workspace = true }
|
||||
clap = { workspace = true, optional = true }
|
||||
futures = { workspace = true }
|
||||
humantime-serde = { workspace = true }
|
||||
@@ -21,8 +21,8 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sha2 = "0.10.6"
|
||||
si-scale = "0.2.2"
|
||||
tap = "1.0.1"
|
||||
si-scale = { workspace = true }
|
||||
tap = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
tokio = { workspace = true, features = ["macros"] }
|
||||
@@ -108,7 +108,7 @@ version = "0.3.17"
|
||||
features = ["wasm-bindgen"]
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -15,8 +15,8 @@ log = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
tokio = { version = "1.24.1", features = ["macros"] }
|
||||
si-scale = "0.2.2"
|
||||
tokio = { workspace = true, features = ["macros"] }
|
||||
si-scale = { workspace = true }
|
||||
time.workspace = true
|
||||
|
||||
# internal
|
||||
|
||||
@@ -10,7 +10,7 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13"
|
||||
colored = "2.0"
|
||||
colored = { workspace = true }
|
||||
|
||||
nym-coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" }
|
||||
nym-contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common" }
|
||||
@@ -48,14 +48,14 @@ tendermint-rpc = { workspace = true }
|
||||
# of cargo's feature unification we'd get `bip32/std` meaning we'd get `std::error::Error` for the re-exported (via cosmrs) bip32::Error type
|
||||
bip32 = { workspace = true, default-features = false, features = ["std"] }
|
||||
|
||||
eyre = { version = "0.6" }
|
||||
eyre = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
prost = { workspace = true, default-features = false }
|
||||
flate2 = { version = "1.0.20" }
|
||||
flate2 = { workspace = true }
|
||||
sha2 = { version = "0.9.5" }
|
||||
itertools = { version = "0.10" }
|
||||
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
|
||||
@@ -10,26 +10,26 @@ anyhow = { workspace = true }
|
||||
base64 = "0.13.0"
|
||||
bip39 = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
comfy-table = "6.0.0"
|
||||
cfg-if = "1.0.0"
|
||||
comfy-table = { workspace = true }
|
||||
cfg-if = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
csv = "1.3.0"
|
||||
csv = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
handlebars = "3.0.1"
|
||||
handlebars = { workspace = true }
|
||||
humantime-serde = { workspace = true }
|
||||
inquire = "0.6.2"
|
||||
inquire = { workspace = true }
|
||||
k256 = { workspace = true, features = ["ecdsa", "sha256"] }
|
||||
log = { workspace = true }
|
||||
rand = {version = "0.6", features = ["std"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
time = { workspace = true, features = ["parsing", "formatting"] }
|
||||
tokio = { workspace = true, features = ["sync"]}
|
||||
toml = "0.5.6"
|
||||
url = { workspace = true }
|
||||
tap = "1"
|
||||
tap = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
cosmrs = { workspace = true }
|
||||
|
||||
@@ -9,7 +9,7 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
dirs = { version = "5.0.1", optional = true }
|
||||
handlebars = "3.5.5"
|
||||
handlebars = { workspace = true }
|
||||
log = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
toml = "0.7.4"
|
||||
|
||||
@@ -11,7 +11,7 @@ repository = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-schema = { workspace = true }
|
||||
schemars = "0.8"
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
|
||||
@@ -10,5 +10,5 @@ license.workspace = true
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
schemars = "0.8"
|
||||
schemars = { workspace = true }
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -14,14 +14,14 @@ cosmwasm-std = { workspace = true }
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cw2 = { workspace = true, optional = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_repr = "0.1"
|
||||
serde_repr = { workspace = true }
|
||||
|
||||
# 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"
|
||||
schemars = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
|
||||
serde-json-wasm = { workspace = true }
|
||||
humantime-serde = "1.1.1"
|
||||
humantime-serde = { workspace = true }
|
||||
|
||||
# TO CHECK WHETHER STILL NEEDED:
|
||||
log = { workspace = true }
|
||||
|
||||
@@ -13,6 +13,6 @@ cw4 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8"
|
||||
schemars = { workspace = true }
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -13,7 +13,7 @@ cosmwasm-schema = { workspace = true }
|
||||
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"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
ts-rs = { workspace = true, optional = true}
|
||||
|
||||
|
||||
@@ -26,4 +26,4 @@ features = [ "rt-multi-thread", "net", "signal", "fs" ]
|
||||
|
||||
[build-dependencies]
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
@@ -11,11 +11,8 @@ use nym_validator_client::nyxd::contract_traits::{
|
||||
};
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute
|
||||
|
||||
pub async fn issue_credential<C>(
|
||||
client: &C,
|
||||
amount: Coin,
|
||||
@@ -92,13 +89,6 @@ where
|
||||
.as_secs();
|
||||
|
||||
if epoch.state.is_final() {
|
||||
if let Some(finish_timestamp) = epoch.deadline {
|
||||
if current_timestamp_secs + SAFETY_BUFFER_SECS >= finish_timestamp.seconds() {
|
||||
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
} else if let Some(final_timestamp) = epoch.final_timestamp_secs() {
|
||||
// Use 1 additional second to not start the next iteration immediately and spam get_current_epoch queries
|
||||
|
||||
@@ -8,7 +8,7 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bls12_381 = { workspace = true, default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
bincode = "1.3.3"
|
||||
bincode = { workspace = true }
|
||||
cosmrs = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
@@ -155,11 +155,6 @@ impl IssuedBandwidthCredential {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn randomise_signature(&mut self) {
|
||||
let signature_prime = self.signature.randomise(bandwidth_credential_params());
|
||||
self.signature = signature_prime.0
|
||||
}
|
||||
|
||||
pub fn default_parameters() -> Parameters {
|
||||
IssuanceBandwidthCredential::default_parameters()
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@ impl<'a> From<&'a BandwidthVoucherIssuanceData> for BandwidthVoucherIssuedData {
|
||||
}
|
||||
|
||||
impl BandwidthVoucherIssuedData {
|
||||
pub fn new(value: Coin) -> Self {
|
||||
BandwidthVoucherIssuedData { value }
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &Coin {
|
||||
&self.value
|
||||
}
|
||||
|
||||
@@ -8,21 +8,21 @@ license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
aes = { version = "0.8.1", optional = true }
|
||||
aes = { workspace = true, optional = true }
|
||||
bs58 = { workspace = true }
|
||||
blake3 = { version = "1.3.1", features = ["traits-preview"], optional = true }
|
||||
ctr = { version = "0.9.1", optional = true }
|
||||
blake3 = { workspace = true, features = ["traits-preview"], optional = true }
|
||||
ctr = { workspace = true, optional = true }
|
||||
digest = { version = "0.10.3", optional = true }
|
||||
generic-array = { workspace = true, optional = true }
|
||||
hkdf = { version = "0.12.3", optional = true }
|
||||
hmac = { version = "0.12.1", optional = true }
|
||||
cipher = { version = "0.4.3", optional = true }
|
||||
hkdf = { workspace = true, optional = true }
|
||||
hmac = { workspace = true, optional = true }
|
||||
cipher = { workspace = true, optional = true }
|
||||
x25519-dalek = { version = "1.1", optional = true }
|
||||
ed25519-dalek = { version = "1.0", optional = true }
|
||||
ed25519-dalek = { workspace = true, optional = true }
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"], optional = true }
|
||||
serde_bytes = { version = "0.11.6", optional = true }
|
||||
serde_bytes = { workspace = true, optional = true }
|
||||
serde_crate = { version = "1.0", optional = true, default_features = false, features = ["derive"], package = "serde" }
|
||||
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
|
||||
subtle-encoding = { workspace = true, features = ["bech32-preview"] }
|
||||
thiserror = { workspace = true }
|
||||
zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] }
|
||||
|
||||
|
||||
@@ -200,6 +200,14 @@ impl<'a> From<&'a PrivateKey> for PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PrivateKey {
|
||||
type Err = KeyRecoveryError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
PrivateKey::from_base58_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
|
||||
@@ -227,6 +227,14 @@ impl<'a> From<&'a PrivateKey> for PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PrivateKey {
|
||||
type Err = Ed25519RecoveryError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
PrivateKey::from_base58_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
|
||||
@@ -8,7 +8,7 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitvec = "1.0.0"
|
||||
bitvec = { workspace = true }
|
||||
|
||||
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
|
||||
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
|
||||
@@ -17,13 +17,13 @@ nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common",
|
||||
bs58 = { workspace = true }
|
||||
|
||||
|
||||
lazy_static = "1.4.0"
|
||||
lazy_static = { workspace = true }
|
||||
rand = { version = "0.8.5", default-features = false}
|
||||
rand_chacha = "0.3"
|
||||
rand_core = "0.6.3"
|
||||
rand_core = { workspace = true }
|
||||
sha2 = "0.9"
|
||||
serde = { workspace = true }
|
||||
serde_derive = "1.0"
|
||||
serde_derive = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
|
||||
@@ -38,7 +38,7 @@ workspace = true
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.4"
|
||||
criterion = { workspace = true }
|
||||
|
||||
[[bench]]
|
||||
name = "benchmarks"
|
||||
|
||||
@@ -8,5 +8,5 @@ license.workspace = true
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1", features = ["full"] }
|
||||
quote = "1"
|
||||
syn = { workspace = true, features = ["full"] }
|
||||
quote = { workspace = true }
|
||||
|
||||
@@ -13,6 +13,7 @@ license.workspace = true
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
http.workspace = true
|
||||
url = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use reqwest::{IntoUrl, Response, StatusCode};
|
||||
use reqwest::header::HeaderValue;
|
||||
use reqwest::{RequestBuilder, Response, StatusCode};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
@@ -11,6 +12,8 @@ use thiserror::Error;
|
||||
use tracing::warn;
|
||||
use url::Url;
|
||||
|
||||
pub use reqwest::IntoUrl;
|
||||
|
||||
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
|
||||
pub type PathSegments<'a> = &'a [&'a str];
|
||||
@@ -52,6 +55,88 @@ pub enum HttpClientError<E: Display = String> {
|
||||
RequestTimeout,
|
||||
}
|
||||
|
||||
pub struct ClientBuilder {
|
||||
url: Url,
|
||||
timeout: Option<Duration>,
|
||||
custom_user_agent: bool,
|
||||
reqwest_client_builder: reqwest::ClientBuilder,
|
||||
}
|
||||
|
||||
impl ClientBuilder {
|
||||
pub fn new<U, E>(url: U) -> 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(alt)
|
||||
} else {
|
||||
Ok(ClientBuilder {
|
||||
url: url.into_url()?,
|
||||
timeout: None,
|
||||
custom_user_agent: false,
|
||||
reqwest_client_builder: reqwest::ClientBuilder::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.timeout = Some(timeout);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_reqwest_builder(mut self, reqwest_builder: reqwest::ClientBuilder) -> Self {
|
||||
self.reqwest_client_builder = reqwest_builder;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_user_agent<V>(mut self, value: V) -> Self
|
||||
where
|
||||
V: TryInto<HeaderValue>,
|
||||
V::Error: Into<http::Error>,
|
||||
{
|
||||
self.custom_user_agent = true;
|
||||
self.reqwest_client_builder = self.reqwest_client_builder.user_agent(value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build<E>(self) -> Result<Client, HttpClientError<E>>
|
||||
where
|
||||
E: Display,
|
||||
{
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let reqwest_client = self.reqwest_client_builder.build()?;
|
||||
|
||||
// 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 = {
|
||||
let mut builder = self
|
||||
.reqwest_client_builder
|
||||
.timeout(self.timeout.unwrap_or(DEFAULT_TIMEOUT));
|
||||
if !self.custom_user_agent {
|
||||
builder =
|
||||
builder.user_agent(format!("nym-http-api-client/{}", env!("CARGO_PKG_VERSION")))
|
||||
}
|
||||
builder.build()?
|
||||
};
|
||||
|
||||
Ok(Client {
|
||||
base_url: self.url,
|
||||
reqwest_client,
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
request_timeout: self.timeout.unwrap_or(DEFAULT_TIMEOUT),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple extendable client wrapper for http request with extra url sanitization.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Client {
|
||||
@@ -65,25 +150,9 @@ pub struct Client {
|
||||
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),
|
||||
}
|
||||
Self::new_url::<_, String>(base_url, timeout).expect(
|
||||
"we provided valid url and we were unwrapping previous construction errors anyway",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_url<U, E>(url: U, timeout: Option<Duration>) -> Result<Self, HttpClientError<E>>
|
||||
@@ -91,19 +160,21 @@ impl Client {
|
||||
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))
|
||||
let builder = Self::builder(url)?;
|
||||
match timeout {
|
||||
Some(timeout) => builder.with_timeout(timeout).build(),
|
||||
None => builder.build(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn builder<U, E>(url: U) -> Result<ClientBuilder, HttpClientError<E>>
|
||||
where
|
||||
U: IntoUrl,
|
||||
E: Display,
|
||||
{
|
||||
ClientBuilder::new(url)
|
||||
}
|
||||
|
||||
pub fn change_base_url(&mut self, new_url: Url) {
|
||||
self.base_url = new_url
|
||||
}
|
||||
@@ -112,6 +183,19 @@ impl Client {
|
||||
&self.base_url
|
||||
}
|
||||
|
||||
pub fn create_get_request<K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> RequestBuilder
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = sanitize_url(&self.base_url, path, params);
|
||||
self.reqwest_client.get(url)
|
||||
}
|
||||
|
||||
async fn send_get_request<K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
@@ -142,6 +226,21 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_post_request<B, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
json_body: &B,
|
||||
) -> RequestBuilder
|
||||
where
|
||||
B: Serialize + ?Sized,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = sanitize_url(&self.base_url, path, params);
|
||||
self.reqwest_client.post(url).json(json_body)
|
||||
}
|
||||
|
||||
async fn send_post_request<B, K, V, E>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
@@ -407,7 +506,7 @@ pub fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
|
||||
url
|
||||
}
|
||||
|
||||
async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
|
||||
pub async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
E: DeserializeOwned + Display,
|
||||
|
||||
@@ -12,9 +12,9 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
axum.workspace = true
|
||||
bytes = "1.5.0"
|
||||
mime = "0.3.17"
|
||||
bytes = { workspace = true }
|
||||
mime = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
serde_yaml = "0.9.25"
|
||||
serde_yaml = { workspace = true }
|
||||
utoipa = { workspace = true }
|
||||
|
||||
@@ -9,12 +9,13 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
bytes = "1.5.0"
|
||||
bincode = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
rand = "0.8.5"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
time = { workspace = true }
|
||||
tokio = { workspace = true, features = ["time"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
|
||||
@@ -12,11 +12,13 @@ pub use v6::response;
|
||||
|
||||
pub mod codec;
|
||||
pub mod v6;
|
||||
pub mod v7;
|
||||
|
||||
// version 3: initial version
|
||||
// version 4: IPv6 support
|
||||
// version 5: Add severity level to info response
|
||||
// version 6: Increase the available IPs
|
||||
// version 7: Add signature support (for the future)
|
||||
pub const CURRENT_VERSION: u8 = 6;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
@@ -0,0 +1,395 @@
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use crate::{make_bincode_serializer, IpPair, CURRENT_VERSION};
|
||||
|
||||
fn generate_random() -> u64 {
|
||||
use rand::RngCore;
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
rng.next_u64()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct IpPacketRequest {
|
||||
pub version: u8,
|
||||
pub data: IpPacketRequestData,
|
||||
}
|
||||
|
||||
impl IpPacketRequest {
|
||||
pub fn new_static_connect_request(
|
||||
ips: IpPair,
|
||||
reply_to: Recipient,
|
||||
reply_to_hops: Option<u8>,
|
||||
reply_to_avg_mix_delays: Option<f64>,
|
||||
buffer_timeout: Option<u64>,
|
||||
) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::StaticConnect(SignedStaticConnectRequest {
|
||||
request: StaticConnectRequest {
|
||||
request_id,
|
||||
ips,
|
||||
reply_to,
|
||||
reply_to_hops,
|
||||
reply_to_avg_mix_delays,
|
||||
buffer_timeout,
|
||||
timestamp: OffsetDateTime::now_utc(),
|
||||
},
|
||||
signature: None,
|
||||
}),
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_dynamic_connect_request(
|
||||
reply_to: Recipient,
|
||||
reply_to_hops: Option<u8>,
|
||||
reply_to_avg_mix_delays: Option<f64>,
|
||||
buffer_timeout: Option<u64>,
|
||||
) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::DynamicConnect(SignedDynamicConnectRequest {
|
||||
request: DynamicConnectRequest {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply_to_hops,
|
||||
reply_to_avg_mix_delays,
|
||||
buffer_timeout,
|
||||
timestamp: OffsetDateTime::now_utc(),
|
||||
},
|
||||
signature: None,
|
||||
}),
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_disconnect_request(reply_to: Recipient) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::Disconnect(SignedDisconnectRequest {
|
||||
request: DisconnectRequest {
|
||||
request_id,
|
||||
reply_to,
|
||||
timestamp: OffsetDateTime::now_utc(),
|
||||
},
|
||||
signature: None,
|
||||
}),
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_data_request(ip_packets: bytes::Bytes) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::Data(DataRequest { ip_packets }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_ping(reply_to: Recipient) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::Ping(PingRequest {
|
||||
request_id,
|
||||
reply_to,
|
||||
timestamp: OffsetDateTime::now_utc(),
|
||||
}),
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_health_request(reply_to: Recipient) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketRequestData::Health(HealthRequest {
|
||||
request_id,
|
||||
reply_to,
|
||||
timestamp: OffsetDateTime::now_utc(),
|
||||
}),
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
IpPacketRequestData::StaticConnect(request) => Some(request.request.request_id),
|
||||
IpPacketRequestData::DynamicConnect(request) => Some(request.request.request_id),
|
||||
IpPacketRequestData::Disconnect(request) => Some(request.request.request_id),
|
||||
IpPacketRequestData::Data(_) => None,
|
||||
IpPacketRequestData::Ping(request) => Some(request.request_id),
|
||||
IpPacketRequestData::Health(request) => Some(request.request_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recipient(&self) -> Option<&Recipient> {
|
||||
match &self.data {
|
||||
IpPacketRequestData::StaticConnect(request) => Some(&request.request.reply_to),
|
||||
IpPacketRequestData::DynamicConnect(request) => Some(&request.request.reply_to),
|
||||
IpPacketRequestData::Disconnect(request) => Some(&request.request.reply_to),
|
||||
IpPacketRequestData::Data(_) => None,
|
||||
IpPacketRequestData::Ping(request) => Some(&request.reply_to),
|
||||
IpPacketRequestData::Health(request) => Some(&request.reply_to),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum IpPacketRequestData {
|
||||
StaticConnect(SignedStaticConnectRequest),
|
||||
DynamicConnect(SignedDynamicConnectRequest),
|
||||
Disconnect(SignedDisconnectRequest),
|
||||
Data(DataRequest),
|
||||
Ping(PingRequest),
|
||||
Health(HealthRequest),
|
||||
}
|
||||
|
||||
impl IpPacketRequestData {
|
||||
pub fn add_signature(&mut self, signature: Vec<u8>) -> Option<Vec<u8>> {
|
||||
match self {
|
||||
IpPacketRequestData::StaticConnect(request) => {
|
||||
request.signature = Some(signature);
|
||||
request.signature.clone()
|
||||
}
|
||||
IpPacketRequestData::DynamicConnect(request) => {
|
||||
request.signature = Some(signature);
|
||||
request.signature.clone()
|
||||
}
|
||||
IpPacketRequestData::Disconnect(request) => {
|
||||
request.signature = Some(signature);
|
||||
request.signature.clone()
|
||||
}
|
||||
IpPacketRequestData::Data(_)
|
||||
| IpPacketRequestData::Ping(_)
|
||||
| IpPacketRequestData::Health(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A static connect request is when the client provides the internal IP address it will use on the
|
||||
// ip packet router.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct StaticConnectRequest {
|
||||
pub request_id: u64,
|
||||
|
||||
pub ips: IpPair,
|
||||
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
|
||||
// The number of mix node hops that responses should take, in addition to the entry and exit
|
||||
// node. Zero means only client -> entry -> exit -> client.
|
||||
pub reply_to_hops: Option<u8>,
|
||||
|
||||
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
|
||||
// ip packet router.
|
||||
pub reply_to_avg_mix_delays: Option<f64>,
|
||||
|
||||
// The maximum time in milliseconds the IPR should wait when filling up a mix packet
|
||||
// with ip packets.
|
||||
pub buffer_timeout: Option<u64>,
|
||||
|
||||
// Timestamp of when the request was sent by the client.
|
||||
pub timestamp: OffsetDateTime,
|
||||
}
|
||||
|
||||
impl StaticConnectRequest {
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct SignedStaticConnectRequest {
|
||||
pub request: StaticConnectRequest,
|
||||
pub signature: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
// A dynamic connect request is when the client does not provide the internal IP address it will use
|
||||
// on the ip packet router, and instead requests one to be assigned to it.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DynamicConnectRequest {
|
||||
pub request_id: u64,
|
||||
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
|
||||
// The number of mix node hops that responses should take, in addition to the entry and exit
|
||||
// node. Zero means only client -> entry -> exit -> client.
|
||||
pub reply_to_hops: Option<u8>,
|
||||
|
||||
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
|
||||
// ip packet router.
|
||||
pub reply_to_avg_mix_delays: Option<f64>,
|
||||
|
||||
// The maximum time in milliseconds the IPR should wait when filling up a mix packet
|
||||
// with ip packets.
|
||||
pub buffer_timeout: Option<u64>,
|
||||
|
||||
// Timestamp of when the request was sent by the client.
|
||||
pub timestamp: OffsetDateTime,
|
||||
}
|
||||
|
||||
impl DynamicConnectRequest {
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct SignedDynamicConnectRequest {
|
||||
pub request: DynamicConnectRequest,
|
||||
pub signature: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
// A disconnect request is when the client wants to disconnect from the ip packet router and free
|
||||
// up the allocated IP address.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DisconnectRequest {
|
||||
pub request_id: u64,
|
||||
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
|
||||
// Timestamp of when the request was sent by the client.
|
||||
pub timestamp: OffsetDateTime,
|
||||
}
|
||||
|
||||
impl DisconnectRequest {
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct SignedDisconnectRequest {
|
||||
pub request: DisconnectRequest,
|
||||
pub signature: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
// A data request is when the client wants to send an IP packet to a destination.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DataRequest {
|
||||
pub ip_packets: bytes::Bytes,
|
||||
}
|
||||
|
||||
// A ping request is when the client wants to check if the ip packet router is still alive.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PingRequest {
|
||||
pub request_id: u64,
|
||||
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
|
||||
// Timestamp of when the request was sent by the client.
|
||||
pub timestamp: OffsetDateTime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct HealthRequest {
|
||||
pub request_id: u64,
|
||||
|
||||
// The nym-address the response should be sent back to
|
||||
pub reply_to: Recipient,
|
||||
|
||||
// Timestamp of when the request was sent by the client.
|
||||
pub timestamp: OffsetDateTime,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn check_size_of_request() {
|
||||
let connect = IpPacketRequest {
|
||||
version: 4,
|
||||
data: IpPacketRequestData::StaticConnect(
|
||||
SignedStaticConnectRequest {
|
||||
request: StaticConnectRequest {
|
||||
request_id: 123,
|
||||
ips: IpPair::new(Ipv4Addr::from_str("10.0.0.1").unwrap(), Ipv6Addr::from_str("2001:db8:a160::1").unwrap()),
|
||||
reply_to: Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(),
|
||||
reply_to_hops: None,
|
||||
reply_to_avg_mix_delays: None,
|
||||
buffer_timeout: None,
|
||||
timestamp: OffsetDateTime::now_utc(),
|
||||
},
|
||||
signature: None,
|
||||
}
|
||||
),
|
||||
};
|
||||
assert_eq!(connect.to_bytes().unwrap().len(), 139);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_size_of_data() {
|
||||
let data = IpPacketRequest {
|
||||
version: 4,
|
||||
data: IpPacketRequestData::Data(DataRequest {
|
||||
ip_packets: bytes::Bytes::from(vec![1u8; 32]),
|
||||
}),
|
||||
};
|
||||
assert_eq!(data.to_bytes().unwrap().len(), 35);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_and_deserialize_data_request() {
|
||||
let data = IpPacketRequest {
|
||||
version: 4,
|
||||
data: IpPacketRequestData::Data(DataRequest {
|
||||
ip_packets: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
|
||||
}),
|
||||
};
|
||||
|
||||
let serialized = data.to_bytes().unwrap();
|
||||
let deserialized = IpPacketRequest::from_reconstructed_message(
|
||||
&nym_sphinx::receiver::ReconstructedMessage {
|
||||
message: serialized,
|
||||
sender_tag: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(deserialized.version, 4);
|
||||
assert_eq!(
|
||||
deserialized.data,
|
||||
IpPacketRequestData::Data(DataRequest {
|
||||
ip_packets: bytes::Bytes::from(vec![1, 2, 4, 2, 5]),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, IpPair, CURRENT_VERSION};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct IpPacketResponse {
|
||||
pub version: u8,
|
||||
pub data: IpPacketResponseData,
|
||||
}
|
||||
|
||||
impl IpPacketResponse {
|
||||
pub fn new_static_connect_success(request_id: u64, reply_to: Recipient) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::StaticConnect(StaticConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: StaticConnectResponseReply::Success,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_static_connect_failure(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
reason: StaticConnectFailureReason,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::StaticConnect(StaticConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: StaticConnectResponseReply::Failure(reason),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dynamic_connect_success(request_id: u64, reply_to: Recipient, ips: IpPair) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::DynamicConnect(DynamicConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: DynamicConnectResponseReply::Success(DynamicConnectSuccess { ips }),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dynamic_connect_failure(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
reason: DynamicConnectFailureReason,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::DynamicConnect(DynamicConnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: DynamicConnectResponseReply::Failure(reason),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_disconnect_success(request_id: u64, reply_to: Recipient) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Disconnect(DisconnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: DisconnectResponseReply::Success,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_disconnect_failure(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
reason: DisconnectFailureReason,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Disconnect(DisconnectResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: DisconnectResponseReply::Failure(reason),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_unrequested_disconnect(
|
||||
reply_to: Recipient,
|
||||
reason: UnrequestedDisconnectReason,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::UnrequestedDisconnect(UnrequestedDisconnect {
|
||||
reply_to,
|
||||
reason,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_ip_packet(ip_packet: bytes::Bytes) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Data(DataResponse { ip_packet }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_version_mismatch(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
request_version: u8,
|
||||
our_version: u8,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Info(InfoResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: InfoResponseReply::VersionMismatch {
|
||||
request_version,
|
||||
response_version: our_version,
|
||||
},
|
||||
level: InfoLevel::Error,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_data_info_response(
|
||||
reply_to: Recipient,
|
||||
reply: InfoResponseReply,
|
||||
level: InfoLevel,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Info(InfoResponse {
|
||||
request_id: 0,
|
||||
reply_to,
|
||||
reply,
|
||||
level,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_pong(request_id: u64, reply_to: Recipient) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Pong(PongResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_health_response(
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
build_info: nym_bin_common::build_information::BinaryBuildInformationOwned,
|
||||
routable: Option<bool>,
|
||||
) -> Self {
|
||||
Self {
|
||||
version: CURRENT_VERSION,
|
||||
data: IpPacketResponseData::Health(HealthResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: HealthResponseReply {
|
||||
build_info,
|
||||
routable,
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
IpPacketResponseData::StaticConnect(response) => Some(response.request_id),
|
||||
IpPacketResponseData::DynamicConnect(response) => Some(response.request_id),
|
||||
IpPacketResponseData::Disconnect(response) => Some(response.request_id),
|
||||
IpPacketResponseData::UnrequestedDisconnect(_) => None,
|
||||
IpPacketResponseData::Data(_) => None,
|
||||
IpPacketResponseData::Pong(response) => Some(response.request_id),
|
||||
IpPacketResponseData::Health(response) => Some(response.request_id),
|
||||
IpPacketResponseData::Info(response) => Some(response.request_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recipient(&self) -> Option<&Recipient> {
|
||||
match &self.data {
|
||||
IpPacketResponseData::StaticConnect(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::DynamicConnect(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::Disconnect(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::UnrequestedDisconnect(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::Data(_) => None,
|
||||
IpPacketResponseData::Pong(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::Health(response) => Some(&response.reply_to),
|
||||
IpPacketResponseData::Info(response) => Some(&response.reply_to),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum IpPacketResponseData {
|
||||
// Response for a static connect request
|
||||
StaticConnect(StaticConnectResponse),
|
||||
|
||||
// Response for a dynamic connect request
|
||||
DynamicConnect(DynamicConnectResponse),
|
||||
|
||||
// Response for a disconnect initiqated by the client
|
||||
Disconnect(DisconnectResponse),
|
||||
|
||||
// Message from the server that the client got disconnected without the client initiating it
|
||||
UnrequestedDisconnect(UnrequestedDisconnect),
|
||||
|
||||
// Response to a data request
|
||||
Data(DataResponse),
|
||||
|
||||
// Response to ping request
|
||||
Pong(PongResponse),
|
||||
|
||||
// Response for a health request
|
||||
Health(HealthResponse),
|
||||
|
||||
// Info response. This can be anything from informative messages to errors
|
||||
Info(InfoResponse),
|
||||
}
|
||||
|
||||
impl IpPacketResponseData {
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct StaticConnectResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: StaticConnectResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum StaticConnectResponseReply {
|
||||
Success,
|
||||
Failure(StaticConnectFailureReason),
|
||||
}
|
||||
|
||||
impl StaticConnectResponseReply {
|
||||
pub fn is_success(&self) -> bool {
|
||||
match self {
|
||||
StaticConnectResponseReply::Success => true,
|
||||
StaticConnectResponseReply::Failure(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum StaticConnectFailureReason {
|
||||
#[error("requested ip address is already in use")]
|
||||
RequestedIpAlreadyInUse,
|
||||
#[error("requested nym-address is already in use")]
|
||||
RequestedNymAddressAlreadyInUse,
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DynamicConnectResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: DynamicConnectResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum DynamicConnectResponseReply {
|
||||
Success(DynamicConnectSuccess),
|
||||
Failure(DynamicConnectFailureReason),
|
||||
}
|
||||
|
||||
impl DynamicConnectResponseReply {
|
||||
pub fn is_success(&self) -> bool {
|
||||
match self {
|
||||
DynamicConnectResponseReply::Success(_) => true,
|
||||
DynamicConnectResponseReply::Failure(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DynamicConnectSuccess {
|
||||
pub ips: IpPair,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum DynamicConnectFailureReason {
|
||||
#[error("requested nym-address is already in use")]
|
||||
RequestedNymAddressAlreadyInUse,
|
||||
#[error("no available ip address")]
|
||||
NoAvailableIp,
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DisconnectResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: DisconnectResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum DisconnectResponseReply {
|
||||
Success,
|
||||
Failure(DisconnectFailureReason),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum DisconnectFailureReason {
|
||||
#[error("requested nym-address is not currently connected")]
|
||||
RequestedNymAddressNotConnected,
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct UnrequestedDisconnect {
|
||||
pub reply_to: Recipient,
|
||||
pub reason: UnrequestedDisconnectReason,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum UnrequestedDisconnectReason {
|
||||
#[error("client mixnet traffic timeout")]
|
||||
ClientMixnetTrafficTimeout,
|
||||
#[error("client tun traffic timeout")]
|
||||
ClientTunTrafficTimeout,
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DataResponse {
|
||||
pub ip_packet: bytes::Bytes,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct PongResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct HealthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: HealthResponseReply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct HealthResponseReply {
|
||||
// Return the binary build information of the IPR
|
||||
pub build_info: nym_bin_common::build_information::BinaryBuildInformationOwned,
|
||||
// Return if the IPR has performed a successful routing test.
|
||||
pub routable: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct InfoResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: InfoResponseReply,
|
||||
pub level: InfoLevel,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum InfoResponseReply {
|
||||
#[error("{msg}")]
|
||||
Generic { msg: String },
|
||||
#[error(
|
||||
"version mismatch: response is v{request_version} and response is v{response_version}"
|
||||
)]
|
||||
VersionMismatch {
|
||||
request_version: u8,
|
||||
response_version: u8,
|
||||
},
|
||||
#[error("destination failed exit policy filter check: {dst}")]
|
||||
ExitPolicyFilterCheckFailed { dst: String },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum InfoLevel {
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
}
|
||||
@@ -7,8 +7,8 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bip32 = "0.5.1"
|
||||
bip32 = { workspace = true }
|
||||
k256 = { workspace = true }
|
||||
ledger-transport = "0.10.0"
|
||||
ledger-transport-hid = "0.10.0"
|
||||
ledger-transport = { workspace = true }
|
||||
ledger-transport-hid = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -8,7 +8,7 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.0"
|
||||
bytes = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
humantime-serde = { workspace = true }
|
||||
log = { workspace = true }
|
||||
@@ -38,4 +38,4 @@ nym-task = { path = "../task" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client" }
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
nym-metrics = { path = "../nym-metrics" }
|
||||
nym-node-http-api = { path = "../../nym-node/nym-node-http-api" }
|
||||
nym-node-http-api = { path = "../../nym-node/nym-node-http-api" }
|
||||
|
||||
@@ -10,7 +10,7 @@ repository.workspace = true
|
||||
[dependencies]
|
||||
cfg-if = { workspace = true }
|
||||
dotenvy = { workspace = true }
|
||||
hex-literal = "0.3.3"
|
||||
hex-literal = { workspace = true }
|
||||
log = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
|
||||
@@ -8,7 +8,7 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio-stream = "0.1.11" # this one seems to be a thing until `Stream` trait is stabilised in stdlib
|
||||
tokio-stream = { workspace = true } # this one seems to be a thing until `Stream` trait is stabilised in stdlib
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
workspace = true
|
||||
|
||||
@@ -14,4 +14,4 @@ license.workspace = true
|
||||
prometheus = { workspace = true }
|
||||
log = { workspace = true }
|
||||
dashmap = { workspace = true }
|
||||
lazy_static = "1.4"
|
||||
lazy_static = { workspace = true }
|
||||
|
||||
@@ -14,7 +14,7 @@ digest = "0.9"
|
||||
rand = "0.8"
|
||||
thiserror = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_derive = "1.0"
|
||||
serde_derive = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
sha2 = "0.9"
|
||||
zeroize = { workspace = true, optional = true }
|
||||
@@ -31,8 +31,8 @@ workspace = true
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version="0.4", features=["html_reports"] }
|
||||
doc-comment = "0.3"
|
||||
criterion = { workspace = true, features = ["html_reports"] }
|
||||
doc-comment = { workspace = true }
|
||||
rand_chacha = "0.3"
|
||||
|
||||
[[bench]]
|
||||
|
||||
@@ -10,7 +10,7 @@ repository = { workspace = true }
|
||||
[dependencies]
|
||||
log = { workspace = true }
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
rand_distr = "0.3"
|
||||
rand_distr = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-sphinx-acknowledgements = { path = "acknowledgements" }
|
||||
|
||||
@@ -10,9 +10,9 @@ repository = { workspace = true }
|
||||
[dependencies]
|
||||
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`
|
||||
serde = { workspace = true } # implementing serialization/deserialization for some types, like `Recipient`
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.7"
|
||||
nym-crypto = { path = "../../crypto", features = ["rand"] }
|
||||
nym-crypto = { path = "../../crypto", features = ["rand"] }
|
||||
|
||||
@@ -8,7 +8,7 @@ license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.0"
|
||||
bytes = { workspace = true }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
sphinx-packet = { version = "0.1.0", optional = true }
|
||||
sphinx-packet = { workspace = true, optional = true }
|
||||
nym-outfox = { path = "../../../nym-outfox", optional = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-trait.workspace = true
|
||||
const_format = "0.2.32"
|
||||
const_format = { workspace = true }
|
||||
cosmrs.workspace = true
|
||||
eyre = "0.6.9"
|
||||
eyre = { workspace = true }
|
||||
futures.workspace = true
|
||||
humantime = "2.1.0"
|
||||
humantime = { workspace = true }
|
||||
sha2 = "0.10.8"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"] }
|
||||
@@ -25,8 +25,8 @@ tendermint-rpc = { workspace = true, features = ["websocket-client", "http-clien
|
||||
thiserror.workspace = true
|
||||
time = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tokio-stream = "0.1.14"
|
||||
tokio-util = { version = "0.7.10", features = ["rt"] }
|
||||
tokio-stream = { workspace = true }
|
||||
tokio-util = { workspace = true, features = ["rt"] }
|
||||
tracing.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
|
||||
@@ -8,4 +8,4 @@ license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
pem = "0.8"
|
||||
pem = { workspace = true }
|
||||
|
||||
@@ -11,14 +11,14 @@ anyhow = { workspace = true }
|
||||
dirs = "4.0"
|
||||
futures = { workspace = true }
|
||||
log = { workspace = true }
|
||||
pin-project = "1.0"
|
||||
pin-project = { workspace = true }
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
reqwest = { workspace = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
|
||||
tap = "1.0.1"
|
||||
tap = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] }
|
||||
url = { workspace = true }
|
||||
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
|
||||
@@ -8,7 +8,7 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.0"
|
||||
bytes = { workspace = true }
|
||||
tokio = { workspace = true, features = [ "net", "io-util", "sync", "macros", "time", "rt-multi-thread" ] }
|
||||
tokio-util = { workspace = true, features = [ "io" ] } # reason for getting this guy is to to able to port to tokio 1.X more quickly by being able to use
|
||||
# their `read_buf` [from the util crate] replacement rather than having to rethink/reimplement `AvailableReader` with the new AsyncRead trait definition.
|
||||
@@ -22,4 +22,4 @@ nym-socks5-requests = { path = "../requests" }
|
||||
nym-task = { path = "../../task" }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4.2"
|
||||
tokio-test = { workspace = true }
|
||||
|
||||
@@ -8,7 +8,7 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
bincode = { workspace = true }
|
||||
log = { workspace = true }
|
||||
nym-exit-policy = { path = "../../../common/exit-policy"}
|
||||
nym-service-providers-common = { path = "../../../service-providers/common" }
|
||||
|
||||
@@ -13,8 +13,8 @@ license.workspace = true
|
||||
async-trait = { workspace = true }
|
||||
log = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "chrono"]}
|
||||
thiserror = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = [ "time" ] }
|
||||
tokio = { workspace = true, features = ["time"] }
|
||||
|
||||
@@ -7,14 +7,14 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
aes-gcm = { version = "0.10.1" }
|
||||
argon2 = { version = "0.5.0" }
|
||||
aes-gcm = { workspace = true }
|
||||
argon2 = { workspace = true }
|
||||
generic-array = { workspace = true, features = ["zeroize"] }
|
||||
rand = "0.8.5"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
thiserror = { workspace = true }
|
||||
zeroize = { version = "1.6.0", features = ["zeroize_derive"] }
|
||||
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
|
||||
[target.'cfg(target_env = "wasm32-unknown-unknown")'.dependencies]
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
|
||||
@@ -13,7 +13,7 @@ license.workspace = true
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util", "time", "sync", "macros"] }
|
||||
etherparse = "0.13.0"
|
||||
etherparse = { workspace = true }
|
||||
log.workspace = true
|
||||
nym-wireguard-types = { path = "../wireguard-types", optional = true }
|
||||
|
||||
|
||||
@@ -9,16 +9,16 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.21.4"
|
||||
eyre = "0.6.5"
|
||||
hmac = "0.12.1"
|
||||
eyre = { workspace = true }
|
||||
hmac = { workspace = true }
|
||||
itertools = "0.11"
|
||||
log = { workspace = true }
|
||||
reqwest = { workspace = true }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sha2 = "0.10.8"
|
||||
strum = { version = "0.25", features = ["derive"] }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
ts-rs = { workspace = true }
|
||||
url = { workspace = true }
|
||||
@@ -34,7 +34,7 @@ nym-config = { path = "../../common/config" }
|
||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.3.0"
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -41,7 +41,7 @@ wasm-storage = { path = "../storage" }
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||
# code size when deploying.
|
||||
console_error_panic_hook = { version = "0.1", optional = true }
|
||||
console_error_panic_hook = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
default = ["console_error_panic_hook"]
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::storage::wasm_client_traits::WasmClientStorageError;
|
||||
use crate::topology::WasmTopologyError;
|
||||
use js_sys::Promise;
|
||||
use nym_client_core::client::base_client::storage::gateways_storage::BadGateway;
|
||||
use nym_client_core::error::ClientCoreError;
|
||||
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||
|
||||
@@ -32,17 +32,4 @@ pub use nym_validator_client::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRp
|
||||
pub use nym_validator_client::client::IdentityKey;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
||||
pub use wasm_utils::set_panic_hook;
|
||||
|
||||
@@ -14,7 +14,7 @@ js-sys = { workspace = true }
|
||||
wasm-bindgen = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde-wasm-bindgen = { workspace = true }
|
||||
indexed_db_futures = { version = " 0.3.0"}
|
||||
indexed_db_futures = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-store-cipher = { path = "../../store-cipher", features = ["json"] }
|
||||
|
||||
@@ -12,9 +12,9 @@ futures = { workspace = true }
|
||||
js-sys = { workspace = true }
|
||||
wasm-bindgen = { workspace = true }
|
||||
wasm-bindgen-futures = { workspace = true }
|
||||
getrandom = { workspace = true, features=["js"], optional = true }
|
||||
getrandom = { workspace = true, features = ["js"], optional = true }
|
||||
gloo-utils = { workspace = true }
|
||||
gloo-net = { version = "0.3.1", features = ["websocket"], optional = true }
|
||||
gloo-net = { workspace = true, features = ["websocket"], optional = true }
|
||||
#gloo-net = { path = "../../../../gloo/crates/net", features = ["websocket"], optional = true }
|
||||
|
||||
# we don't want entire tokio-tungstenite, tungstenite itself is just fine - we just want message and error enums
|
||||
|
||||
@@ -21,7 +21,7 @@ macro_rules! wasm_error {
|
||||
|
||||
impl From<$struct> for js_sys::Promise {
|
||||
fn from(value: $struct) -> Self {
|
||||
Promise::reject(&value.into())
|
||||
js_sys::Promise::reject(&value.into())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,6 +41,18 @@ macro_rules! console_error {
|
||||
($($t:tt)*) => ($crate::error(&format_args!($($t)*).to_string()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
|
||||
@@ -22,7 +22,7 @@ nym-crypto = { path = "../crypto", features = ["asymmetric"] }
|
||||
# feature-specific dependencies:
|
||||
|
||||
## verify:
|
||||
hmac = { version = "0.12.1", optional = true }
|
||||
hmac = { workspace = true, optional = true }
|
||||
sha2 = { version = "0.10.8", optional = true }
|
||||
|
||||
## openapi:
|
||||
|
||||
@@ -16,7 +16,7 @@ base64 = "0.21.3"
|
||||
# version mismatch with x25519-dalek/curve25519-dalek that is resolved in the
|
||||
# latest commit. So pick that for now.
|
||||
x25519-dalek = "2.0.0"
|
||||
ip_network = "0.4.1"
|
||||
ip_network = { workspace = true }
|
||||
log.workspace = true
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-task = { path = "../task" }
|
||||
|
||||
+11
-11
@@ -7,28 +7,28 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
dotenvy = { workspace = true }
|
||||
humantime-serde = { workspace = true }
|
||||
isocountry = "0.3.2"
|
||||
isocountry = { workspace = true }
|
||||
itertools = "0.10.3"
|
||||
log = { workspace = true }
|
||||
maxminddb = "0.23.0"
|
||||
okapi = { version = "0.7.0", features = ["impl_json_schema"] }
|
||||
pretty_env_logger = "0.4.0"
|
||||
maxminddb = { workspace = true }
|
||||
okapi = { workspace = true, features = ["impl_json_schema"] }
|
||||
pretty_env_logger = { workspace = true }
|
||||
rand = "0.8.5"
|
||||
rand_pcg = "0.3.1"
|
||||
rand_seeder = "0.2.3"
|
||||
rand_pcg = { workspace = true }
|
||||
rand_seeder = { workspace = true }
|
||||
reqwest = { workspace = true }
|
||||
rocket = { version = "0.5.0", features = ["json"] }
|
||||
rocket_cors = { version = "0.6.0" }
|
||||
rocket_okapi = { version = "0.8.0", features = ["swagger"] }
|
||||
rocket = { workspace = true, features = ["json"] }
|
||||
rocket_cors = { workspace = true }
|
||||
rocket_okapi = { workspace = true, features = ["swagger"] }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = {version = "1.21.2", features = ["full"] }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
|
||||
nym-bin-common = { path = "../common/bin-common"}
|
||||
nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" }
|
||||
|
||||
@@ -9,5 +9,5 @@ nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts
|
||||
nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
|
||||
+5
-5
@@ -21,7 +21,7 @@ async-trait = { workspace = true }
|
||||
bip39 = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
colored = "2.0"
|
||||
colored = { workspace = true }
|
||||
dashmap = { workspace = true }
|
||||
dirs = "4.0"
|
||||
dotenvy = { workspace = true }
|
||||
@@ -29,7 +29,7 @@ futures = { workspace = true }
|
||||
humantime-serde = { workspace = true }
|
||||
ipnetwork = "0.16"
|
||||
log = { workspace = true }
|
||||
once_cell = "1.7.2"
|
||||
once_cell = { workspace = true }
|
||||
rand = "0.7"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
@@ -40,7 +40,7 @@ sqlx = { workspace = true, features = [
|
||||
"migrate",
|
||||
"time"
|
||||
] }
|
||||
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
|
||||
subtle-encoding = { workspace = true, features = ["bech32-preview"] }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = [
|
||||
"rt-multi-thread",
|
||||
@@ -49,7 +49,7 @@ tokio = { workspace = true, features = [
|
||||
"fs",
|
||||
"time",
|
||||
] }
|
||||
tokio-stream = { version = "0.1.11", features = ["fs"] }
|
||||
tokio-stream = { workspace = true, features = ["fs"] }
|
||||
tokio-tungstenite = { workspace = true }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
@@ -81,7 +81,7 @@ nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
|
||||
nym-wireguard = { path = "../common/wireguard", optional = true }
|
||||
nym-wireguard-types = { path = "../common/wireguard-types", default-features = false }
|
||||
|
||||
defguard_wireguard_rs = { git = "https://github.com/neacsu/wireguard-rs.git", rev = "c2cd0c1119f699f4bc43f5e6ffd6fc242caa42ed", optional = true }
|
||||
defguard_wireguard_rs = { workspace = true, optional = true }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -6,11 +6,11 @@ rust-version = "1.56"
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
k256 = { workspace = true, features = ["ecdsa", "sha256"] }
|
||||
eyre = "0.6.5"
|
||||
eyre = { workspace = true }
|
||||
|
||||
cosmrs = { workspace = true }
|
||||
|
||||
@@ -18,4 +18,4 @@ nym-cli-commands = { path = "../../common/commands" }
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client" }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1"
|
||||
anyhow = { workspace = true }
|
||||
|
||||
+3
-3
@@ -21,12 +21,12 @@ axum = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
colored = "2.0"
|
||||
cupid = "0.6.1"
|
||||
colored = { workspace = true }
|
||||
cupid = { workspace = true }
|
||||
dirs = "4.0"
|
||||
futures = { workspace = true }
|
||||
humantime-serde = { workspace = true }
|
||||
lazy_static = "1.4"
|
||||
lazy_static = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rand = "0.7.3"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
+15
-20
@@ -11,7 +11,7 @@ authors = [
|
||||
"Drazen Urch <durch@users.noreply.github.com>",
|
||||
]
|
||||
edition = "2021"
|
||||
rust-version = "1.70.0"
|
||||
rust-version = "1.76.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -19,39 +19,34 @@ rust-version = "1.70.0"
|
||||
async-trait = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
bip39 = { workspace = true }
|
||||
cfg-if = "1.0"
|
||||
cfg-if = { workspace = true }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
console-subscriber = { version = "0.1.1", optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable"
|
||||
console-subscriber = { workspace = true, optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable"
|
||||
dirs = "4.0"
|
||||
futures = { workspace = true }
|
||||
itertools = "0.12.0"
|
||||
humantime-serde = { workspace = true }
|
||||
k256 = { version = "*", features = ["ecdsa-core"] } # needed for the Verifier trait; pull whatever version is used by other dependencies
|
||||
k256 = { workspace = true, features = ["ecdsa-core"] } # needed for the Verifier trait; pull whatever version is used by other dependencies
|
||||
log = { workspace = true }
|
||||
pin-project = "1.0"
|
||||
pin-project = { workspace = true }
|
||||
rand = "0.8.5"
|
||||
rand-07 = { package = "rand", version = "0.7.3" } # required for compatibility
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
rocket = { version = "0.5.0", features = ["json"] }
|
||||
rocket_cors = { version = "0.6.0" }
|
||||
rocket = { workspace = true, features = ["json"] }
|
||||
rocket_cors = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tap = "1.0"
|
||||
tap = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
time = { workspace = true, features = ["serde-human-readable", "parsing"] }
|
||||
tokio = { version = "1.24.1", features = [
|
||||
"rt-multi-thread",
|
||||
"macros",
|
||||
"signal",
|
||||
"time",
|
||||
] }
|
||||
tokio-stream = "0.1.11"
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal", "time"] }
|
||||
tokio-stream = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
ts-rs = { workspace = true, optional = true}
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
getset = "0.1.1"
|
||||
getset = { workspace = true }
|
||||
|
||||
sqlx = { workspace = true, features = [
|
||||
"runtime-tokio-rustls",
|
||||
@@ -60,8 +55,8 @@ sqlx = { workspace = true, features = [
|
||||
"migrate",
|
||||
] }
|
||||
|
||||
okapi = { version = "0.7.0", features = ["impl_json_schema"] }
|
||||
rocket_okapi = { version = "0.8.0", features = ["swagger"] }
|
||||
okapi = { workspace = true, features = ["impl_json_schema"] }
|
||||
rocket_okapi = { workspace = true, features = ["swagger"] }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
@@ -121,7 +116,7 @@ sqlx = { workspace = true, features = [
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.3.0"
|
||||
tempfile = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
rand_chacha = "0.3"
|
||||
|
||||
@@ -10,7 +10,7 @@ license.workspace = true
|
||||
bs58 = { workspace = true }
|
||||
cosmrs = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
getset = "0.1.1"
|
||||
getset = { workspace = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
@@ -19,7 +19,7 @@ time = { workspace = true, features = ["serde", "parsing", "formatting"] }
|
||||
|
||||
|
||||
# for serde on secp256k1 signatures
|
||||
ecdsa = { version = "0.16", features = ["serde"] }
|
||||
ecdsa = { workspace = true, features = ["serde"] }
|
||||
|
||||
nym-credentials-interface = { path = "../../common/credentials-interface" }
|
||||
nym-crypto = { path = "../../common/crypto", features = ["serde", "asymmetric"] }
|
||||
|
||||
@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod coconut;
|
||||
pub mod models;
|
||||
pub mod pagination;
|
||||
|
||||
pub trait Deprecatable {
|
||||
fn deprecate(self) -> Deprecated<Self>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::pagination::PaginatedResponse;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal};
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
@@ -9,7 +10,7 @@ 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::node::models::BinaryBuildInformationOwned;
|
||||
use nym_node_requests::api::v1::node::models::{AuxiliaryDetails, BinaryBuildInformationOwned};
|
||||
use schemars::gen::SchemaGenerator;
|
||||
use schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||
use schemars::JsonSchema;
|
||||
@@ -482,6 +483,7 @@ impl JsonSchema for OffsetDateTimeJsonSchemaWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
// this struct is getting quite bloated...
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
|
||||
pub struct NymNodeDescription {
|
||||
#[serde(default)]
|
||||
@@ -489,6 +491,9 @@ pub struct NymNodeDescription {
|
||||
|
||||
pub host_information: HostInformation,
|
||||
|
||||
#[serde(default)]
|
||||
pub auxiliary_details: AuxiliaryDetails,
|
||||
|
||||
// TODO: do we really care about ALL build info or just the version?
|
||||
pub build_information: BinaryBuildInformationOwned,
|
||||
|
||||
@@ -569,3 +574,28 @@ pub struct SignerInformationResponse {
|
||||
|
||||
pub verification_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, Default)]
|
||||
pub struct TestNode {
|
||||
pub node_id: Option<u32>,
|
||||
pub identity_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
|
||||
pub struct TestRoute {
|
||||
pub gateway: TestNode,
|
||||
pub layer1: TestNode,
|
||||
pub layer2: TestNode,
|
||||
pub layer3: TestNode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
|
||||
pub struct PartialTestResult {
|
||||
pub monitor_run_id: i64,
|
||||
pub timestamp: i64,
|
||||
pub overall_reliability_for_all_routes_in_monitor_run: Option<u8>,
|
||||
pub test_routes: TestRoute,
|
||||
}
|
||||
|
||||
pub type MixnodeTestResultResponse = PaginatedResponse<PartialTestResult>;
|
||||
pub type GatewayTestResultResponse = PaginatedResponse<PartialTestResult>;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||
pub struct Pagination {
|
||||
pub total: usize,
|
||||
pub page: u32,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||
pub struct PaginatedResponse<T> {
|
||||
pub pagination: Pagination,
|
||||
pub data: Vec<T>,
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::coconut::api_routes::helpers::build_credentials_response;
|
||||
use crate::coconut::error::{CoconutError, Result};
|
||||
use crate::coconut::helpers::{accepted_vote_err, blind_sign};
|
||||
use crate::coconut::state::State;
|
||||
use crate::coconut::storage::CoconutStorageExt;
|
||||
use std::ops::Deref;
|
||||
|
||||
use k256::ecdsa::signature::Verifier;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State as RocketState;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use nym_api_requests::coconut::models::{
|
||||
CredentialsRequestBody, EpochCredentialsResponse, FreePassNonceResponse, FreePassRequest,
|
||||
IssuedCredentialResponse, IssuedCredentialsResponse,
|
||||
@@ -23,12 +26,12 @@ use nym_credentials::coconut::bandwidth::{
|
||||
bandwidth_credential_params, CredentialType, IssuanceBandwidthCredential,
|
||||
};
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State as RocketState;
|
||||
use std::ops::Deref;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use crate::coconut::api_routes::helpers::build_credentials_response;
|
||||
use crate::coconut::error::{CoconutError, Result};
|
||||
use crate::coconut::helpers::{accepted_vote_err, blind_sign};
|
||||
use crate::coconut::state::State;
|
||||
use crate::coconut::storage::CoconutStorageExt;
|
||||
|
||||
mod helpers;
|
||||
|
||||
@@ -98,17 +101,30 @@ pub async fn post_free_pass(
|
||||
|
||||
validate_freepass_public_attributes(&freepass_request_body)?;
|
||||
|
||||
// grab the admin of the bandwidth contract
|
||||
let Some(authorised_admin) = state.get_bandwidth_contract_admin().await? else {
|
||||
error!("our bandwidth contract does not have an admin set! We won't be able to migrate the contract! We should redeploy it ASAP");
|
||||
return Err(CoconutError::MissingBandwidthContractAdmin);
|
||||
// check for explicit admin
|
||||
let explicit_admin = state.get_authorised_freepass_requester().await;
|
||||
|
||||
// otherwise fallback to bandwidth contract admin
|
||||
let bandwidth_contract_admin = state
|
||||
.get_bandwidth_contract_admin()
|
||||
.await
|
||||
.cloned()
|
||||
.inspect_err(|_| error!("our bandwidth contract does not have an admin set! We won't be able to migrate the contract! We should redeploy it ASAP"))
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
// extract account prefix
|
||||
let prefix = match (&explicit_admin, &bandwidth_contract_admin) {
|
||||
(None, None) => {
|
||||
error!("neither explicit admin nor bandwidth contract admin has been set!");
|
||||
return Err(CoconutError::MissingBandwidthContractAddress);
|
||||
}
|
||||
(Some(addr), _) => addr.prefix(),
|
||||
(None, Some(addr)) => addr.prefix(),
|
||||
};
|
||||
|
||||
// derive the address out of the provided pubkey
|
||||
let requester = match freepass_request_body
|
||||
.cosmos_pubkey
|
||||
.account_id(authorised_admin.prefix())
|
||||
{
|
||||
let requester = match freepass_request_body.cosmos_pubkey.account_id(prefix) {
|
||||
Ok(address) => address,
|
||||
Err(err) => {
|
||||
return Err(CoconutError::AdminAccountDerivationFailure {
|
||||
@@ -116,12 +132,16 @@ pub async fn post_free_pass(
|
||||
})
|
||||
}
|
||||
};
|
||||
debug!("derived the following address out of the provided public key: {requester}. Going to check it against the authorised admin ({authorised_admin})");
|
||||
debug!("derived the following address out of the provided public key: {requester}. Going to check it against the authorised admin ({explicit_admin:?}) and fallback to bandwidth contract admin: {bandwidth_contract_admin:?}");
|
||||
|
||||
if &requester != authorised_admin {
|
||||
// check if request matches any address
|
||||
if Some(&requester) != explicit_admin.as_ref()
|
||||
&& Some(&requester) != bandwidth_contract_admin.as_ref()
|
||||
{
|
||||
return Err(CoconutError::UnauthorisedFreePassAccount {
|
||||
requester,
|
||||
authorised_admin: authorised_admin.clone(),
|
||||
explicit_admin,
|
||||
bandwidth_contract_admin,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,20 @@ pub enum CoconutError {
|
||||
#[error("failed to derive the admin account from the provided public key: {formatted_source}")]
|
||||
AdminAccountDerivationFailure { formatted_source: String },
|
||||
|
||||
#[error("the requester of the free pass ({requester}) is not authorised. the only allowed account is {authorised_admin}.")]
|
||||
#[error("failed to query for the authorised freepass requester address: {source}")]
|
||||
FreepassAuthorisedFreepassRequesterQueryFailure {
|
||||
#[from]
|
||||
source: reqwest::Error,
|
||||
},
|
||||
|
||||
#[error("the provided authorised freepass requester address ({address}) is not a valid cosmos address")]
|
||||
MalformedAuthorisedFreepassRequesterAddress { address: String },
|
||||
|
||||
#[error("the requester of the free pass ({requester}) is not authorised. the only allowed account is {explicit_admin:?} or {bandwidth_contract_admin:?}.")]
|
||||
UnauthorisedFreePassAccount {
|
||||
requester: AccountId,
|
||||
authorised_admin: AccountId,
|
||||
explicit_admin: Option<AccountId>,
|
||||
bandwidth_contract_admin: Option<AccountId>,
|
||||
},
|
||||
|
||||
#[error("failed to verify signature on the provided free pass request")]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::coconut::client::Client as LocalClient;
|
||||
use crate::coconut::comm::APICommunicationChannel;
|
||||
use crate::coconut::deposit::validate_deposit_tx;
|
||||
use crate::coconut::error::Result;
|
||||
use crate::coconut::error::{CoconutError, Result};
|
||||
use crate::coconut::keys::KeyPair;
|
||||
use crate::coconut::storage::CoconutStorageExt;
|
||||
use crate::support::storage::NymApiStorage;
|
||||
@@ -17,6 +17,7 @@ use nym_validator_client::nyxd::{AccountId, Hash, TxResponse};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use std::sync::Arc;
|
||||
use time::{Duration, OffsetDateTime};
|
||||
use tokio::sync::{OnceCell, RwLock};
|
||||
|
||||
pub use nym_credentials::coconut::bandwidth::bandwidth_credential_params;
|
||||
@@ -30,6 +31,25 @@ pub struct State {
|
||||
pub(crate) comm_channel: Arc<dyn APICommunicationChannel + Send + Sync>,
|
||||
pub(crate) storage: NymApiStorage,
|
||||
pub(crate) freepass_nonce: Arc<RwLock<[u8; 16]>>,
|
||||
pub(crate) authorised_freepass_requester: Arc<RwLock<AuthorisedFreepassRequester>>,
|
||||
}
|
||||
|
||||
const FREEPASS_REQUESTER_TTL: Duration = Duration::hours(1);
|
||||
const AUTHORISED_FREEPASS_REQUESTER_ENDPOINT: &str =
|
||||
"https://nymtech.net/.wellknown/authorised-freepass-requester.txt";
|
||||
|
||||
pub struct AuthorisedFreepassRequester {
|
||||
address: Option<AccountId>,
|
||||
refreshed_at: OffsetDateTime,
|
||||
}
|
||||
|
||||
impl Default for AuthorisedFreepassRequester {
|
||||
fn default() -> Self {
|
||||
AuthorisedFreepassRequester {
|
||||
address: None,
|
||||
refreshed_at: OffsetDateTime::UNIX_EPOCH,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -60,6 +80,7 @@ impl State {
|
||||
comm_channel,
|
||||
storage,
|
||||
freepass_nonce: Arc::new(RwLock::new(nonce)),
|
||||
authorised_freepass_requester: Arc::new(Default::default()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +104,58 @@ impl State {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn try_get_authorised_freepass_requester(&self) -> Result<AccountId> {
|
||||
let address = reqwest::Client::builder()
|
||||
.user_agent(format!(
|
||||
"nym-api / {} identity: {}",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
self.identity_keypair.public_key().to_base58_string()
|
||||
))
|
||||
.build()?
|
||||
.get(AUTHORISED_FREEPASS_REQUESTER_ENDPOINT)
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
let trimmed = address.trim();
|
||||
|
||||
address.parse().map_err(
|
||||
|_| CoconutError::MalformedAuthorisedFreepassRequesterAddress {
|
||||
address: trimmed.to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_authorised_freepass_requester(&self) -> Option<AccountId> {
|
||||
{
|
||||
let cached = self.authorised_freepass_requester.read().await;
|
||||
|
||||
// the entry hasn't expired
|
||||
if cached.refreshed_at + FREEPASS_REQUESTER_TTL >= OffsetDateTime::now_utc() {
|
||||
if let Some(cached_address) = cached.address.as_ref() {
|
||||
return Some(cached_address.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// refresh cache
|
||||
let mut cache = self.authorised_freepass_requester.write().await;
|
||||
|
||||
// whatever happens, update refresh time
|
||||
cache.refreshed_at = OffsetDateTime::now_utc();
|
||||
|
||||
let refreshed = match self.try_get_authorised_freepass_requester().await {
|
||||
Ok(upstream) => upstream,
|
||||
Err(err) => {
|
||||
warn!("failed to obtain authorised freepass requester address: {err}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
cache.address = Some(refreshed.clone());
|
||||
Some(refreshed)
|
||||
}
|
||||
|
||||
pub async fn validate_request(
|
||||
&self,
|
||||
request: &BlindSignRequestBody,
|
||||
|
||||
@@ -146,6 +146,11 @@ async fn get_gateway_description(
|
||||
source: err,
|
||||
})?;
|
||||
|
||||
// this can be an old node that hasn't yet exposed this
|
||||
let auxiliary_details = client.get_auxiliary_details().await.inspect_err(|err| {
|
||||
debug!("could not obtain auxiliary details of gateway {}: {err} is it running an old version?", gateway.identity_key);
|
||||
}).unwrap_or_default();
|
||||
|
||||
let websockets =
|
||||
client
|
||||
.get_mixnet_websockets()
|
||||
@@ -188,6 +193,7 @@ async fn get_gateway_description(
|
||||
network_requester,
|
||||
ip_packet_router,
|
||||
mixnet_websockets: websockets.into(),
|
||||
auxiliary_details,
|
||||
};
|
||||
|
||||
Ok((gateway.identity_key, description))
|
||||
|
||||
+5
-2
@@ -2,6 +2,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated};
|
||||
use nym_contracts_common::IdentityKey;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::support::caching::Cache;
|
||||
|
||||
@@ -9,11 +12,11 @@ use super::inclusion_probabilities::InclusionProbabilities;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct NodeStatusCacheData {
|
||||
pub(crate) mixnodes_annotated: Cache<Vec<MixNodeBondAnnotated>>,
|
||||
pub(crate) mixnodes_annotated: Cache<HashMap<MixId, MixNodeBondAnnotated>>,
|
||||
pub(crate) rewarded_set_annotated: Cache<Vec<MixNodeBondAnnotated>>,
|
||||
pub(crate) active_set_annotated: Cache<Vec<MixNodeBondAnnotated>>,
|
||||
|
||||
pub(crate) gateways_annotated: Cache<Vec<GatewayBondAnnotated>>,
|
||||
pub(crate) gateways_annotated: Cache<HashMap<IdentityKey, GatewayBondAnnotated>>,
|
||||
|
||||
// Estimated active set inclusion probabilities from Monte Carlo simulation
|
||||
pub(crate) inclusion_probabilities: Cache<InclusionProbabilities>,
|
||||
|
||||
+48
-12
@@ -1,13 +1,14 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::support::caching::Cache;
|
||||
|
||||
use self::data::NodeStatusCacheData;
|
||||
use self::inclusion_probabilities::InclusionProbabilities;
|
||||
use crate::support::caching::Cache;
|
||||
use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated, MixnodeStatus};
|
||||
use nym_contracts_common::{IdentityKey, IdentityKeyRef};
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use rocket::fairing::AdHoc;
|
||||
use std::collections::HashMap;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use thiserror::Error;
|
||||
use tokio::sync::RwLockReadGuard;
|
||||
@@ -55,10 +56,10 @@ impl NodeStatusCache {
|
||||
/// Updates the cache with the latest data.
|
||||
async fn update(
|
||||
&self,
|
||||
mixnodes: Vec<MixNodeBondAnnotated>,
|
||||
mixnodes: HashMap<MixId, MixNodeBondAnnotated>,
|
||||
rewarded_set: Vec<MixNodeBondAnnotated>,
|
||||
active_set: Vec<MixNodeBondAnnotated>,
|
||||
gateways: Vec<GatewayBondAnnotated>,
|
||||
gateways: HashMap<IdentityKey, GatewayBondAnnotated>,
|
||||
inclusion_probabilities: InclusionProbabilities,
|
||||
) {
|
||||
match time::timeout(Duration::from_millis(CACHE_TIMEOUT_MS), self.inner.write()).await {
|
||||
@@ -76,7 +77,7 @@ impl NodeStatusCache {
|
||||
}
|
||||
|
||||
/// Returns a copy of the current cache data.
|
||||
async fn get<T>(
|
||||
async fn get_owned<T>(
|
||||
&self,
|
||||
fn_arg: impl FnOnce(RwLockReadGuard<'_, NodeStatusCacheData>) -> Cache<T>,
|
||||
) -> Option<Cache<T>> {
|
||||
@@ -89,8 +90,24 @@ impl NodeStatusCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnodes_annotated_full(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
self.get(|c| c.mixnodes_annotated.clone_cache()).await
|
||||
async fn get<'a, T: 'a>(
|
||||
&'a self,
|
||||
fn_arg: impl FnOnce(&NodeStatusCacheData) -> &Cache<T>,
|
||||
) -> Option<RwLockReadGuard<'a, Cache<T>>> {
|
||||
match time::timeout(Duration::from_millis(CACHE_TIMEOUT_MS), self.inner.read()).await {
|
||||
Ok(cache) => Some(RwLockReadGuard::map(cache, |item| fn_arg(item))),
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnodes_annotated_full(&self) -> Option<Vec<MixNodeBondAnnotated>> {
|
||||
let mixnodes = self.get(|c| &c.mixnodes_annotated).await?;
|
||||
|
||||
// just clone everything and return the vec to work with the existing code
|
||||
Some(mixnodes.values().cloned().collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnodes_annotated_filtered(&self) -> Option<Vec<MixNodeBondAnnotated>> {
|
||||
@@ -98,16 +115,26 @@ impl NodeStatusCache {
|
||||
Some(full.iter().filter(|m| !m.blacklisted).cloned().collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnode_annotated(&self, mix_id: MixId) -> Option<MixNodeBondAnnotated> {
|
||||
let mixnodes = self.get(|c| &c.mixnodes_annotated).await?;
|
||||
mixnodes.get(&mix_id).cloned()
|
||||
}
|
||||
|
||||
pub(crate) async fn rewarded_set_annotated(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
self.get(|c| c.rewarded_set_annotated.clone_cache()).await
|
||||
self.get_owned(|c| c.rewarded_set_annotated.clone_cache())
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn active_set_annotated(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
self.get(|c| c.active_set_annotated.clone_cache()).await
|
||||
self.get_owned(|c| c.active_set_annotated.clone_cache())
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn gateways_annotated_full(&self) -> Option<Cache<Vec<GatewayBondAnnotated>>> {
|
||||
self.get(|c| c.gateways_annotated.clone_cache()).await
|
||||
pub(crate) async fn gateways_annotated_full(&self) -> Option<Vec<GatewayBondAnnotated>> {
|
||||
let gateways = self.get(|c| &c.gateways_annotated).await?;
|
||||
|
||||
// just clone everything and return the vec to work with the existing code
|
||||
Some(gateways.values().cloned().collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn gateways_annotated_filtered(&self) -> Option<Vec<GatewayBondAnnotated>> {
|
||||
@@ -115,8 +142,17 @@ impl NodeStatusCache {
|
||||
Some(full.iter().filter(|m| !m.blacklisted).cloned().collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn gateway_annotated(
|
||||
&self,
|
||||
gateway_id: IdentityKeyRef<'_>,
|
||||
) -> Option<GatewayBondAnnotated> {
|
||||
let gateways = self.get(|c| &c.gateways_annotated).await?;
|
||||
gateways.get(gateway_id).cloned()
|
||||
}
|
||||
|
||||
pub(crate) async fn inclusion_probabilities(&self) -> Option<Cache<InclusionProbabilities>> {
|
||||
self.get(|c| c.inclusion_probabilities.clone_cache()).await
|
||||
self.get_owned(|c| c.inclusion_probabilities.clone_cache())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn mixnode_details(
|
||||
|
||||
+30
-24
@@ -28,11 +28,11 @@ pub(super) fn to_rewarded_set_node_status(
|
||||
}
|
||||
|
||||
pub(super) fn split_into_active_and_rewarded_set(
|
||||
mixnodes_annotated: &[MixNodeBondAnnotated],
|
||||
mixnodes_annotated: &HashMap<MixId, MixNodeBondAnnotated>,
|
||||
rewarded_set_node_status: &HashMap<u32, RewardedSetNodeStatus>,
|
||||
) -> (Vec<MixNodeBondAnnotated>, Vec<MixNodeBondAnnotated>) {
|
||||
let rewarded_set: Vec<_> = mixnodes_annotated
|
||||
.iter()
|
||||
.values()
|
||||
.filter(|mixnode| rewarded_set_node_status.get(&mixnode.mix_id()).is_some())
|
||||
.cloned()
|
||||
.collect();
|
||||
@@ -88,12 +88,12 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
rewarded_set: &HashMap<MixId, RewardedSetNodeStatus>,
|
||||
mix_to_family: Vec<(IdentityKey, FamilyHead)>,
|
||||
blacklist: &HashSet<MixId>,
|
||||
) -> Vec<MixNodeBondAnnotated> {
|
||||
) -> HashMap<MixId, MixNodeBondAnnotated> {
|
||||
let mix_to_family = mix_to_family
|
||||
.into_iter()
|
||||
.collect::<HashMap<IdentityKey, FamilyHead>>();
|
||||
|
||||
let mut annotated = Vec::new();
|
||||
let mut annotated = HashMap::new();
|
||||
for mixnode in mixnodes {
|
||||
let stake_saturation = mixnode
|
||||
.rewarding_details
|
||||
@@ -138,17 +138,20 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
.get(mixnode.bond_information.identity())
|
||||
.cloned();
|
||||
|
||||
annotated.push(MixNodeBondAnnotated {
|
||||
blacklisted: blacklist.contains(&mixnode.mix_id()),
|
||||
mixnode_details: mixnode,
|
||||
stake_saturation,
|
||||
uncapped_stake_saturation,
|
||||
performance,
|
||||
node_performance,
|
||||
estimated_operator_apy,
|
||||
estimated_delegators_apy,
|
||||
family,
|
||||
});
|
||||
annotated.insert(
|
||||
mixnode.mix_id(),
|
||||
MixNodeBondAnnotated {
|
||||
blacklisted: blacklist.contains(&mixnode.mix_id()),
|
||||
mixnode_details: mixnode,
|
||||
stake_saturation,
|
||||
uncapped_stake_saturation,
|
||||
performance,
|
||||
node_performance,
|
||||
estimated_operator_apy,
|
||||
estimated_delegators_apy,
|
||||
family,
|
||||
},
|
||||
);
|
||||
}
|
||||
annotated
|
||||
}
|
||||
@@ -158,8 +161,8 @@ pub(crate) async fn annotate_gateways_with_details(
|
||||
gateway_bonds: Vec<GatewayBond>,
|
||||
current_interval: Interval,
|
||||
blacklist: &HashSet<IdentityKey>,
|
||||
) -> Vec<GatewayBondAnnotated> {
|
||||
let mut annotated = Vec::new();
|
||||
) -> HashMap<IdentityKey, GatewayBondAnnotated> {
|
||||
let mut annotated = HashMap::new();
|
||||
for gateway_bond in gateway_bonds {
|
||||
let performance = get_gateway_performance_from_storage(
|
||||
storage,
|
||||
@@ -180,13 +183,16 @@ pub(crate) async fn annotate_gateways_with_details(
|
||||
}
|
||||
.unwrap_or_default();
|
||||
|
||||
annotated.push(GatewayBondAnnotated {
|
||||
blacklisted: blacklist.contains(&gateway_bond.gateway.identity_key),
|
||||
gateway_bond,
|
||||
self_described: None,
|
||||
performance,
|
||||
node_performance,
|
||||
});
|
||||
annotated.insert(
|
||||
gateway_bond.identity().to_string(),
|
||||
GatewayBondAnnotated {
|
||||
blacklisted: blacklist.contains(&gateway_bond.gateway.identity_key),
|
||||
gateway_bond,
|
||||
self_described: None,
|
||||
performance,
|
||||
node_performance,
|
||||
},
|
||||
);
|
||||
}
|
||||
annotated
|
||||
}
|
||||
|
||||
@@ -24,14 +24,9 @@ async fn get_gateway_bond_annotated(
|
||||
cache: &NodeStatusCache,
|
||||
identity: &str,
|
||||
) -> Result<GatewayBondAnnotated, ErrorResponse> {
|
||||
let gateways = cache
|
||||
.gateways_annotated_filtered()
|
||||
cache
|
||||
.gateway_annotated(identity)
|
||||
.await
|
||||
.ok_or_else(|| ErrorResponse::new("no data available", Status::ServiceUnavailable))?;
|
||||
|
||||
gateways
|
||||
.into_iter()
|
||||
.find(|gateway| gateway.identity() == identity)
|
||||
.ok_or(ErrorResponse::new(
|
||||
"gateway bond not found",
|
||||
Status::NotFound,
|
||||
@@ -42,17 +37,9 @@ async fn get_mixnode_bond_annotated(
|
||||
cache: &NodeStatusCache,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixNodeBondAnnotated, ErrorResponse> {
|
||||
let mixnodes = cache
|
||||
.mixnodes_annotated_filtered()
|
||||
cache
|
||||
.mixnode_annotated(mix_id)
|
||||
.await
|
||||
.ok_or(ErrorResponse::new(
|
||||
"no data available",
|
||||
Status::ServiceUnavailable,
|
||||
))?;
|
||||
|
||||
mixnodes
|
||||
.into_iter()
|
||||
.find(|mixnode| mixnode.mix_id() == mix_id)
|
||||
.ok_or(ErrorResponse::new(
|
||||
"mixnode bond not found",
|
||||
Status::NotFound,
|
||||
@@ -383,11 +370,7 @@ pub(crate) async fn _get_mixnodes_detailed(cache: &NodeStatusCache) -> Vec<MixNo
|
||||
pub(crate) async fn _get_mixnodes_detailed_unfiltered(
|
||||
cache: &NodeStatusCache,
|
||||
) -> Vec<MixNodeBondAnnotated> {
|
||||
cache
|
||||
.mixnodes_annotated_full()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_inner()
|
||||
cache.mixnodes_annotated_full().await.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) async fn _get_rewarded_set_detailed(
|
||||
@@ -418,9 +401,5 @@ pub(crate) async fn _get_gateways_detailed(cache: &NodeStatusCache) -> Vec<Gatew
|
||||
pub(crate) async fn _get_gateways_detailed_unfiltered(
|
||||
cache: &NodeStatusCache,
|
||||
) -> Vec<GatewayBondAnnotated> {
|
||||
cache
|
||||
.gateways_annotated_full()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_inner()
|
||||
cache.gateways_annotated_full().await.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ pub(crate) fn node_status_routes(
|
||||
routes::get_active_set_detailed,
|
||||
routes::get_gateways_detailed,
|
||||
routes::get_gateways_detailed_unfiltered,
|
||||
routes::unstable::mixnode_test_results,
|
||||
routes::unstable::gateway_test_results,
|
||||
]
|
||||
} else {
|
||||
// in the minimal variant we would not have access to endpoints relying on existence
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_api_requests::models::{
|
||||
AllInclusionProbabilitiesResponse, ComputeRewardEstParam, GatewayBondAnnotated,
|
||||
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
|
||||
GatewayUptimeResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
||||
MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse,
|
||||
MixnodeUptimeHistoryResponse, RewardEstimationResponse, StakeSaturationResponse,
|
||||
UptimeResponse,
|
||||
};
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use rocket_okapi::openapi;
|
||||
|
||||
use super::helpers::_get_gateways_detailed;
|
||||
use super::NodeStatusCache;
|
||||
use crate::node_status_api::helpers::{
|
||||
@@ -15,18 +28,6 @@ use crate::node_status_api::helpers::{
|
||||
use crate::node_status_api::models::ErrorResponse;
|
||||
use crate::storage::NymApiStorage;
|
||||
use crate::NymContractCache;
|
||||
use nym_api_requests::models::{
|
||||
AllInclusionProbabilitiesResponse, ComputeRewardEstParam, GatewayBondAnnotated,
|
||||
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
|
||||
GatewayUptimeResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
||||
MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse,
|
||||
MixnodeUptimeHistoryResponse, RewardEstimationResponse, StakeSaturationResponse,
|
||||
UptimeResponse,
|
||||
};
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use rocket_okapi::openapi;
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/gateway/<identity>/report")]
|
||||
@@ -227,3 +228,278 @@ pub async fn get_gateways_detailed_unfiltered(
|
||||
) -> Json<Vec<GatewayBondAnnotated>> {
|
||||
Json(_get_gateways_detailed_unfiltered(cache).await)
|
||||
}
|
||||
|
||||
pub mod unstable {
|
||||
use crate::node_status_api::models::ErrorResponse;
|
||||
use crate::support::http::helpers::PaginationRequest;
|
||||
use crate::support::storage::NymApiStorage;
|
||||
use nym_api_requests::models::{
|
||||
GatewayTestResultResponse, MixnodeTestResultResponse, PartialTestResult, TestNode,
|
||||
TestRoute,
|
||||
};
|
||||
use nym_api_requests::pagination::Pagination;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use rocket::http::Status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use rocket_okapi::openapi;
|
||||
use std::cmp::min;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub type DbId = i64;
|
||||
|
||||
// a simply in-memory cache of node details
|
||||
#[derive(Debug, Default)]
|
||||
pub struct NodeInfoCache {
|
||||
inner: Arc<RwLock<NodeInfoCacheInner>>,
|
||||
}
|
||||
|
||||
impl NodeInfoCache {
|
||||
async fn get_mix_node_details(&self, db_id: DbId, storage: &NymApiStorage) -> TestNode {
|
||||
{
|
||||
let read_guard = self.inner.read().await;
|
||||
if let Some(cached) = read_guard.mixnodes.get(&db_id) {
|
||||
trace!("cache hit for mixnode {db_id}");
|
||||
return cached.clone();
|
||||
}
|
||||
}
|
||||
trace!("cache miss for mixnode {db_id}");
|
||||
|
||||
let mut write_guard = self.inner.write().await;
|
||||
// double-check the cache in case somebody already updated it while we were waiting for the lock
|
||||
if let Some(cached) = write_guard.mixnodes.get(&db_id) {
|
||||
return cached.clone();
|
||||
}
|
||||
|
||||
let details = match storage.get_mixnode_details_by_db_id(db_id).await {
|
||||
Ok(Some(details)) => details.into(),
|
||||
Ok(None) => {
|
||||
error!("somebody has been messing with the database! details for mixnode with database id {db_id} have been removed!");
|
||||
TestNode::default()
|
||||
}
|
||||
Err(err) => {
|
||||
// don't insert into the cache in case another request is successful
|
||||
error!("failed to retrieve details for mixnode {db_id}: {err}");
|
||||
return TestNode::default();
|
||||
}
|
||||
};
|
||||
|
||||
write_guard.mixnodes.insert(db_id, details.clone());
|
||||
details
|
||||
}
|
||||
|
||||
async fn get_gateway_details(&self, db_id: DbId, storage: &NymApiStorage) -> TestNode {
|
||||
{
|
||||
let read_guard = self.inner.read().await;
|
||||
if let Some(cached) = read_guard.gateways.get(&db_id) {
|
||||
trace!("cache hit for gateway {db_id}");
|
||||
return cached.clone();
|
||||
}
|
||||
}
|
||||
trace!("cache miss for gateway {db_id}");
|
||||
|
||||
let mut write_guard = self.inner.write().await;
|
||||
// double-check the cache in case somebody already updated it while we were waiting for the lock
|
||||
if let Some(cached) = write_guard.gateways.get(&db_id) {
|
||||
return cached.clone();
|
||||
}
|
||||
|
||||
let details = match storage.get_gateway_details_by_db_id(db_id).await {
|
||||
Ok(Some(details)) => details.into(),
|
||||
Ok(None) => {
|
||||
error!("somebody has been messing with the database! details for gateway with database id {db_id} have been removed!");
|
||||
TestNode::default()
|
||||
}
|
||||
Err(err) => {
|
||||
// don't insert into the cache in case another request is successful
|
||||
error!("failed to retrieve details for gateway {db_id}: {err}");
|
||||
return TestNode::default();
|
||||
}
|
||||
};
|
||||
|
||||
write_guard.gateways.insert(db_id, details.clone());
|
||||
details
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct NodeInfoCacheInner {
|
||||
mixnodes: HashMap<DbId, TestNode>,
|
||||
gateways: HashMap<DbId, TestNode>,
|
||||
}
|
||||
|
||||
const MAX_TEST_RESULTS_PAGE_SIZE: u32 = 100;
|
||||
const DEFAULT_TEST_RESULTS_PAGE_SIZE: u32 = 50;
|
||||
|
||||
async fn _mixnode_test_results(
|
||||
mix_id: MixId,
|
||||
page: u32,
|
||||
per_page: u32,
|
||||
info_cache: &State<NodeInfoCache>,
|
||||
storage: &State<NymApiStorage>,
|
||||
) -> anyhow::Result<MixnodeTestResultResponse> {
|
||||
// convert to db offset
|
||||
// we're paging from page 0 like civilised people,
|
||||
// so we have to skip (page * per_page) results
|
||||
let offset = page * per_page;
|
||||
let limit = per_page;
|
||||
|
||||
let raw_results = storage
|
||||
.get_mixnode_detailed_statuses(mix_id, limit, offset)
|
||||
.await?;
|
||||
let total = match raw_results.first() {
|
||||
None => 0,
|
||||
Some(r) => storage.get_mixnode_detailed_statuses_count(r.db_id).await?,
|
||||
};
|
||||
|
||||
let mut partial_results = Vec::new();
|
||||
for result in raw_results {
|
||||
let gateway = info_cache
|
||||
.get_gateway_details(result.gateway_id, storage)
|
||||
.await;
|
||||
let layer1 = info_cache
|
||||
.get_mix_node_details(result.layer1_mix_id, storage)
|
||||
.await;
|
||||
let layer2 = info_cache
|
||||
.get_mix_node_details(result.layer2_mix_id, storage)
|
||||
.await;
|
||||
let layer3 = info_cache
|
||||
.get_mix_node_details(result.layer3_mix_id, storage)
|
||||
.await;
|
||||
|
||||
partial_results.push(PartialTestResult {
|
||||
monitor_run_id: result.monitor_run_id,
|
||||
timestamp: result.timestamp,
|
||||
overall_reliability_for_all_routes_in_monitor_run: result.reliability,
|
||||
test_routes: TestRoute {
|
||||
gateway,
|
||||
layer1,
|
||||
layer2,
|
||||
layer3,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
Ok(MixnodeTestResultResponse {
|
||||
pagination: Pagination {
|
||||
total,
|
||||
page,
|
||||
size: partial_results.len(),
|
||||
},
|
||||
data: partial_results,
|
||||
})
|
||||
}
|
||||
|
||||
#[openapi(tag = "UNSTABLE - DO **NOT** USE")]
|
||||
#[get("/mixnodes/unstable/<mix_id>/test-results?<pagination..>")]
|
||||
pub async fn mixnode_test_results(
|
||||
mix_id: MixId,
|
||||
pagination: PaginationRequest,
|
||||
info_cache: &State<NodeInfoCache>,
|
||||
storage: &State<NymApiStorage>,
|
||||
) -> Result<Json<MixnodeTestResultResponse>, ErrorResponse> {
|
||||
let page = pagination.page.unwrap_or_default();
|
||||
let per_page = min(
|
||||
pagination
|
||||
.per_page
|
||||
.unwrap_or(DEFAULT_TEST_RESULTS_PAGE_SIZE),
|
||||
MAX_TEST_RESULTS_PAGE_SIZE,
|
||||
);
|
||||
|
||||
match _mixnode_test_results(mix_id, page, per_page, info_cache, storage).await {
|
||||
Ok(res) => Ok(Json(res)),
|
||||
Err(err) => Err(ErrorResponse::new(
|
||||
format!("failed to retrieve mixnode test results for node {mix_id}: {err}"),
|
||||
Status::InternalServerError,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn _gateway_test_results(
|
||||
gateway_identity: &str,
|
||||
page: u32,
|
||||
per_page: u32,
|
||||
info_cache: &State<NodeInfoCache>,
|
||||
storage: &State<NymApiStorage>,
|
||||
) -> anyhow::Result<GatewayTestResultResponse> {
|
||||
// convert to db offset
|
||||
// we're paging from page 0 like civilised people,
|
||||
// so we have to skip (page * per_page) results
|
||||
let offset = page * per_page;
|
||||
let limit = per_page;
|
||||
|
||||
let raw_results = storage
|
||||
.get_gateway_detailed_statuses(gateway_identity, limit, offset)
|
||||
.await?;
|
||||
let total = match raw_results.first() {
|
||||
None => 0,
|
||||
Some(r) => storage.get_gateway_detailed_statuses_count(r.db_id).await?,
|
||||
};
|
||||
|
||||
let mut partial_results = Vec::new();
|
||||
for result in raw_results {
|
||||
let gateway = info_cache
|
||||
.get_gateway_details(result.gateway_id, storage)
|
||||
.await;
|
||||
let layer1 = info_cache
|
||||
.get_mix_node_details(result.layer1_mix_id, storage)
|
||||
.await;
|
||||
let layer2 = info_cache
|
||||
.get_mix_node_details(result.layer2_mix_id, storage)
|
||||
.await;
|
||||
let layer3 = info_cache
|
||||
.get_mix_node_details(result.layer3_mix_id, storage)
|
||||
.await;
|
||||
|
||||
partial_results.push(PartialTestResult {
|
||||
monitor_run_id: result.monitor_run_id,
|
||||
timestamp: result.timestamp,
|
||||
overall_reliability_for_all_routes_in_monitor_run: result.reliability,
|
||||
test_routes: TestRoute {
|
||||
gateway,
|
||||
layer1,
|
||||
layer2,
|
||||
layer3,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
Ok(GatewayTestResultResponse {
|
||||
pagination: Pagination {
|
||||
total,
|
||||
page,
|
||||
size: partial_results.len(),
|
||||
},
|
||||
data: partial_results,
|
||||
})
|
||||
}
|
||||
|
||||
#[openapi(tag = "UNSTABLE - DO **NOT** USE")]
|
||||
#[get("/gateways/unstable/<gateway_identity>/test-results?<pagination..>")]
|
||||
pub async fn gateway_test_results(
|
||||
gateway_identity: &str,
|
||||
pagination: PaginationRequest,
|
||||
info_cache: &State<NodeInfoCache>,
|
||||
storage: &State<NymApiStorage>,
|
||||
) -> Result<Json<GatewayTestResultResponse>, ErrorResponse> {
|
||||
let page = pagination.page.unwrap_or_default();
|
||||
let per_page = min(
|
||||
pagination
|
||||
.per_page
|
||||
.unwrap_or(DEFAULT_TEST_RESULTS_PAGE_SIZE),
|
||||
MAX_TEST_RESULTS_PAGE_SIZE,
|
||||
);
|
||||
|
||||
match _gateway_test_results(gateway_identity, page, per_page, info_cache, storage).await {
|
||||
Ok(res) => Ok(Json(res)),
|
||||
Err(err) => Err(ErrorResponse::new(
|
||||
format!(
|
||||
"failed to retrieve mixnode test results for gateway {gateway_identity}: {err}"
|
||||
),
|
||||
Status::InternalServerError,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,18 @@ impl<T> Cache<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// I know, it's dead code for now, but I feel it could be useful code in the future
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn map<F, U>(this: Self, f: F) -> Cache<U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
Cache {
|
||||
value: f(this.value),
|
||||
as_at: this.as_at,
|
||||
}
|
||||
}
|
||||
|
||||
// ugh. I hate to expose it, but it'd have broken pre-existing code
|
||||
pub(crate) fn clone_cache(&self) -> Self
|
||||
where
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, FromForm, Debug, JsonSchema)]
|
||||
pub struct PaginationRequest {
|
||||
pub page: Option<u32>,
|
||||
pub per_page: Option<u32>,
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use crate::coconut::{self, comm::QueryCommunicationChannel};
|
||||
use crate::network::models::NetworkDetails;
|
||||
use crate::network::network_routes;
|
||||
use crate::node_describe_cache::DescribedNodes;
|
||||
use crate::node_status_api::routes::unstable;
|
||||
use crate::node_status_api::{self, NodeStatusCache};
|
||||
use crate::nym_contract_cache::cache::NymContractCache;
|
||||
use crate::status::{api_status_routes, ApiStatusState, SignerState};
|
||||
@@ -23,6 +24,7 @@ use rocket_cors::{AllowedHeaders, AllowedOrigins, Cors};
|
||||
use rocket_okapi::mount_endpoints_and_merged_docs;
|
||||
use rocket_okapi::swagger_ui::make_swagger_ui;
|
||||
|
||||
pub(crate) mod helpers;
|
||||
pub(crate) mod openapi;
|
||||
|
||||
pub(crate) async fn setup_rocket(
|
||||
@@ -57,7 +59,8 @@ pub(crate) async fn setup_rocket(
|
||||
.attach(setup_cors()?)
|
||||
.attach(NymContractCache::stage())
|
||||
.attach(NodeStatusCache::stage())
|
||||
.attach(CirculatingSupplyCache::stage(mix_denom.clone()));
|
||||
.attach(CirculatingSupplyCache::stage(mix_denom.clone()))
|
||||
.manage(unstable::NodeInfoCache::default());
|
||||
|
||||
// This is not a very nice approach. A lazy value would be more suitable, but that's still
|
||||
// a nightly feature: https://github.com/rust-lang/rust/issues/74465
|
||||
|
||||
@@ -4,7 +4,8 @@ use crate::network_monitor::monitor::summary_producer::{GatewayResult, MixnodeRe
|
||||
use crate::node_status_api::models::{HistoricalUptime, Uptime};
|
||||
use crate::node_status_api::utils::{ActiveGatewayStatuses, ActiveMixnodeStatuses};
|
||||
use crate::support::storage::models::{
|
||||
ActiveGateway, ActiveMixnode, NodeStatus, RewardingReport, TestingRoute,
|
||||
ActiveGateway, ActiveMixnode, GatewayDetails, MixnodeDetails, NodeStatus, RewardingReport,
|
||||
TestedGatewayStatus, TestedMixnodeStatus, TestingRoute,
|
||||
};
|
||||
use nym_mixnet_contract_common::{EpochId, IdentityKey, MixId};
|
||||
|
||||
@@ -971,4 +972,133 @@ impl StorageManager {
|
||||
|
||||
Ok(active_day_statuses)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_mixnode_details_by_db_id(
|
||||
&self,
|
||||
id: i64,
|
||||
) -> Result<Option<MixnodeDetails>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
MixnodeDetails,
|
||||
"SELECT * FROM mixnode_details WHERE id = ?",
|
||||
id
|
||||
)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_gateway_details_by_db_id(
|
||||
&self,
|
||||
id: i64,
|
||||
) -> Result<Option<GatewayDetails>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
GatewayDetails,
|
||||
"SELECT * FROM gateway_details WHERE id = ?",
|
||||
id
|
||||
)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_mixnode_statuses_count(&self, db_id: i64) -> Result<i32, sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
SELECT COUNT(*) as count
|
||||
FROM mixnode_status
|
||||
JOIN monitor_run ON mixnode_status.timestamp = monitor_run.timestamp
|
||||
JOIN testing_route ON monitor_run.id = testing_route.monitor_run_id
|
||||
WHERE mixnode_details_id = ?
|
||||
"#,
|
||||
db_id
|
||||
)
|
||||
.fetch_one(&self.connection_pool)
|
||||
.await
|
||||
.map(|record| record.count)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_mixnode_statuses(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
limit: u32,
|
||||
offset: u32,
|
||||
) -> Result<Vec<TestedMixnodeStatus>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
TestedMixnodeStatus,
|
||||
r#"
|
||||
SELECT
|
||||
mixnode_details.id as "db_id",
|
||||
mix_id as "mix_id!",
|
||||
identity_key,
|
||||
reliability as "reliability: u8",
|
||||
monitor_run.timestamp as "timestamp!",
|
||||
gateway_id as "gateway_id!",
|
||||
layer1_mix_id as "layer1_mix_id!",
|
||||
layer2_mix_id as "layer2_mix_id!",
|
||||
layer3_mix_id as "layer3_mix_id!",
|
||||
monitor_run_id as "monitor_run_id!"
|
||||
FROM mixnode_status
|
||||
JOIN mixnode_details ON mixnode_status.mixnode_details_id = mixnode_details.id
|
||||
JOIN monitor_run ON mixnode_status.timestamp = monitor_run.timestamp
|
||||
JOIN testing_route ON monitor_run.id = testing_route.monitor_run_id
|
||||
WHERE mix_id = ?
|
||||
ORDER BY mixnode_status.timestamp DESC
|
||||
LIMIT ? OFFSET ?
|
||||
"#,
|
||||
mix_id,
|
||||
limit,
|
||||
offset
|
||||
)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_gateway_statuses_count(&self, db_id: i64) -> Result<i32, sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
SELECT COUNT(*) as count
|
||||
FROM gateway_status
|
||||
JOIN monitor_run ON gateway_status.timestamp = monitor_run.timestamp
|
||||
JOIN testing_route ON monitor_run.id = testing_route.monitor_run_id
|
||||
WHERE gateway_details_id = ?
|
||||
"#,
|
||||
db_id
|
||||
)
|
||||
.fetch_one(&self.connection_pool)
|
||||
.await
|
||||
.map(|record| record.count)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_gateway_statuses(
|
||||
&self,
|
||||
gateway_identity: &str,
|
||||
limit: u32,
|
||||
offset: u32,
|
||||
) -> Result<Vec<TestedGatewayStatus>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
TestedGatewayStatus,
|
||||
r#"
|
||||
SELECT
|
||||
gateway_details.id as "db_id",
|
||||
identity as "identity_key",
|
||||
reliability as "reliability: u8",
|
||||
monitor_run.timestamp as "timestamp!",
|
||||
gateway_id as "gateway_id!",
|
||||
layer1_mix_id as "layer1_mix_id!",
|
||||
layer2_mix_id as "layer2_mix_id!",
|
||||
layer3_mix_id as "layer3_mix_id!",
|
||||
monitor_run_id as "monitor_run_id!"
|
||||
FROM gateway_status
|
||||
JOIN gateway_details ON gateway_status.gateway_details_id = gateway_details.id
|
||||
JOIN monitor_run ON gateway_status.timestamp = monitor_run.timestamp
|
||||
JOIN testing_route ON monitor_run.id = testing_route.monitor_run_id
|
||||
WHERE identity = ?
|
||||
ORDER BY gateway_status.timestamp DESC
|
||||
LIMIT ? OFFSET ?
|
||||
"#,
|
||||
gateway_identity,
|
||||
limit,
|
||||
offset
|
||||
)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ use crate::node_status_api::models::{
|
||||
use crate::node_status_api::{ONE_DAY, ONE_HOUR};
|
||||
use crate::storage::manager::StorageManager;
|
||||
use crate::storage::models::{NodeStatus, TestingRoute};
|
||||
use crate::support::storage::models::{
|
||||
GatewayDetails, MixnodeDetails, TestedGatewayStatus, TestedMixnodeStatus,
|
||||
};
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use rocket::fairing::AdHoc;
|
||||
use sqlx::ConnectOptions;
|
||||
@@ -716,4 +719,66 @@ impl NymApiStorage {
|
||||
.await
|
||||
.map_err(|err| err.into())
|
||||
}
|
||||
|
||||
pub(crate) async fn get_mixnode_details_by_db_id(
|
||||
&self,
|
||||
id: i64,
|
||||
) -> Result<Option<MixnodeDetails>, NymApiStorageError> {
|
||||
Ok(self.manager.get_mixnode_details_by_db_id(id).await?)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_gateway_details_by_db_id(
|
||||
&self,
|
||||
id: i64,
|
||||
) -> Result<Option<GatewayDetails>, NymApiStorageError> {
|
||||
Ok(self.manager.get_gateway_details_by_db_id(id).await?)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_mixnode_detailed_statuses_count(
|
||||
&self,
|
||||
db_id: i64,
|
||||
) -> Result<usize, NymApiStorageError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.get_mixnode_statuses_count(db_id)
|
||||
.await?
|
||||
.try_into()
|
||||
.unwrap_or(usize::MAX))
|
||||
}
|
||||
|
||||
pub(crate) async fn get_mixnode_detailed_statuses(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
limit: u32,
|
||||
offset: u32,
|
||||
) -> Result<Vec<TestedMixnodeStatus>, NymApiStorageError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.get_mixnode_statuses(mix_id, limit, offset)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_gateway_detailed_statuses_count(
|
||||
&self,
|
||||
db_id: i64,
|
||||
) -> Result<usize, NymApiStorageError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.get_gateway_statuses_count(db_id)
|
||||
.await?
|
||||
.try_into()
|
||||
.unwrap_or(usize::MAX))
|
||||
}
|
||||
|
||||
pub(crate) async fn get_gateway_detailed_statuses(
|
||||
&self,
|
||||
gateway_identity: &str,
|
||||
limit: u32,
|
||||
offset: u32,
|
||||
) -> Result<Vec<TestedGatewayStatus>, NymApiStorageError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.get_gateway_statuses(gateway_identity, limit, offset)
|
||||
.await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_api_requests::models::TestNode;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
|
||||
// Internally used struct to catch results from the database to calculate uptimes for given mixnode/gateway
|
||||
@@ -49,3 +50,61 @@ pub(crate) struct RewardingReport {
|
||||
|
||||
pub(crate) eligible_mixnodes: u32,
|
||||
}
|
||||
|
||||
pub struct MixnodeDetails {
|
||||
pub id: i64,
|
||||
pub mix_id: i64,
|
||||
pub owner: String,
|
||||
pub identity_key: String,
|
||||
}
|
||||
|
||||
impl From<MixnodeDetails> for TestNode {
|
||||
fn from(value: MixnodeDetails) -> Self {
|
||||
TestNode {
|
||||
node_id: Some(value.mix_id.try_into().unwrap_or(u32::MAX)),
|
||||
identity_key: Some(value.identity_key),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GatewayDetails {
|
||||
pub id: i64,
|
||||
pub owner: String,
|
||||
pub identity: String,
|
||||
}
|
||||
|
||||
impl From<GatewayDetails> for TestNode {
|
||||
fn from(value: GatewayDetails) -> Self {
|
||||
TestNode {
|
||||
node_id: None,
|
||||
identity_key: Some(value.identity),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestedMixnodeStatus {
|
||||
pub db_id: i64,
|
||||
pub mix_id: i64,
|
||||
pub identity_key: String,
|
||||
pub reliability: Option<u8>,
|
||||
pub timestamp: i64,
|
||||
|
||||
pub gateway_id: i64,
|
||||
pub layer1_mix_id: i64,
|
||||
pub layer2_mix_id: i64,
|
||||
pub layer3_mix_id: i64,
|
||||
pub monitor_run_id: i64,
|
||||
}
|
||||
|
||||
pub struct TestedGatewayStatus {
|
||||
pub db_id: i64,
|
||||
pub identity_key: String,
|
||||
pub reliability: Option<u8>,
|
||||
pub timestamp: i64,
|
||||
|
||||
pub gateway_id: i64,
|
||||
pub layer1_mix_id: i64,
|
||||
pub layer2_mix_id: i64,
|
||||
pub layer3_mix_id: i64,
|
||||
pub monitor_run_id: i64,
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ wasm-bindgen = { workspace = true }
|
||||
wasm-bindgen-futures = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
console_error_panic_hook = { version = "0.1", optional = true }
|
||||
console_error_panic_hook = { workspace = true, optional = true }
|
||||
|
||||
wasm-utils = { path = "../../common/wasm/utils" }
|
||||
wasm-storage = { path = "../../common/wasm/storage" }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use js_sys::Promise;
|
||||
use thiserror::Error;
|
||||
use wasm_storage::error::StorageError;
|
||||
use wasm_utils::wasm_error;
|
||||
|
||||
@@ -14,17 +14,4 @@ pub use error::ExtensionStorageError;
|
||||
pub use storage::ExtensionStorage;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[wasm_bindgen]
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
||||
pub use wasm_utils::set_panic_hook;
|
||||
|
||||
+4
-4
@@ -17,8 +17,8 @@ license = "GPL-3.0"
|
||||
anyhow.workspace = true
|
||||
bip39 = { workspace = true, features = ["zeroize"] }
|
||||
bs58.workspace = true
|
||||
celes = "2.4.0" # country codes
|
||||
colored = "2"
|
||||
celes = { workspace = true } # country codes
|
||||
colored = { workspace = true }
|
||||
clap = { workspace = true, features = ["cargo", "env"] }
|
||||
humantime-serde = { workspace = true }
|
||||
ipnetwork = "0.16.0"
|
||||
@@ -36,7 +36,7 @@ zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
semver = "1.0.22"
|
||||
|
||||
# system info:
|
||||
cupid = "0.6.1"
|
||||
cupid = { workspace = true }
|
||||
sysinfo = "0.30.7"
|
||||
|
||||
nym-bin-common = { path = "../common/bin-common", features = ["basic_tracing", "output_format"] }
|
||||
@@ -59,4 +59,4 @@ nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
|
||||
|
||||
[build-dependencies]
|
||||
# temporary bonding information v1 (to grab and parse nym-mixnode and nym-gateway package versions)
|
||||
cargo_metadata = "0.18.1"
|
||||
cargo_metadata = { workspace = true }
|
||||
|
||||
@@ -16,12 +16,12 @@ headers.workspace = true
|
||||
thiserror.workspace = true
|
||||
time = { workspace = true, features = ["serde"] }
|
||||
tokio = { workspace = true, features = ["macros"] }
|
||||
tower-http = { version = "0.5.2", features = ["fs"] }
|
||||
tower-http = { workspace = true, features = ["fs"] }
|
||||
tracing.workspace = true
|
||||
utoipa = { workspace = true, features = ["axum_extras", "time"] }
|
||||
utoipa-swagger-ui = { workspace = true, features = ["axum"] }
|
||||
|
||||
colored = "2"
|
||||
colored = { workspace = true }
|
||||
ipnetwork = "0.16"
|
||||
rand = "0.7.3"
|
||||
|
||||
@@ -42,8 +42,8 @@ hyper.workspace = true
|
||||
dashmap.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
hmac = "0.12.1"
|
||||
tower = { version = "0.4.13" }
|
||||
hmac = { workspace = true }
|
||||
tower = { workspace = true }
|
||||
x25519-dalek = { version = "2.0.0" }
|
||||
|
||||
nym-crypto = { path = "../../common/crypto", features = ["rand"] }
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::api::{FormattedResponse, OutputParams};
|
||||
use crate::router::types::RequestError;
|
||||
use axum::extract::Query;
|
||||
use nym_node_requests::api::v1::node::models::AuxiliaryDetails;
|
||||
|
||||
/// Returns auxiliary details of this node.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/auxiliary-details",
|
||||
context_path = "/api/v1",
|
||||
tag = "Node",
|
||||
responses(
|
||||
(status = 200, content(
|
||||
("application/json" = AuxiliaryDetails),
|
||||
("application/yaml" = AuxiliaryDetails)
|
||||
)),
|
||||
),
|
||||
params(OutputParams)
|
||||
)]
|
||||
pub(crate) async fn auxiliary(
|
||||
description: AuxiliaryDetails,
|
||||
Query(output): Query<OutputParams>,
|
||||
) -> Result<AuxiliaryDetailsResponse, RequestError> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
Ok(output.to_response(description))
|
||||
}
|
||||
|
||||
pub type AuxiliaryDetailsResponse = FormattedResponse<AuxiliaryDetails>;
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::api::v1::node::auxiliary::auxiliary;
|
||||
use crate::api::v1::node::build_information::build_information;
|
||||
use crate::api::v1::node::description::description;
|
||||
use crate::api::v1::node::hardware::host_system;
|
||||
@@ -11,6 +12,7 @@ use axum::Router;
|
||||
use nym_node_requests::api::v1::node::models;
|
||||
use nym_node_requests::routes::api::v1;
|
||||
|
||||
pub mod auxiliary;
|
||||
pub mod build_information;
|
||||
pub mod description;
|
||||
pub mod hardware;
|
||||
@@ -24,6 +26,7 @@ pub struct Config {
|
||||
pub system_info: Option<models::HostSystem>,
|
||||
pub roles: models::NodeRoles,
|
||||
pub description: models::NodeDescription,
|
||||
pub auxiliary_details: models::AuxiliaryDetails,
|
||||
}
|
||||
|
||||
pub(super) fn routes<S: Send + Sync + 'static + Clone>(config: Config) -> Router<S> {
|
||||
@@ -63,4 +66,11 @@ pub(super) fn routes<S: Send + Sync + 'static + Clone>(config: Config) -> Router
|
||||
move |query| description(node_description, query)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
v1::AUXILIARY,
|
||||
get({
|
||||
let auxiliary_details = config.auxiliary_details;
|
||||
move |query| auxiliary(auxiliary_details, query)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::router::api;
|
||||
use crate::router::types::{ErrorResponse, RequestError};
|
||||
use axum::Router;
|
||||
use nym_node_requests::api as api_requests;
|
||||
use nym_node_requests::routes::api::v1;
|
||||
use nym_node_requests::routes::api::{v1, v1_absolute};
|
||||
use utoipa::openapi::security::{Http, HttpAuthScheme};
|
||||
use utoipa::{openapi::security::SecurityScheme, Modify, OpenApi};
|
||||
use utoipa_swagger_ui::SwaggerUi;
|
||||
@@ -19,6 +19,7 @@ use utoipa_swagger_ui::SwaggerUi;
|
||||
api::v1::node::roles::roles,
|
||||
api::v1::node::hardware::host_system,
|
||||
api::v1::node::description::description,
|
||||
api::v1::node::auxiliary::auxiliary,
|
||||
api::v1::metrics::mixing::mixing_stats,
|
||||
api::v1::metrics::verloc::verloc_stats,
|
||||
api::v1::metrics::prometheus::prometheus_metrics,
|
||||
@@ -52,6 +53,7 @@ use utoipa_swagger_ui::SwaggerUi;
|
||||
api_requests::v1::node::models::Cpu,
|
||||
api_requests::v1::node::models::CryptoHardware,
|
||||
api_requests::v1::node::models::NodeDescription,
|
||||
api_requests::v1::node::models::AuxiliaryDetails,
|
||||
api_requests::v1::metrics::models::MixingStats,
|
||||
api_requests::v1::metrics::models::VerlocStats,
|
||||
api_requests::v1::metrics::models::VerlocResult,
|
||||
@@ -97,7 +99,8 @@ impl Modify for SecurityAddon {
|
||||
|
||||
pub(crate) fn route<S: Send + Sync + 'static + Clone>() -> Router<S> {
|
||||
// provide absolute path to the openapi.json
|
||||
let config = utoipa_swagger_ui::Config::from("/api/v1/api-docs/openapi.json");
|
||||
let config =
|
||||
utoipa_swagger_ui::Config::from(format!("{}/api-docs/openapi.json", v1_absolute()));
|
||||
SwaggerUi::new(v1::SWAGGER)
|
||||
.url("/api-docs/openapi.json", ApiDoc::openapi())
|
||||
.config(config)
|
||||
|
||||
@@ -15,7 +15,7 @@ use nym_node_requests::api::v1::mixnode::models::Mixnode;
|
||||
use nym_node_requests::api::v1::network_requester::exit_policy::models::UsedExitPolicy;
|
||||
use nym_node_requests::api::v1::network_requester::models::NetworkRequester;
|
||||
use nym_node_requests::api::v1::node::models;
|
||||
use nym_node_requests::api::v1::node::models::{HostSystem, NodeDescription};
|
||||
use nym_node_requests::api::v1::node::models::{AuxiliaryDetails, HostSystem, NodeDescription};
|
||||
use nym_node_requests::api::SignedHostInformation;
|
||||
use nym_node_requests::routes;
|
||||
use std::net::SocketAddr;
|
||||
@@ -47,6 +47,7 @@ impl Config {
|
||||
system_info: None,
|
||||
roles: Default::default(),
|
||||
description: Default::default(),
|
||||
auxiliary_details: Default::default(),
|
||||
},
|
||||
metrics: Default::default(),
|
||||
gateway: Default::default(),
|
||||
@@ -88,6 +89,12 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_auxiliary_details(mut self, auxiliary_details: AuxiliaryDetails) -> Self {
|
||||
self.api.v1_config.node.auxiliary_details = auxiliary_details;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_gateway(mut self, gateway: Gateway) -> Self {
|
||||
self.api.v1_config.node.roles.gateway_enabled = true;
|
||||
|
||||
@@ -12,7 +12,8 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
base64 = { workspace = true }
|
||||
humantime = "2.1.0"
|
||||
celes = { workspace = true } # country codes
|
||||
humantime = { workspace = true }
|
||||
humantime-serde = { workspace = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user