Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f1321e1b0 | |||
| 76953df4bb | |||
| ce2449f86a | |||
| 598014bf30 | |||
| 73fe7618db | |||
| 7a416f8cf5 | |||
| 3077c2ea8d | |||
| be6c63723c | |||
| c5a3cb7707 | |||
| cce9f0b183 | |||
| 84b74703b2 | |||
| 9bf3600e5b | |||
| 9f20c8ed1f | |||
| 84e66c34f2 | |||
| e04df37988 | |||
| 0eb6eb855b | |||
| c91412f949 | |||
| 0a89f31a29 | |||
| 9badeac832 | |||
| b59c41d9cd | |||
| 8f083ff91e | |||
| 0f44836025 | |||
| 68ee2d747d | |||
| e29c76678d | |||
| a4005c7d81 | |||
| efe6d916e2 | |||
| a06ae48e2f | |||
| 910b6a1369 | |||
| 7818658ee8 | |||
| 89e34b4fd3 | |||
| 2f5a00dbda | |||
| 7f87d42f9a | |||
| e5f41731ae | |||
| a6fda391ae | |||
| 1ded24dcfc | |||
| 8c42640853 | |||
| 38aabc7983 | |||
| 4324845d29 | |||
| 93b12bccca | |||
| 89fb4ef03f | |||
| b8ab187db0 | |||
| a9790c1f66 | |||
| b46634b8f7 | |||
| 633e7ffb46 | |||
| dd2077bf12 | |||
| 0323ba2bb9 | |||
| b9524a0f58 | |||
| e7cd417894 | |||
| 07cc47a0ff | |||
| ca25db845a | |||
| 64a0ce31a8 | |||
| a8fe8d9bfb | |||
| c346f145d1 | |||
| 45dd6f2632 | |||
| 22d28759ab | |||
| 890d0f7440 | |||
| b342eb870e | |||
| fc71e0cafd | |||
| 1ecb57fda0 | |||
| 3c1ec82289 | |||
| 089e403d87 | |||
| dd2b477cda | |||
| 0783c532de | |||
| 8817ae7805 | |||
| 6a900c3c42 | |||
| 0ba80c9a86 | |||
| d712b65ec5 | |||
| 383b2c1351 | |||
| fe7484f0f4 | |||
| b63d04b10c | |||
| 5a35068c87 | |||
| 4899773e61 | |||
| 996f4afaf7 | |||
| 0af807ac92 | |||
| f827eb4242 | |||
| 6f4b00b5c2 | |||
| d681ad20cf | |||
| 5818d58caf | |||
| da4eab8fdb | |||
| 9323ca9339 |
@@ -9,7 +9,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git python3 && sudo apt-get update --fix-missing
|
||||
- name: Install pip3
|
||||
run: sudo apt install -y python3-pip
|
||||
- name: Install Python3 modules
|
||||
run: sudo pip3 install pandas tabulate
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,7 +13,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git python3 && sudo apt-get update --fix-missing
|
||||
- name: Install pip3
|
||||
run: sudo apt install -y python3-pip
|
||||
- name: Install Python3 modules
|
||||
run: sudo pip3 install pandas tabulate
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
|
||||
Generated
+1345
-3137
File diff suppressed because it is too large
Load Diff
+117
-17
@@ -122,6 +122,7 @@ members = [
|
||||
# "wasm/full-nym-wasm",
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/zknym-lib",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -158,44 +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.6.20"
|
||||
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"
|
||||
hyper = "0.14.27"
|
||||
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"
|
||||
reqwest = { version = "0.11.22", default-features = false }
|
||||
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"
|
||||
utoipa = "3.5.0"
|
||||
utoipa-swagger-ui = "3.1.5"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
url = "2.4"
|
||||
utoipa = "4.2.0"
|
||||
utoipa-swagger-ui = "6.0.0"
|
||||
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" }
|
||||
@@ -207,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"
|
||||
@@ -235,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 = []
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "nym-client-core"
|
||||
version = "1.1.15"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.66"
|
||||
rust-version = "1.70"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -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,11 +21,10 @@ 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"] }
|
||||
tungstenite = { workspace = true, default-features = false }
|
||||
tokio = { workspace = true, features = ["macros"] }
|
||||
time = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
@@ -48,7 +47,7 @@ nym-validator-client = { path = "../client-libs/validator-client", default-featu
|
||||
nym-task = { path = "../task" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-client-core-config-types = { path = "./config-types", features = ["disk-persistence"]}
|
||||
nym-client-core-config-types = { path = "./config-types", features = ["disk-persistence"] }
|
||||
nym-client-core-surb-storage = { path = "./surb-storage" }
|
||||
nym-client-core-gateways-storage = { path = "./gateways-storage" }
|
||||
|
||||
@@ -74,8 +73,17 @@ workspace = true
|
||||
features = ["time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
version = "0.20.1"
|
||||
features = ["rustls-tls-native-roots"]
|
||||
workspace = true
|
||||
features = ["rustls-tls-webpki-roots"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tungstenite]
|
||||
workspace = true
|
||||
default-features = true
|
||||
features = ["rustls-tls-webpki-roots"]
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.tungstenite]
|
||||
workspace = true
|
||||
default-features = false
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
|
||||
workspace = true
|
||||
@@ -100,7 +108,7 @@ version = "0.3.17"
|
||||
features = ["wasm-bindgen"]
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -39,7 +39,7 @@ use log::{debug, error, info, warn};
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_gateway_client::{
|
||||
AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter,
|
||||
};
|
||||
@@ -670,6 +670,7 @@ where
|
||||
let self_address = Self::mix_address(&init_res);
|
||||
let ack_key = init_res.client_keys.ack_key();
|
||||
let encryption_keys = init_res.client_keys.encryption_keypair();
|
||||
let identity_keys = init_res.client_keys.identity_keypair();
|
||||
|
||||
// the components are started in very specific order. Unless you know what you are doing,
|
||||
// do not change that.
|
||||
@@ -792,6 +793,7 @@ where
|
||||
|
||||
Ok(BaseClient {
|
||||
address: self_address,
|
||||
identity_keys,
|
||||
client_input: ClientInputStatus::AwaitingProducer {
|
||||
client_input: ClientInput {
|
||||
connection_command_sender: client_connection_tx,
|
||||
@@ -816,6 +818,7 @@ where
|
||||
|
||||
pub struct BaseClient {
|
||||
pub address: Recipient,
|
||||
pub identity_keys: Arc<identity::KeyPair>,
|
||||
pub client_input: ClientInputStatus,
|
||||
pub client_output: ClientOutputStatus,
|
||||
pub client_state: ClientState,
|
||||
|
||||
@@ -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
|
||||
@@ -48,10 +48,7 @@ features = ["net", "sync", "time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
workspace = true
|
||||
# the choice of this particular tls feature was arbitrary;
|
||||
# if you reckon a different one would be more appropriate, feel free to change it
|
||||
# features = ["native-tls"]
|
||||
features = ["rustls-tls-native-roots"]
|
||||
features = ["rustls-tls-webpki-roots"]
|
||||
|
||||
# wasm-only dependencies
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||
|
||||
@@ -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" }
|
||||
@@ -24,7 +24,6 @@ nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contr
|
||||
nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
nym-http-api-client = { path = "../../../common/http-api-client"}
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
@@ -49,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"] }
|
||||
@@ -67,6 +66,14 @@ cosmwasm-std = { workspace = true }
|
||||
workspace = true
|
||||
features = ["tokio"]
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.reqwest]
|
||||
workspace = true
|
||||
features = ["json"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.reqwest]
|
||||
workspace = true
|
||||
features = ["json", "rustls-tls"]
|
||||
|
||||
[dev-dependencies]
|
||||
bip39 = { workspace = true }
|
||||
cosmrs = { workspace = true, features = ["bip32"] }
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -157,7 +157,7 @@ async fn fetch_delegation_data(
|
||||
// If a pending undelegate tx is found, remove it from delegation map
|
||||
Undelegate { owner, mix_id, .. } => {
|
||||
if owner == address.as_ref()
|
||||
&& existing_delegation_map.get(&mix_id.to_string()).is_some()
|
||||
&& existing_delegation_map.contains_key(&mix_id.to_string())
|
||||
{
|
||||
existing_delegation_map.remove(&mix_id.to_string());
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -328,4 +328,8 @@ impl EpochState {
|
||||
pub fn is_dealing_exchange(&self) -> bool {
|
||||
matches!(self, EpochState::DealingExchange { .. })
|
||||
}
|
||||
|
||||
pub fn is_waiting_initialisation(&self) -> bool {
|
||||
matches!(self, EpochState::WaitingInitialisation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -18,4 +18,7 @@ pub enum StorageError {
|
||||
|
||||
#[error("No unused credential in database. You need to buy at least one")]
|
||||
NoCredential,
|
||||
|
||||
#[error("Database unique constraint violation. Is the credential already imported?")]
|
||||
ConstraintUnique,
|
||||
}
|
||||
|
||||
@@ -69,9 +69,21 @@ impl Storage for PersistentStorage {
|
||||
bandwidth_credential.credential_data,
|
||||
bandwidth_credential.epoch_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
.await
|
||||
.map_err(|err| {
|
||||
// There is one error we want to handle specifically.
|
||||
// Check if database_error is `SqliteError` with code 2067 which
|
||||
// means UNIQUE constraint violation
|
||||
if let Some(db_error) = err.as_database_error() {
|
||||
if db_error.code().map_or(false, |code| code == "2067") {
|
||||
StorageError::ConstraintUnique
|
||||
} else {
|
||||
err.into()
|
||||
}
|
||||
} else {
|
||||
err.into()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_next_unspent_credential(
|
||||
|
||||
@@ -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,11 +8,11 @@ use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use nym_coconut::{
|
||||
aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar, keygen,
|
||||
prepare_blind_sign, prove_bandwidth_credential, verify_credential, Attribute, Base58,
|
||||
BlindSignRequest, BlindedSerialNumber, BlindedSignature, Bytable, CoconutError, KeyPair,
|
||||
Parameters, PrivateAttribute, PublicAttribute, SecretKey, Signature, SignatureShare,
|
||||
VerificationKey, VerifyCredentialRequest,
|
||||
aggregate_signature_shares, aggregate_signature_shares_and_verify, aggregate_verification_keys,
|
||||
blind_sign, hash_to_scalar, keygen, prepare_blind_sign, prove_bandwidth_credential,
|
||||
verify_credential, Attribute, Base58, BlindSignRequest, BlindedSerialNumber, BlindedSignature,
|
||||
Bytable, CoconutError, KeyPair, Parameters, PrivateAttribute, PublicAttribute, SecretKey,
|
||||
Signature, SignatureShare, VerificationKey, VerifyCredentialRequest,
|
||||
};
|
||||
|
||||
pub const VOUCHER_INFO_TYPE: &str = "BandwidthVoucher";
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -12,7 +12,8 @@ use serde::{Deserialize, Serialize};
|
||||
use time::{Duration, OffsetDateTime, Time};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
pub const MAX_FREE_PASS_VALIDITY: Duration = Duration::WEEK; // 1 week
|
||||
pub const DEFAULT_FREE_PASS_VALIDITY: Duration = Duration::WEEK; // 1 week
|
||||
pub const MAX_FREE_PASS_VALIDITY: Duration = Duration::weeks(12); // 12 weeks
|
||||
|
||||
#[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
|
||||
pub struct FreePassIssuedData {
|
||||
@@ -77,9 +78,9 @@ impl FreePassIssuanceData {
|
||||
}
|
||||
|
||||
pub fn default_expiry_date() -> OffsetDateTime {
|
||||
// set it to furthest midnight in the future such as it's no more than a week away,
|
||||
// set it to the furthest midnight in the future such as it's no more than a week away,
|
||||
// i.e. if it's currently for example 9:43 on 2nd March 2024, it will set it to 0:00 on 9th March 2024
|
||||
(OffsetDateTime::now_utc() + MAX_FREE_PASS_VALIDITY).replace_time(Time::MIDNIGHT)
|
||||
(OffsetDateTime::now_utc() + DEFAULT_FREE_PASS_VALIDITY).replace_time(Time::MIDNIGHT)
|
||||
}
|
||||
|
||||
pub fn expiry_date_attribute(&self) -> &Attribute {
|
||||
|
||||
@@ -10,18 +10,19 @@ use crate::coconut::bandwidth::{
|
||||
use crate::coconut::utils::scalar_serde_helper;
|
||||
use crate::error::Error;
|
||||
use nym_credentials_interface::{
|
||||
aggregate_signature_shares, hash_to_scalar, prepare_blind_sign, Attribute, BlindedSerialNumber,
|
||||
BlindedSignature, Parameters, PrivateAttribute, PublicAttribute, Signature, SignatureShare,
|
||||
VerificationKey,
|
||||
aggregate_signature_shares, aggregate_signature_shares_and_verify, hash_to_scalar,
|
||||
prepare_blind_sign, Attribute, BlindedSerialNumber, BlindedSignature, Parameters,
|
||||
PrivateAttribute, PublicAttribute, Signature, SignatureShare, VerificationKey,
|
||||
};
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nyxd::{Coin, Hash};
|
||||
use nym_validator_client::signing::AccountData;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
pub use nym_validator_client::nyxd::{Coin, Hash};
|
||||
|
||||
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
|
||||
pub enum BandwidthCredentialIssuanceDataVariant {
|
||||
Voucher(BandwidthVoucherIssuanceData),
|
||||
@@ -265,6 +266,13 @@ impl IssuanceBandwidthCredential {
|
||||
self.unblind_signature(validator_vk, &signing_data, blinded_signature)
|
||||
}
|
||||
|
||||
pub fn unchecked_aggregate_signature_shares(
|
||||
&self,
|
||||
shares: &[SignatureShare],
|
||||
) -> Result<Signature, Error> {
|
||||
aggregate_signature_shares(shares).map_err(Error::SignatureAggregationError)
|
||||
}
|
||||
|
||||
pub fn aggregate_signature_shares(
|
||||
&self,
|
||||
verification_key: &VerificationKey,
|
||||
@@ -279,7 +287,7 @@ impl IssuanceBandwidthCredential {
|
||||
attributes.extend_from_slice(&private_attributes);
|
||||
attributes.extend_from_slice(&public_attributes);
|
||||
|
||||
aggregate_signature_shares(params, verification_key, &attributes, shares)
|
||||
aggregate_signature_shares_and_verify(params, verification_key, &attributes, shares)
|
||||
.map_err(Error::SignatureAggregationError)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::coconut::utils::scalar_serde_helper;
|
||||
use crate::error::Error;
|
||||
use nym_api_requests::coconut::BlindSignRequestBody;
|
||||
use nym_credentials_interface::{
|
||||
hash_to_scalar, Attribute, BlindSignRequest, BlindedSignature, PublicAttribute,
|
||||
hash_to_scalar, Attribute, BlindSignRequest, BlindedSignature, CredentialType, PublicAttribute,
|
||||
};
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_validator_client::nyxd::{Coin, Hash};
|
||||
@@ -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
|
||||
}
|
||||
@@ -123,6 +127,10 @@ impl BandwidthVoucherIssuanceData {
|
||||
&self.value_prehashed
|
||||
}
|
||||
|
||||
pub fn typ() -> CredentialType {
|
||||
CredentialType::Voucher
|
||||
}
|
||||
|
||||
pub fn tx_hash(&self) -> Hash {
|
||||
self.deposit_tx_hash
|
||||
}
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -18,9 +18,12 @@ pub const VESTING_CONTRACT_ADDRESS: &str =
|
||||
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
|
||||
|
||||
pub const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "";
|
||||
pub const GROUP_CONTRACT_ADDRESS: &str = "";
|
||||
pub const MULTISIG_CONTRACT_ADDRESS: &str = "";
|
||||
pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "";
|
||||
pub const GROUP_CONTRACT_ADDRESS: &str =
|
||||
"n1e2zq4886zzewpvpucmlw8v9p7zv692f6yck4zjzxh699dkcmlrfqk2knsr";
|
||||
pub const MULTISIG_CONTRACT_ADDRESS: &str =
|
||||
"n1txayqfz5g9qww3rlflpg025xd26m9payz96u54x4fe3s2ktz39xqk67gzx";
|
||||
pub const COCONUT_DKG_CONTRACT_ADDRESS: &str =
|
||||
"n19604yflqggs9mk2z26mqygq43q2kr3n932egxx630svywd5mpxjsztfpvx";
|
||||
pub const EPHEMERA_CONTRACT_ADDRESS: &str = "";
|
||||
|
||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -6,10 +6,10 @@ use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use ff::Field;
|
||||
use group::{Curve, Group};
|
||||
use nym_coconut::{
|
||||
aggregate_signature_shares, aggregate_verification_keys, blind_sign, prepare_blind_sign,
|
||||
prove_bandwidth_credential, random_scalars_refs, setup, ttp_keygen, verify_credential,
|
||||
verify_partial_blind_signature, Attribute, BlindedSignature, Parameters, Signature,
|
||||
SignatureShare, VerificationKey,
|
||||
aggregate_signature_shares_and_verify, aggregate_verification_keys, blind_sign,
|
||||
prepare_blind_sign, prove_bandwidth_credential, random_scalars_refs, setup, ttp_keygen,
|
||||
verify_credential, verify_partial_blind_signature, Attribute, BlindedSignature, Parameters,
|
||||
Signature, SignatureShare, VerificationKey,
|
||||
};
|
||||
use rand::seq::SliceRandom;
|
||||
use std::ops::Neg;
|
||||
@@ -99,7 +99,7 @@ fn unblind_and_aggregate(
|
||||
let mut attributes = vec![];
|
||||
attributes.extend_from_slice(private_attributes);
|
||||
attributes.extend_from_slice(public_attributes);
|
||||
aggregate_signature_shares(
|
||||
aggregate_signature_shares_and_verify(
|
||||
params,
|
||||
verification_key,
|
||||
&attributes,
|
||||
|
||||
@@ -4,14 +4,18 @@
|
||||
#![warn(clippy::expect_used)]
|
||||
#![warn(clippy::unwrap_used)]
|
||||
|
||||
pub use bls12_381::Scalar;
|
||||
pub use elgamal::elgamal_keygen;
|
||||
pub use elgamal::ElGamalKeyPair;
|
||||
pub use elgamal::PublicKey;
|
||||
pub use error::CoconutError;
|
||||
pub use scheme::aggregation::aggregate_key_shares;
|
||||
pub use scheme::aggregation::aggregate_signature_shares;
|
||||
pub use scheme::aggregation::aggregate_signature_shares_and_verify;
|
||||
pub use scheme::aggregation::aggregate_verification_keys;
|
||||
pub use scheme::issuance::blind_sign;
|
||||
pub use scheme::issuance::prepare_blind_sign;
|
||||
pub use scheme::issuance::sign;
|
||||
pub use scheme::issuance::verify_partial_blind_signature;
|
||||
pub use scheme::issuance::BlindSignRequest;
|
||||
pub use scheme::keygen::keygen;
|
||||
@@ -19,16 +23,19 @@ pub use scheme::keygen::ttp_keygen;
|
||||
pub use scheme::keygen::KeyPair;
|
||||
pub use scheme::keygen::SecretKey;
|
||||
pub use scheme::keygen::VerificationKey;
|
||||
pub use scheme::keygen::VerificationKeyShare;
|
||||
pub use scheme::setup::setup;
|
||||
pub use scheme::setup::Parameters;
|
||||
pub use scheme::verification::check_vk_pairing;
|
||||
pub use scheme::verification::prove_bandwidth_credential;
|
||||
pub use scheme::verification::verify;
|
||||
pub use scheme::verification::verify_credential;
|
||||
pub use scheme::verification::BlindedSerialNumber;
|
||||
pub use scheme::verification::VerifyCredentialRequest;
|
||||
pub use scheme::BlindedSignature;
|
||||
pub use scheme::Signature;
|
||||
pub use scheme::SignatureShare;
|
||||
pub use scheme::SignerIndex;
|
||||
pub use traits::Base58;
|
||||
pub use traits::Bytable;
|
||||
pub use utils::hash_to_scalar;
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::error::{CoconutError, Result};
|
||||
use crate::scheme::verification::check_bilinear_pairing;
|
||||
use crate::scheme::{PartialSignature, Signature, SignatureShare, SignerIndex, VerificationKey};
|
||||
use crate::utils::perform_lagrangian_interpolation_at_origin;
|
||||
use crate::{Attribute, Parameters};
|
||||
use crate::{Attribute, Parameters, VerificationKeyShare};
|
||||
|
||||
pub(crate) trait Aggregatable: Sized {
|
||||
fn aggregate(aggregatable: &[Self], indices: Option<&[SignerIndex]>) -> Result<Self>;
|
||||
@@ -80,7 +80,23 @@ pub fn aggregate_verification_keys(
|
||||
Aggregatable::aggregate(keys, indices)
|
||||
}
|
||||
|
||||
pub fn aggregate_key_shares(shares: &[VerificationKeyShare]) -> Result<VerificationKey> {
|
||||
let (keys, indices): (Vec<_>, Vec<_>) = shares
|
||||
.iter()
|
||||
.map(|share| (share.key.clone(), share.index))
|
||||
.unzip();
|
||||
|
||||
aggregate_verification_keys(&keys, Some(&indices))
|
||||
}
|
||||
|
||||
pub fn aggregate_signatures(
|
||||
signatures: &[PartialSignature],
|
||||
indices: Option<&[SignerIndex]>,
|
||||
) -> Result<Signature> {
|
||||
Aggregatable::aggregate(signatures, indices)
|
||||
}
|
||||
|
||||
pub fn aggregate_signatures_and_verify(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKey,
|
||||
attributes: &[&Attribute],
|
||||
@@ -88,11 +104,7 @@ pub fn aggregate_signatures(
|
||||
indices: Option<&[SignerIndex]>,
|
||||
) -> Result<Signature> {
|
||||
// aggregate the signature
|
||||
|
||||
let signature = match Aggregatable::aggregate(signatures, indices) {
|
||||
Ok(res) => res,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let signature = aggregate_signatures(signatures, indices)?;
|
||||
|
||||
// Verify the signature
|
||||
let alpha = verification_key.alpha;
|
||||
@@ -116,7 +128,16 @@ pub fn aggregate_signatures(
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
pub fn aggregate_signature_shares(
|
||||
pub fn aggregate_signature_shares(shares: &[SignatureShare]) -> Result<Signature> {
|
||||
let (signatures, indices): (Vec<_>, Vec<_>) = shares
|
||||
.iter()
|
||||
.map(|share| (*share.signature(), share.index()))
|
||||
.unzip();
|
||||
|
||||
aggregate_signatures(&signatures, Some(&indices))
|
||||
}
|
||||
|
||||
pub fn aggregate_signature_shares_and_verify(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKey,
|
||||
attributes: &[&Attribute],
|
||||
@@ -127,7 +148,7 @@ pub fn aggregate_signature_shares(
|
||||
.map(|share| (*share.signature(), share.index()))
|
||||
.unzip();
|
||||
|
||||
aggregate_signatures(
|
||||
aggregate_signatures_and_verify(
|
||||
params,
|
||||
verification_key,
|
||||
attributes,
|
||||
@@ -210,7 +231,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn signature_aggregation_works_for_any_subset_of_signatures() {
|
||||
let mut params = Parameters::new(2).unwrap();
|
||||
let params = Parameters::new(2).unwrap();
|
||||
random_scalars_refs!(attributes, params, 2);
|
||||
|
||||
let keypairs = ttp_keygen(¶ms, 3, 5).unwrap();
|
||||
@@ -227,12 +248,12 @@ mod tests {
|
||||
|
||||
let sigs = sks
|
||||
.iter()
|
||||
.map(|sk| sign(&mut params, sk, &attributes).unwrap())
|
||||
.map(|sk| sign(¶ms, sk, &attributes).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// aggregating (any) threshold works
|
||||
let aggr_vk_1 = aggregate_verification_keys(&vks[..3], Some(&[1, 2, 3])).unwrap();
|
||||
let aggr_sig1 = aggregate_signatures(
|
||||
let aggr_sig1 = aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk_1,
|
||||
&attributes,
|
||||
@@ -242,7 +263,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let aggr_vk_2 = aggregate_verification_keys(&vks[2..], Some(&[3, 4, 5])).unwrap();
|
||||
let aggr_sig2 = aggregate_signatures(
|
||||
let aggr_sig2 = aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk_1,
|
||||
&attributes,
|
||||
@@ -258,7 +279,7 @@ mod tests {
|
||||
|
||||
// aggregating threshold+1 works
|
||||
let aggr_vk_more = aggregate_verification_keys(&vks[1..], Some(&[2, 3, 4, 5])).unwrap();
|
||||
let aggr_more = aggregate_signatures(
|
||||
let aggr_more = aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk_more,
|
||||
&attributes,
|
||||
@@ -270,7 +291,7 @@ mod tests {
|
||||
|
||||
// aggregating all
|
||||
let aggr_vk_all = aggregate_verification_keys(&vks, Some(&[1, 2, 3, 4, 5])).unwrap();
|
||||
let aggr_all = aggregate_signatures(
|
||||
let aggr_all = aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk_all,
|
||||
&attributes,
|
||||
@@ -282,7 +303,7 @@ mod tests {
|
||||
|
||||
// not taking enough points (threshold was 3) should fail
|
||||
let aggr_vk_not_enough = aggregate_verification_keys(&vks[..2], Some(&[1, 2])).unwrap();
|
||||
let aggr_not_enough = aggregate_signatures(
|
||||
let aggr_not_enough = aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk_not_enough,
|
||||
&attributes,
|
||||
@@ -294,7 +315,7 @@ mod tests {
|
||||
|
||||
// taking wrong index should fail
|
||||
let aggr_vk_bad = aggregate_verification_keys(&vks[2..], Some(&[1, 2, 3])).unwrap();
|
||||
assert!(aggregate_signatures(
|
||||
assert!(aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk_bad,
|
||||
&attributes,
|
||||
@@ -330,9 +351,14 @@ mod tests {
|
||||
.unzip();
|
||||
|
||||
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
|
||||
assert!(
|
||||
aggregate_signatures(¶ms, &aggr_vk_all, &attributes, &signatures, None).is_err()
|
||||
);
|
||||
assert!(aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk_all,
|
||||
&attributes,
|
||||
&signatures,
|
||||
None
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -352,11 +378,15 @@ mod tests {
|
||||
.unzip();
|
||||
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
|
||||
|
||||
assert!(
|
||||
aggregate_signatures(¶ms, &aggr_vk_all, &attributes, &signatures, Some(&[]))
|
||||
.is_err()
|
||||
);
|
||||
assert!(aggregate_signatures(
|
||||
assert!(aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk_all,
|
||||
&attributes,
|
||||
&signatures,
|
||||
Some(&[])
|
||||
)
|
||||
.is_err());
|
||||
assert!(aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk_all,
|
||||
&attributes,
|
||||
@@ -383,7 +413,7 @@ mod tests {
|
||||
.unzip();
|
||||
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
|
||||
|
||||
assert!(aggregate_signatures(
|
||||
assert!(aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk_all,
|
||||
&attributes,
|
||||
|
||||
@@ -13,9 +13,8 @@ use crate::scheme::setup::Parameters;
|
||||
use crate::scheme::BlindedSignature;
|
||||
use crate::scheme::SecretKey;
|
||||
use crate::Attribute;
|
||||
/// Creates a Coconut Signature under a given secret key on a set of public attributes only.
|
||||
#[cfg(test)]
|
||||
use crate::Signature;
|
||||
|
||||
// TODO: possibly completely remove those two functions.
|
||||
// They only exist to have a simpler and smaller code snippets to test
|
||||
// basic functionalities.
|
||||
@@ -158,6 +157,10 @@ impl BlindSignRequest {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn verify_commitment_hash(&self, public_attributes: &[&Attribute]) -> bool {
|
||||
self.commitment_hash == compute_hash(self.commitment, public_attributes)
|
||||
}
|
||||
|
||||
pub fn get_commitment_hash(&self) -> G1Projective {
|
||||
self.commitment_hash
|
||||
}
|
||||
@@ -426,9 +429,9 @@ pub fn verify_partial_blind_signature(
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Creates a Coconut Signature under a given secret key on a set of public attributes only.
|
||||
pub fn sign(
|
||||
params: &mut Parameters,
|
||||
params: &Parameters,
|
||||
secret_key: &SecretKey,
|
||||
public_attributes: &[&Attribute],
|
||||
) -> Result<Signature> {
|
||||
|
||||
@@ -151,10 +151,6 @@ impl Base58 for SecretKey {}
|
||||
// TODO: perhaps change points to affine representation
|
||||
// to make verification slightly more efficient?
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(
|
||||
feature = "key-zeroize",
|
||||
derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)
|
||||
)]
|
||||
pub struct VerificationKey {
|
||||
// TODO add gen2 as per the paper or imply it from the fact library is using bls381?
|
||||
pub(crate) alpha: G2Projective,
|
||||
@@ -411,12 +407,23 @@ impl Bytable for VerificationKey {
|
||||
|
||||
impl Base58 for VerificationKey {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VerificationKeyShare {
|
||||
pub key: VerificationKey,
|
||||
pub index: SignerIndex,
|
||||
}
|
||||
|
||||
impl From<(VerificationKey, SignerIndex)> for VerificationKeyShare {
|
||||
fn from(value: (VerificationKey, SignerIndex)) -> Self {
|
||||
VerificationKeyShare {
|
||||
key: value.0,
|
||||
index: value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Clone))]
|
||||
#[cfg_attr(
|
||||
feature = "key-zeroize",
|
||||
derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)
|
||||
)]
|
||||
pub struct KeyPair {
|
||||
secret_key: SecretKey,
|
||||
verification_key: VerificationKey,
|
||||
@@ -425,6 +432,12 @@ pub struct KeyPair {
|
||||
pub index: Option<SignerIndex>,
|
||||
}
|
||||
|
||||
impl From<KeyPair> for (SecretKey, VerificationKey) {
|
||||
fn from(value: KeyPair) -> Self {
|
||||
(value.secret_key, value.verification_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl PemStorableKeyPair for KeyPair {
|
||||
type PrivatePemKey = SecretKey;
|
||||
type PublicPemKey = VerificationKey;
|
||||
@@ -461,6 +474,13 @@ impl KeyPair {
|
||||
&self.verification_key
|
||||
}
|
||||
|
||||
pub fn to_verification_key_share(&self) -> Option<VerificationKeyShare> {
|
||||
self.index.map(|index| VerificationKeyShare {
|
||||
key: self.verification_key.clone(),
|
||||
index,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
// Schema is coconutkeypair[14]|secret_key_len[8]|secret_key[secret_key_len]|verification_key_len[8]|verification_key[verification_key_len]|signer_index[8] - optional
|
||||
self.to_byte_vec()
|
||||
|
||||
@@ -70,6 +70,11 @@ impl Signature {
|
||||
&self.1
|
||||
}
|
||||
|
||||
pub fn randomise_simple(&self, params: &Parameters) -> Signature {
|
||||
let r = params.random_scalar();
|
||||
Signature(self.0 * r, self.1 * r)
|
||||
}
|
||||
|
||||
pub fn randomise(&self, params: &Parameters) -> (Signature, Scalar) {
|
||||
let r = params.random_scalar();
|
||||
let r_prime = params.random_scalar();
|
||||
@@ -191,7 +196,7 @@ impl BlindedSignature {
|
||||
&self,
|
||||
partial_verification_key: &VerificationKey,
|
||||
pedersen_commitments_openings: &[Scalar],
|
||||
) -> Result<Signature> {
|
||||
) -> Signature {
|
||||
// parse the signature
|
||||
let h = &self.0;
|
||||
let c = &self.1;
|
||||
@@ -204,7 +209,7 @@ impl BlindedSignature {
|
||||
|
||||
let unblinded_c = c - blinding_removers;
|
||||
|
||||
Ok(Signature(*h, unblinded_c))
|
||||
Signature(*h, unblinded_c)
|
||||
}
|
||||
|
||||
pub fn unblind_and_verify(
|
||||
@@ -216,7 +221,7 @@ impl BlindedSignature {
|
||||
commitment_hash: &G1Projective,
|
||||
pedersen_commitments_openings: &[Scalar],
|
||||
) -> Result<Signature> {
|
||||
let unblinded = self.unblind(partial_verification_key, pedersen_commitments_openings)?;
|
||||
let unblinded = self.unblind(partial_verification_key, pedersen_commitments_openings);
|
||||
unblinded.verify(
|
||||
params,
|
||||
partial_verification_key,
|
||||
@@ -240,6 +245,7 @@ impl BlindedSignature {
|
||||
}
|
||||
|
||||
// perhaps this should take signature by reference? we'll see how it goes
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct SignatureShare {
|
||||
signature: Signature,
|
||||
index: SignerIndex,
|
||||
@@ -276,7 +282,9 @@ impl SignatureShare {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::hash_to_scalar;
|
||||
use crate::scheme::aggregation::{aggregate_signatures, aggregate_verification_keys};
|
||||
use crate::scheme::aggregation::{
|
||||
aggregate_signatures_and_verify, aggregate_verification_keys,
|
||||
};
|
||||
use crate::scheme::issuance::{blind_sign, compute_hash, prepare_blind_sign, sign};
|
||||
use crate::scheme::keygen::{keygen, ttp_keygen};
|
||||
use crate::scheme::verification::{prove_bandwidth_credential, verify, verify_credential};
|
||||
@@ -418,13 +426,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn verification_on_two_public_attributes() {
|
||||
let mut params = Parameters::new(2).unwrap();
|
||||
let params = Parameters::new(2).unwrap();
|
||||
random_scalars_refs!(attributes, params, 2);
|
||||
|
||||
let keypair1 = keygen(¶ms);
|
||||
let keypair2 = keygen(¶ms);
|
||||
let sig1 = sign(&mut params, keypair1.secret_key(), &attributes).unwrap();
|
||||
let sig2 = sign(&mut params, keypair2.secret_key(), &attributes).unwrap();
|
||||
let sig1 = sign(¶ms, keypair1.secret_key(), &attributes).unwrap();
|
||||
let sig2 = sign(¶ms, keypair2.secret_key(), &attributes).unwrap();
|
||||
|
||||
assert!(verify(
|
||||
¶ms,
|
||||
@@ -568,9 +576,14 @@ mod tests {
|
||||
attributes.extend_from_slice(&public_attributes);
|
||||
|
||||
let aggr_vk = aggregate_verification_keys(&vks[..2], Some(&[1, 2])).unwrap();
|
||||
let aggr_sig =
|
||||
aggregate_signatures(¶ms, &aggr_vk, &attributes, &sigs[..2], Some(&[1, 2]))
|
||||
.unwrap();
|
||||
let aggr_sig = aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk,
|
||||
&attributes,
|
||||
&sigs[..2],
|
||||
Some(&[1, 2]),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let theta = prove_bandwidth_credential(
|
||||
¶ms,
|
||||
@@ -590,9 +603,14 @@ mod tests {
|
||||
|
||||
// taking different subset of keys and credentials
|
||||
let aggr_vk = aggregate_verification_keys(&vks[1..], Some(&[2, 3])).unwrap();
|
||||
let aggr_sig =
|
||||
aggregate_signatures(¶ms, &aggr_vk, &attributes, &sigs[1..], Some(&[2, 3]))
|
||||
.unwrap();
|
||||
let aggr_sig = aggregate_signatures_and_verify(
|
||||
¶ms,
|
||||
&aggr_vk,
|
||||
&attributes,
|
||||
&sigs[1..],
|
||||
Some(&[2, 3]),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let theta = prove_bandwidth_credential(
|
||||
¶ms,
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::error::{CoconutError, Result};
|
||||
use crate::utils::hash_g1;
|
||||
|
||||
/// System-wide parameters used for the protocol
|
||||
#[derive(Clone)]
|
||||
pub struct Parameters {
|
||||
/// Generator of the G1 group
|
||||
g1: G1Affine,
|
||||
|
||||
@@ -288,7 +288,6 @@ pub fn verify_credential(
|
||||
}
|
||||
|
||||
// Used in tests only
|
||||
#[cfg(test)]
|
||||
pub fn verify(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKey,
|
||||
|
||||
@@ -75,8 +75,12 @@ pub fn theta_from_keys_and_attributes(
|
||||
attributes.extend_from_slice(public_attributes);
|
||||
|
||||
// Randomize credentials and generate any cryptographic material to verify them
|
||||
let signature =
|
||||
aggregate_signature_shares(params, &verification_key, &attributes, &signature_shares)?;
|
||||
let signature = aggregate_signature_shares_and_verify(
|
||||
params,
|
||||
&verification_key,
|
||||
&attributes,
|
||||
&signature_shares,
|
||||
)?;
|
||||
|
||||
// Generate cryptographic material to verify them
|
||||
let theta = prove_bandwidth_credential(
|
||||
|
||||
@@ -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,25 +12,27 @@ 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 = { workspace = true }
|
||||
sha2 = "0.10.8"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"] }
|
||||
tendermint.workspace = true
|
||||
tendermint-rpc = { workspace = true, features = ["websocket-client", "http-client"] }
|
||||
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
|
||||
|
||||
|
||||
# TEMP
|
||||
nym-bin-common = { path = "../bin-common", features = ["basic_tracing"]}
|
||||
#nym-bin-common = { path = "../bin-common", features = ["basic_tracing"]}
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Nyxd Scraper
|
||||
|
||||
## Pruning
|
||||
|
||||
Similarly to cosmos-sdk, we incorporate pruning into our (scraped) chain data. We attempt to follow their strategies as
|
||||
closely as possible for convenience's sake. Therefore, the following are available:
|
||||
|
||||
### Strategies
|
||||
|
||||
The strategies are configured in `config.toml`, with the format `pruning = "<strategy>"` where the options are:
|
||||
|
||||
* `default`: only the last 362,880 states(approximately 3.5 weeks worth of state) are kept; pruning at 10 block
|
||||
intervals
|
||||
* `nothing`: all historic states will be saved, nothing will be deleted (i.e. archiving node)
|
||||
* `everything`: 2 latest states will be kept; pruning at 10 block intervals.
|
||||
* `custom`: allow pruning options to be manually specified through `pruning.keep_recent`, and `pruning.interval`
|
||||
|
||||
### Custom Pruning
|
||||
|
||||
These are applied if and only if the pruning strategy is `custom`:
|
||||
|
||||
* `pruning.keep_recent`: N means to keep all of the last N blocks
|
||||
* `pruning.interval`: N means to delete old block data from disk every Nth block.
|
||||
@@ -8,6 +8,7 @@ use crate::error::ScraperError;
|
||||
use crate::modules::{BlockModule, MsgModule, TxModule};
|
||||
use crate::rpc_client::RpcClient;
|
||||
use crate::storage::{persist_block, ScraperStorage};
|
||||
use crate::PruningOptions;
|
||||
use futures::StreamExt;
|
||||
use std::collections::{BTreeMap, HashSet, VecDeque};
|
||||
use std::ops::{Add, Range};
|
||||
@@ -18,9 +19,10 @@ use tokio::sync::Notify;
|
||||
use tokio::time::{interval_at, Instant};
|
||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, error, info, warn};
|
||||
use tracing::{debug, error, info, instrument, trace, warn};
|
||||
|
||||
mod helpers;
|
||||
pub(crate) mod pruning;
|
||||
pub(crate) mod types;
|
||||
|
||||
const MISSING_BLOCKS_CHECK_INTERVAL: Duration = Duration::from_secs(30);
|
||||
@@ -40,9 +42,11 @@ impl PendingSync {
|
||||
}
|
||||
|
||||
pub struct BlockProcessor {
|
||||
pruning_options: PruningOptions,
|
||||
cancel: CancellationToken,
|
||||
synced: Arc<Notify>,
|
||||
last_processed_height: u32,
|
||||
last_pruned_height: u32,
|
||||
last_processed_at: Instant,
|
||||
pending_sync: PendingSync,
|
||||
queued_blocks: BTreeMap<u32, BlockToProcess>,
|
||||
@@ -62,6 +66,7 @@ pub struct BlockProcessor {
|
||||
|
||||
impl BlockProcessor {
|
||||
pub async fn new(
|
||||
pruning_options: PruningOptions,
|
||||
cancel: CancellationToken,
|
||||
synced: Arc<Notify>,
|
||||
incoming: UnboundedReceiver<BlockToProcess>,
|
||||
@@ -70,11 +75,17 @@ impl BlockProcessor {
|
||||
rpc_client: RpcClient,
|
||||
) -> Result<Self, ScraperError> {
|
||||
let last_processed = storage.get_last_processed_height().await?;
|
||||
let last_processed_height = last_processed.try_into().unwrap_or_default();
|
||||
|
||||
let last_pruned = storage.get_pruned_height().await?;
|
||||
let last_pruned_height = last_pruned.try_into().unwrap_or_default();
|
||||
|
||||
Ok(BlockProcessor {
|
||||
pruning_options,
|
||||
cancel,
|
||||
synced,
|
||||
last_processed_height: last_processed.try_into().unwrap_or_default(),
|
||||
last_processed_height,
|
||||
last_pruned_height,
|
||||
last_processed_at: Instant::now(),
|
||||
pending_sync: Default::default(),
|
||||
queued_blocks: Default::default(),
|
||||
@@ -131,12 +142,17 @@ impl BlockProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
let commit_start = Instant::now();
|
||||
tx.commit()
|
||||
.await
|
||||
.map_err(|source| ScraperError::StorageTxCommitFailure { source })?;
|
||||
crate::storage::log_db_operation_time("committing processing tx", commit_start);
|
||||
|
||||
self.last_processed_height = full_info.block.header.height.value() as u32;
|
||||
self.last_processed_at = Instant::now();
|
||||
if let Err(err) = self.maybe_prune_storage().await {
|
||||
error!("failed to prune the storage: {err}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -210,6 +226,61 @@ impl BlockProcessor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
async fn prune_storage(&mut self) -> Result<(), ScraperError> {
|
||||
let keep_recent = self.pruning_options.strategy_keep_recent();
|
||||
let last_to_keep = self.last_processed_height - keep_recent;
|
||||
|
||||
info!(
|
||||
keep_recent,
|
||||
oldest_to_keep = last_to_keep,
|
||||
"pruning the storage"
|
||||
);
|
||||
|
||||
let lowest: u32 = self
|
||||
.storage
|
||||
.lowest_block_height()
|
||||
.await?
|
||||
.unwrap_or_default()
|
||||
.try_into()
|
||||
.unwrap_or_default();
|
||||
|
||||
let to_prune = last_to_keep.saturating_sub(lowest);
|
||||
match to_prune {
|
||||
v if v > 1000 => warn!("approximately {v} blocks worth of data will be pruned"),
|
||||
v if v > 100 => info!("approximately {v} blocks worth of data will be pruned"),
|
||||
0 => trace!("no blocks to prune"),
|
||||
v => debug!("approximately {v} blocks worth of data will be pruned"),
|
||||
}
|
||||
|
||||
if to_prune == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.storage
|
||||
.prune_storage(last_to_keep, self.last_processed_height)
|
||||
.await?;
|
||||
|
||||
self.last_pruned_height = self.last_processed_height;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn maybe_prune_storage(&mut self) -> Result<(), ScraperError> {
|
||||
debug!("checking for storage pruning");
|
||||
|
||||
if self.pruning_options.strategy.is_nothing() {
|
||||
trace!("the current pruning strategy is 'nothing'");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let interval = self.pruning_options.strategy_interval();
|
||||
if self.last_pruned_height + interval <= self.last_processed_height {
|
||||
self.prune_storage().await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn next_incoming(&mut self, block: BlockToProcess) {
|
||||
let height = block.height;
|
||||
|
||||
@@ -279,6 +350,8 @@ impl BlockProcessor {
|
||||
async fn startup_resync(&mut self) -> Result<(), ScraperError> {
|
||||
assert!(self.pending_sync.is_empty());
|
||||
|
||||
self.maybe_prune_storage().await?;
|
||||
|
||||
let latest_block = self.rpc_client.current_block_height().await? as u32;
|
||||
if latest_block > self.last_processed_height && self.last_processed_height != 0 {
|
||||
let request_range = self.last_processed_height + 1..latest_block + 1;
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::ScraperError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const DEFAULT_PRUNING_KEEP_RECENT: u32 = 362880;
|
||||
pub const DEFAULT_PRUNING_INTERVAL: u32 = 10;
|
||||
pub const EVERYTHING_PRUNING_KEEP_RECENT: u32 = 2;
|
||||
pub const EVERYTHING_PRUNING_INTERVAL: u32 = 10;
|
||||
|
||||
/// We follow cosmos-sdk pruning strategies for convenience’s sake.
|
||||
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PruningStrategy {
|
||||
/// 'Default' strategy defines a pruning strategy where the last 362880 heights are
|
||||
/// kept where to-be pruned heights are pruned at every 10th height.
|
||||
/// The last 362880 heights are kept(approximately 3.5 weeks worth of state) assuming the typical
|
||||
/// block time is 6s. If these values do not match the applications' requirements, use the "custom" option.
|
||||
#[default]
|
||||
Default,
|
||||
|
||||
/// 'Everything' strategy defines a pruning strategy where all committed heights are
|
||||
/// deleted, storing only the current height and last 2 states. To-be pruned heights are
|
||||
/// pruned at every 10th height.
|
||||
Everything,
|
||||
|
||||
/// 'Nothing' strategy defines a pruning strategy where all heights are kept on disk.
|
||||
Nothing,
|
||||
|
||||
/// 'Custom' strategy defines a pruning strategy where the user specifies the pruning.
|
||||
Custom,
|
||||
}
|
||||
|
||||
impl PruningStrategy {
|
||||
pub fn is_custom(&self) -> bool {
|
||||
matches!(self, PruningStrategy::Custom)
|
||||
}
|
||||
|
||||
pub fn is_nothing(&self) -> bool {
|
||||
matches!(self, PruningStrategy::Nothing)
|
||||
}
|
||||
|
||||
pub fn is_everything(&self) -> bool {
|
||||
matches!(self, PruningStrategy::Everything)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct PruningOptions {
|
||||
/// keep_recent defines how many recent heights to keep on disk.
|
||||
pub keep_recent: u32,
|
||||
|
||||
/// interval defines the frequency of removing the pruned heights from the disk.
|
||||
pub interval: u32,
|
||||
|
||||
/// strategy defines the currently used kind of [PruningStrategy].
|
||||
pub strategy: PruningStrategy,
|
||||
}
|
||||
|
||||
impl PruningOptions {
|
||||
pub fn validate(&self) -> Result<(), ScraperError> {
|
||||
// if strategy is not set to custom, other options are meaningless since they won't be applied
|
||||
if !self.strategy.is_custom() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.interval == 0 {
|
||||
return Err(ScraperError::ZeroPruningInterval);
|
||||
}
|
||||
|
||||
if self.interval < EVERYTHING_PRUNING_INTERVAL {
|
||||
return Err(ScraperError::TooSmallPruningInterval {
|
||||
interval: self.interval,
|
||||
});
|
||||
}
|
||||
|
||||
if self.keep_recent < EVERYTHING_PRUNING_KEEP_RECENT {
|
||||
return Err(ScraperError::TooSmallKeepRecent {
|
||||
keep_recent: self.keep_recent,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn nothing() -> Self {
|
||||
PruningOptions {
|
||||
keep_recent: 0,
|
||||
interval: 0,
|
||||
strategy: PruningStrategy::Nothing,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strategy_interval(&self) -> u32 {
|
||||
match self.strategy {
|
||||
PruningStrategy::Default => DEFAULT_PRUNING_INTERVAL,
|
||||
PruningStrategy::Everything => EVERYTHING_PRUNING_INTERVAL,
|
||||
PruningStrategy::Nothing => 0,
|
||||
PruningStrategy::Custom => self.interval,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strategy_keep_recent(&self) -> u32 {
|
||||
match self.strategy {
|
||||
PruningStrategy::Default => DEFAULT_PRUNING_KEEP_RECENT,
|
||||
PruningStrategy::Everything => EVERYTHING_PRUNING_KEEP_RECENT,
|
||||
PruningStrategy::Nothing => 0,
|
||||
PruningStrategy::Custom => self.keep_recent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PruningOptions {
|
||||
fn default() -> Self {
|
||||
PruningOptions {
|
||||
keep_recent: DEFAULT_PRUNING_KEEP_RECENT,
|
||||
interval: DEFAULT_PRUNING_INTERVAL,
|
||||
strategy: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::block_processor::pruning::{
|
||||
EVERYTHING_PRUNING_INTERVAL, EVERYTHING_PRUNING_KEEP_RECENT,
|
||||
};
|
||||
use tendermint::Hash;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc::error::SendError;
|
||||
@@ -122,6 +125,15 @@ pub enum ScraperError {
|
||||
"could not find validator information for {address}; the validator has signed a commit"
|
||||
)]
|
||||
MissingValidatorInfoCommitted { address: String },
|
||||
|
||||
#[error("pruning.interval must not be set to 0. If you want to disable pruning, select pruning.strategy = \"nothing\"")]
|
||||
ZeroPruningInterval,
|
||||
|
||||
#[error("pruning.interval must not be smaller than {}. got: {interval}. for most aggressive pruning, select pruning.strategy = \"everything\"", EVERYTHING_PRUNING_INTERVAL)]
|
||||
TooSmallPruningInterval { interval: u32 },
|
||||
|
||||
#[error("pruning.keep_recent must not be smaller than {}. got: {keep_recent}. for most aggressive pruning, select pruning.strategy = \"everything\"", EVERYTHING_PRUNING_KEEP_RECENT)]
|
||||
TooSmallKeepRecent { keep_recent: u32 },
|
||||
}
|
||||
|
||||
impl<T> From<SendError<T>> for ScraperError {
|
||||
|
||||
@@ -14,6 +14,7 @@ pub(crate) mod rpc_client;
|
||||
pub(crate) mod scraper;
|
||||
pub mod storage;
|
||||
|
||||
pub use block_processor::pruning::{PruningOptions, PruningStrategy};
|
||||
pub use modules::{BlockModule, MsgModule, TxModule};
|
||||
pub use scraper::{Config, NyxdScraper};
|
||||
pub use storage::models;
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::modules::{BlockModule, MsgModule, TxModule};
|
||||
use crate::rpc_client::RpcClient;
|
||||
use crate::scraper::subscriber::ChainSubscriber;
|
||||
use crate::storage::ScraperStorage;
|
||||
use crate::PruningOptions;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::{channel, unbounded_channel};
|
||||
@@ -27,6 +28,8 @@ pub struct Config {
|
||||
pub rpc_url: Url,
|
||||
|
||||
pub database_path: PathBuf,
|
||||
|
||||
pub pruning_options: PruningOptions,
|
||||
}
|
||||
|
||||
pub struct NyxdScraperBuilder {
|
||||
@@ -54,6 +57,7 @@ impl NyxdScraperBuilder {
|
||||
processing_tx.clone(),
|
||||
);
|
||||
let mut block_processor = BlockProcessor::new(
|
||||
scraper.config.pruning_options,
|
||||
scraper.cancel_token.clone(),
|
||||
scraper.startup_sync.clone(),
|
||||
processing_rx,
|
||||
@@ -119,6 +123,7 @@ impl NyxdScraper {
|
||||
}
|
||||
|
||||
pub async fn new(config: Config) -> Result<Self, ScraperError> {
|
||||
config.pruning_options.validate()?;
|
||||
let storage = ScraperStorage::init(&config.database_path).await?;
|
||||
|
||||
Ok(NyxdScraper {
|
||||
@@ -160,6 +165,7 @@ impl NyxdScraper {
|
||||
processing_tx.clone(),
|
||||
);
|
||||
let block_processor = BlockProcessor::new(
|
||||
self.config.pruning_options,
|
||||
self.cancel_token.clone(),
|
||||
self.startup_sync.clone(),
|
||||
processing_rx,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::storage::log_db_operation_time;
|
||||
use crate::storage::models::{CommitSignature, Validator};
|
||||
use sqlx::types::time::OffsetDateTime;
|
||||
use sqlx::{Executor, Sqlite};
|
||||
use tokio::time::Instant;
|
||||
use tracing::{instrument, trace};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -25,10 +27,36 @@ impl StorageManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn get_lowest_block(&self) -> Result<Option<i64>, sqlx::Error> {
|
||||
trace!("get_lowest_block");
|
||||
let start = Instant::now();
|
||||
|
||||
let maybe_record = sqlx::query!(
|
||||
r#"
|
||||
SELECT height
|
||||
FROM block
|
||||
ORDER BY height ASC
|
||||
LIMIT 1
|
||||
"#,
|
||||
)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await?;
|
||||
log_db_operation_time("get_lowest_block", start);
|
||||
|
||||
if let Some(row) = maybe_record {
|
||||
Ok(row.height)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_first_block_height_after(
|
||||
&self,
|
||||
time: OffsetDateTime,
|
||||
) -> Result<Option<i64>, sqlx::Error> {
|
||||
trace!("get_first_block_height_after");
|
||||
let start = Instant::now();
|
||||
|
||||
let maybe_record = sqlx::query!(
|
||||
r#"
|
||||
SELECT height
|
||||
@@ -41,6 +69,7 @@ impl StorageManager {
|
||||
)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await?;
|
||||
log_db_operation_time("get_first_block_height_after", start);
|
||||
|
||||
if let Some(row) = maybe_record {
|
||||
Ok(row.height)
|
||||
@@ -53,6 +82,9 @@ impl StorageManager {
|
||||
&self,
|
||||
time: OffsetDateTime,
|
||||
) -> Result<Option<i64>, sqlx::Error> {
|
||||
trace!("get_last_block_height_before");
|
||||
let start = Instant::now();
|
||||
|
||||
let maybe_record = sqlx::query!(
|
||||
r#"
|
||||
SELECT height
|
||||
@@ -65,6 +97,7 @@ impl StorageManager {
|
||||
)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await?;
|
||||
log_db_operation_time("get_last_block_height_before", start);
|
||||
|
||||
if let Some(row) = maybe_record {
|
||||
Ok(row.height)
|
||||
@@ -79,6 +112,9 @@ impl StorageManager {
|
||||
start_height: i64,
|
||||
end_height: i64,
|
||||
) -> Result<i32, sqlx::Error> {
|
||||
trace!("get_signed_between");
|
||||
let start = Instant::now();
|
||||
|
||||
let count = sqlx::query!(
|
||||
r#"
|
||||
SELECT COUNT(*) as count FROM pre_commit
|
||||
@@ -94,6 +130,7 @@ impl StorageManager {
|
||||
.fetch_one(&self.connection_pool)
|
||||
.await?
|
||||
.count;
|
||||
log_db_operation_time("get_signed_between", start);
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
@@ -103,7 +140,10 @@ impl StorageManager {
|
||||
consensus_address: &str,
|
||||
height: i64,
|
||||
) -> Result<Option<CommitSignature>, sqlx::Error> {
|
||||
sqlx::query_as(
|
||||
trace!("get_precommit");
|
||||
let start = Instant::now();
|
||||
|
||||
let res = sqlx::query_as(
|
||||
r#"
|
||||
SELECT * FROM pre_commit
|
||||
WHERE validator_address = ?
|
||||
@@ -113,14 +153,20 @@ impl StorageManager {
|
||||
.bind(consensus_address)
|
||||
.bind(height)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
.await?;
|
||||
log_db_operation_time("get_precommit", start);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_block_validators(
|
||||
&self,
|
||||
height: i64,
|
||||
) -> Result<Vec<Validator>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
trace!("get_block_validators");
|
||||
let start = Instant::now();
|
||||
|
||||
let res = sqlx::query_as!(
|
||||
Validator,
|
||||
r#"
|
||||
SELECT * FROM validator
|
||||
@@ -133,16 +179,28 @@ impl StorageManager {
|
||||
height
|
||||
)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
.await?;
|
||||
log_db_operation_time("get_block_validators", start);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_validators(&self) -> Result<Vec<Validator>, sqlx::Error> {
|
||||
sqlx::query_as("SELECT * FROM validator")
|
||||
trace!("get_validators");
|
||||
let start = Instant::now();
|
||||
|
||||
let res = sqlx::query_as("SELECT * FROM validator")
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
.await?;
|
||||
log_db_operation_time("get_validators", start);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_last_processed_height(&self) -> Result<i64, sqlx::Error> {
|
||||
trace!("get_last_processed_height");
|
||||
let start = Instant::now();
|
||||
|
||||
let maybe_record = sqlx::query!(
|
||||
r#"
|
||||
SELECT last_processed_height FROM metadata
|
||||
@@ -150,6 +208,7 @@ impl StorageManager {
|
||||
)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await?;
|
||||
log_db_operation_time("get_last_processed_height", start);
|
||||
|
||||
if let Some(row) = maybe_record {
|
||||
Ok(row.last_processed_height)
|
||||
@@ -157,6 +216,27 @@ impl StorageManager {
|
||||
Ok(-1)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_pruned_height(&self) -> Result<i64, sqlx::Error> {
|
||||
trace!("get_pruned_height");
|
||||
let start = Instant::now();
|
||||
|
||||
let maybe_record = sqlx::query!(
|
||||
r#"
|
||||
SELECT last_pruned_height FROM pruning
|
||||
"#
|
||||
)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await?;
|
||||
|
||||
log_db_operation_time("get_pruned_height", start);
|
||||
|
||||
if let Some(row) = maybe_record {
|
||||
Ok(row.last_pruned_height)
|
||||
} else {
|
||||
Ok(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make those generic over executor so that they could be performed over connection pool and a tx
|
||||
@@ -170,7 +250,8 @@ pub(crate) async fn insert_validator<'a, E>(
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("insert validator");
|
||||
trace!("insert_validator");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -183,6 +264,7 @@ where
|
||||
)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("insert_validator", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -200,7 +282,8 @@ pub(crate) async fn insert_block<'a, E>(
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("insert block");
|
||||
trace!("insert_block");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -217,6 +300,7 @@ where
|
||||
)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("insert_block", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -233,7 +317,8 @@ pub(crate) async fn insert_precommit<'a, E>(
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("insert precommit");
|
||||
trace!("insert_precommit");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -249,6 +334,7 @@ where
|
||||
)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("insert_precommit", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -270,7 +356,8 @@ pub(crate) async fn insert_transaction<'a, E>(
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("insert transaction");
|
||||
trace!("insert_transaction");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -298,6 +385,7 @@ where
|
||||
)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("insert_transaction", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -313,7 +401,8 @@ pub(crate) async fn insert_message<'a, E>(
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("insert message");
|
||||
trace!("insert_message");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -330,6 +419,7 @@ where
|
||||
)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("insert_message", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -343,10 +433,100 @@ where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("update_last_processed");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!("UPDATE metadata SET last_processed_height = ?", height)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("update_last_processed", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(executor))]
|
||||
pub(crate) async fn update_last_pruned<'a, E>(height: i64, executor: E) -> Result<(), sqlx::Error>
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("update_last_pruned");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!("UPDATE pruning SET last_pruned_height = ?", height)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("update_last_pruned", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn prune_blocks<'a, E>(oldest_to_keep: i64, executor: E) -> Result<(), sqlx::Error>
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("prune_blocks");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!("DELETE FROM block WHERE height < ?", oldest_to_keep)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("prune_blocks", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn prune_pre_commits<'a, E>(
|
||||
oldest_to_keep: i64,
|
||||
executor: E,
|
||||
) -> Result<(), sqlx::Error>
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("prune_pre_commits");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!("DELETE FROM pre_commit WHERE height < ?", oldest_to_keep)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("prune_pre_commits", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn prune_transactions<'a, E>(
|
||||
oldest_to_keep: i64,
|
||||
executor: E,
|
||||
) -> Result<(), sqlx::Error>
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("prune_transactions");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM \"transaction\" WHERE height < ?",
|
||||
oldest_to_keep
|
||||
)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("prune_transactions", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn prune_messages<'a, E>(
|
||||
oldest_to_keep: i64,
|
||||
executor: E,
|
||||
) -> Result<(), sqlx::Error>
|
||||
where
|
||||
E: Executor<'a, Database = Sqlite>,
|
||||
{
|
||||
trace!("prune_messages");
|
||||
let start = Instant::now();
|
||||
|
||||
sqlx::query!("DELETE FROM message WHERE height < ?", oldest_to_keep)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
log_db_operation_time("prune_messages", start);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ use crate::block_processor::types::{FullBlockInformation, ParsedTransactionRespo
|
||||
use crate::error::ScraperError;
|
||||
use crate::storage::manager::{
|
||||
insert_block, insert_message, insert_precommit, insert_transaction, insert_validator,
|
||||
update_last_processed, StorageManager,
|
||||
prune_blocks, prune_messages, prune_pre_commits, prune_transactions, update_last_processed,
|
||||
update_last_pruned, StorageManager,
|
||||
};
|
||||
use crate::storage::models::{CommitSignature, Validator};
|
||||
use sqlx::types::time::OffsetDateTime;
|
||||
@@ -15,6 +16,7 @@ use std::path::Path;
|
||||
use tendermint::block::{Commit, CommitSig};
|
||||
use tendermint::Block;
|
||||
use tendermint_rpc::endpoint::validators;
|
||||
use tokio::time::Instant;
|
||||
use tracing::{debug, error, info, instrument, trace, warn};
|
||||
|
||||
mod helpers;
|
||||
@@ -28,6 +30,19 @@ pub struct ScraperStorage {
|
||||
pub(crate) manager: StorageManager,
|
||||
}
|
||||
|
||||
pub(crate) fn log_db_operation_time(op_name: &str, start_time: Instant) {
|
||||
let elapsed = start_time.elapsed();
|
||||
let formatted = humantime::format_duration(elapsed);
|
||||
|
||||
match elapsed.as_millis() {
|
||||
v if v > 10000 => error!("{op_name} took {formatted} to execute"),
|
||||
v if v > 1000 => warn!("{op_name} took {formatted} to execute"),
|
||||
v if v > 100 => info!("{op_name} took {formatted} to execute"),
|
||||
v if v > 10 => debug!("{op_name} took {formatted} to execute"),
|
||||
_ => trace!("{op_name} took {formatted} to execute"),
|
||||
}
|
||||
}
|
||||
|
||||
impl ScraperStorage {
|
||||
#[instrument]
|
||||
pub async fn init<P: AsRef<Path> + Debug>(database_path: P) -> Result<Self, ScraperError> {
|
||||
@@ -65,6 +80,32 @@ impl ScraperStorage {
|
||||
Ok(storage)
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub async fn prune_storage(
|
||||
&self,
|
||||
oldest_to_keep: u32,
|
||||
current_height: u32,
|
||||
) -> Result<(), ScraperError> {
|
||||
let start = Instant::now();
|
||||
|
||||
let mut tx = self.begin_processing_tx().await?;
|
||||
|
||||
prune_messages(oldest_to_keep.into(), &mut tx).await?;
|
||||
prune_transactions(oldest_to_keep.into(), &mut tx).await?;
|
||||
prune_pre_commits(oldest_to_keep.into(), &mut tx).await?;
|
||||
prune_blocks(oldest_to_keep.into(), &mut tx).await?;
|
||||
update_last_pruned(current_height.into(), &mut tx).await?;
|
||||
|
||||
let commit_start = Instant::now();
|
||||
tx.commit()
|
||||
.await
|
||||
.map_err(|source| ScraperError::StorageTxCommitFailure { source })?;
|
||||
log_db_operation_time("committing pruning tx", commit_start);
|
||||
|
||||
log_db_operation_time("pruning storage", start);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn begin_processing_tx(&self) -> Result<StorageTransaction, ScraperError> {
|
||||
debug!("starting storage tx");
|
||||
@@ -75,6 +116,10 @@ impl ScraperStorage {
|
||||
.map_err(|source| ScraperError::StorageTxBeginFailure { source })
|
||||
}
|
||||
|
||||
pub async fn lowest_block_height(&self) -> Result<Option<i64>, ScraperError> {
|
||||
Ok(self.manager.get_lowest_block().await?)
|
||||
}
|
||||
|
||||
pub async fn get_first_block_height_after(
|
||||
&self,
|
||||
time: OffsetDateTime,
|
||||
@@ -155,6 +200,10 @@ impl ScraperStorage {
|
||||
pub async fn get_last_processed_height(&self) -> Result<i64, ScraperError> {
|
||||
Ok(self.manager.get_last_processed_height().await?)
|
||||
}
|
||||
|
||||
pub async fn get_pruned_height(&self) -> Result<i64, ScraperError> {
|
||||
Ok(self.manager.get_pruned_height().await?)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn persist_block(
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -27,13 +27,15 @@
|
||||
- [Maintenance](nodes/maintenance.md)
|
||||
- [Manual Node Upgrade](nodes/manual-upgrade.md)
|
||||
- [Automatic Node Upgrade: Nymvisor Setup and Usage](nodes/nymvisor-upgrade.md)
|
||||
- [Performance Testing](testing/performance.md)
|
||||
- [Node Setup](testing/node-setup.md)
|
||||
- [Metrics Monitoring](testing/templates.md)
|
||||
- [Performance Monitoring & Testing](testing/performance.md)
|
||||
<!--- [Node Setup](testing/node-setup.md)-->
|
||||
- [Gateway Probe](testing/gateway-probe.md)
|
||||
- [Prometheus & Grafana](testing/prometheus-grafana.md)
|
||||
- [ExploreNYM scripts](testing/explorenym-scripts.md)
|
||||
<!-- - [Run in a Docker](testing/docker-monitor.md) -->
|
||||
|
||||
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
- [VPS Setup](troubleshooting/vps-isp.md)
|
||||
@@ -56,6 +58,7 @@
|
||||
|
||||
- [Exit Gateway](legal/exit-gateway.md)
|
||||
- [Community Counsel](legal/community-counsel.md)
|
||||
- [ISP List](legal/isp-list.md)
|
||||
- [Jurisdictions](legal/jurisdictions.md)
|
||||
- [Switzerland](legal/swiss.md)
|
||||
- [United States](legal/united-states.md)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
**ISP**,**Locations**,**Public IPv6**,**Crypto Payments**,**Comments**,**Last Updated**
|
||||
[Flokinet](https://flokinet.is),"Netherlands, Iceland, Romania,France","Yes, needs a ticket and custom setup","yes, including XMR","Very slow customer support","05/2024"
|
||||
[BitLaunch](https://bitlaunch.io),"Canada, USA, UK","No","Yes","Expensive. Digial Ocean through BitLanch has IPv6","05/2024"
|
||||
[Hostinger](https://hostinger.com),"France, Lithuania, India, USA, Brazil","Yes, out of the box","Yes","Crypto payments must be done per each server monthly or annually.","05/2024"
|
||||
[Linode](https://linode.com),"USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy","Yes out of the box","No, only through [BitLAunch](https://bitlaunch.io)","IPv6 sometimes need to be re-added in Networking tab, no reboot needed","05/2024"
|
||||
[Cherry Servers](https://www.cherryservers.com),"Lithuania, Netherlands, USA, Singapore","No","Yes","Issued IP doesn’t match the location offered by the provider.","05/2024"
|
||||
[Njalla](https://nja.la),"Sweden","Yes","Yes","Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market.","05/2024"
|
||||
[HostSailor](https://hostsailor.com),"USA","Yes, based on ticket","Yes","The IPv6 setup needs custom research and is not documented","05/2024"
|
||||
|
@@ -0,0 +1,25 @@
|
||||
# Where to host your `nym-node`?
|
||||
|
||||
```admonish info
|
||||
The entire content of this page is under [Creative Commons Attribution 4.0 International Public License](https://creativecommons.org/licenses/by/4.0/).
|
||||
```
|
||||
|
||||
Inspired by a valuable resource, done by Tor community - [*Good Bad ISPs*](https://community.torproject.org/relay/community-resources/good-bad-isps/), LunarDAO squad initiated a table customised for Nym Exit Gateways operators.
|
||||
|
||||
This ISP list is fully managed by Nym operator community and it serves as a space to share their experience of running Exit Gateways on various Internet Service Providers (ISPs). The ISPs greatly differ in regards to services they offer as well as to their openess of hosting exit routing software.
|
||||
|
||||
Please share any experiences running a node like policies, complains, legal issues and solutions, discrepancy between offers and reality (bandwidth, IP range, locations) or anything regarding pricing or customer support.
|
||||
|
||||
If you came across any legal findings, please share them in our [list of jurisdictions](jurisdictions.md).
|
||||
|
||||
While we trust that Nym node operators are honest, we would like to ask everyone to do your own research.
|
||||
|
||||
```admonish caution title=""
|
||||
To edit or add information to the ISP list, make changes to the csv file located [here](https://github.com/nymtech/nym/blob/develop/documentation/operators/src/data/isp-sheet.csv) and submit your edits as a pull request according to [this guide](add-content.md).
|
||||
```
|
||||
|
||||
```admonish note title=""
|
||||
As of now the list is quite short. When it grows, we can divide it according the localities of the listed ISPs.
|
||||
```
|
||||
|
||||
<!--cmdrun python3 ../../../scripts/csv2md.py ../data/isp-sheet.csv -s 0 -->
|
||||
@@ -10,9 +10,9 @@ A suboptimally configured VPS often results in a non-functional node. To follow
|
||||
|
||||
You will need to rent a VPS to run your node on. One key reason for this is that your node **must be able to send TCP data using both IPv4 and IPv6** (as other nodes you talk to may use either protocol).
|
||||
|
||||
Tor community created a very helpful table called [*Good Bad ISPs*](https://community.torproject.org/relay/community-resources/good-bad-isps/), use that one as a guideline for your choice of ISP for your VPS.
|
||||
Tor community created a very helpful table called [*Good Bad ISPs*](https://community.torproject.org/relay/community-resources/good-bad-isps/), you can use that one as a guideline for your choice of ISP for your VPS.
|
||||
|
||||
Currently we run [performance testing](../testing/performance.md) events to find out the best optimization. Sphinx packet decryption is CPU-bound, so more fast cores the better throughput.
|
||||
**Update:** Nym community started an ISP table called [*Where to host your nym node?*](../legal/isp-list.md), check it out and add your findings!
|
||||
|
||||
### `nym-node`
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# Nym Gateway Probe
|
||||
|
||||
Nym Node operators running Gateway functionality are already familiar with the monitoring tool [Harbourmaster.nymtech.net](https://harbourmaster.nymtech.net). Under the hood of Nym Harbourmaster runs iterations of `nym-gateway-probe` doing various checks and displaying the results on the interface. Operators don't have to rely on the probe ran by Nym and wait for the data to refresh. With `nym-gateway-probe` everyone can check any Gateway's networking status from their own computer at any time. In one command the client queries data from:
|
||||
|
||||
- [`nym-api`](https://validator.nymtech.net/api/)
|
||||
- [`explorer-api`](https://explorer.nymtech.net/api/)
|
||||
- [`harbour-master`](https://harbourmaster.nymtech.net/)
|
||||
|
||||
|
||||
## Preparation
|
||||
|
||||
We recommend to have install all [the prerequisites](../binaries/building-nym.md#prerequisites) needed to build `nym-node` from source including latest [Rust Toolchain](https://www.rust-lang.org/tools/install).
|
||||
|
||||
## Installation
|
||||
|
||||
`nym-gateway-probe` source code is in [`nym-vpn-client`](https://github.com/nymtech/nym-vpn-client) repository. The client needs to be build from source.
|
||||
|
||||
1. Clone the repository:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/nymtech/nym-vpn-client.git
|
||||
```
|
||||
|
||||
2. Build `nym-gateway-probe`:
|
||||
|
||||
```sh
|
||||
cd nym-vpn-client
|
||||
|
||||
cargo build --release -p nym-gateway-probe
|
||||
```
|
||||
|
||||
## Running the client
|
||||
|
||||
```sh
|
||||
./target/release/nym-gateway-probe --help
|
||||
```
|
||||
~~~admonish collapsible=true
|
||||
```
|
||||
Usage: nym-gateway-probe [OPTIONS]
|
||||
|
||||
Options:
|
||||
-c, --config-env-file <CONFIG_ENV_FILE> Path pointing to an env file describing the network
|
||||
-g, --gateway <GATEWAY>
|
||||
-n, --no-log
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
```
|
||||
~~~
|
||||
|
||||
To run the client, simply add a flag `--gateway` with a targeted gateway identity key.
|
||||
|
||||
```sh
|
||||
./target/release/nym-gateway-probe --gateway <GATEWAY_IDENTITY_KEY>
|
||||
```
|
||||
|
||||
For any `nym-node --mode exit-gateway` the aim is to have this outcome:
|
||||
```sh
|
||||
{
|
||||
"gateway": "<GATEWAY_IDENTITY_KEY>",
|
||||
"outcome": {
|
||||
"as_entry": {
|
||||
"can_connect": true,
|
||||
"can_route": true
|
||||
},
|
||||
"as_exit": {
|
||||
"can_connect": true,
|
||||
"can_route_ip_v4": true,
|
||||
"can_route_ip_external_v4": true,
|
||||
"can_route_ip_v6": true,
|
||||
"can_route_ip_external_v6": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you don't provide a `--gateway` flag it will pick a random one to test.
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# Node Setup for Performance Testing Event
|
||||
|
||||
```admonish info
|
||||
For the moment we paused Fast and Furious `perf` environment. Nym Mainnet environment will be used for future tests, please wait for further instructions.
|
||||
```
|
||||
|
||||
To join the [Performance testing event]({{performance_testing_webpage}}) node operators need to do proceed with the following tasks:
|
||||
|
||||
1. **[Sign their node]({{performance_testing_webpage}}) into the testing environment**
|
||||
2. **[Configure their node](#node-configuration) for the test**
|
||||
3. (*Not mandatory*) [Setup metric monitoring system](templates.md) to observe node performance at any time
|
||||
3. (*Not mandatory*) [Setup metric monitoring system](performance.md#monitoring) to observe node performance at any time
|
||||
|
||||
## Node Configuration
|
||||
|
||||
|
||||
@@ -1,8 +1,34 @@
|
||||
# Performance Testing
|
||||
# Performance Monitoring & Testing
|
||||
|
||||
> To configure your node for a testing event, visit [node setup page](node-setup.md).
|
||||
Nym Mixnet has been running on mainnet for quite some time. There is still work to be done in order for the network to meet its full potential - mass adoption of privacy through fully distributed Mixnet.
|
||||
|
||||
Nym Mixnet has been running on mainnet for quite some time. There is still work to be done in order for the network to meet its full potential - mass adoption of privacy through fully distributed Mixnet.
|
||||
As developers we need to be constantly improving the software. Operators have as much important role, keep their nodes up to date, monitor their performance and share their feedback with the rest of the community and core developers.
|
||||
|
||||
Therefore [monitoring](#monitoring) and [testing](#testing) are essential pieces of our common work. We call out all Nym operators to join the efforts!
|
||||
|
||||
## Monitoring
|
||||
|
||||
There are multiple ways to monitor performance of nodes and the machines on which they run. For the purpose of maximal privacy and decentralisation of the data - preventing Nym Mixnet from any global adversary takeover - we created these pages as a source of mutual empowerment, a place where operators can share and learn new skills to **setup metrics monitors on their own infrastructure**.
|
||||
|
||||
### Guides to Setup Own Metrics
|
||||
|
||||
A list of different scripts, templates and guides for easier navigation:
|
||||
|
||||
* [`nym-gateway-probe`](gateway-probe.md) - a useful tool used under the hood of [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net)
|
||||
* [Prometheus and Grafana](prometheus-grafana.md) self-hosted setup
|
||||
* [Nym-node CPU cron service](https://gist.github.com/tommyv1987/97e939a7adf491333d686a8eaa68d4bd) - an easy bash script by Nym core developer [@tommy1987](https://gist.github.com/tommyv1987), designed to monitor a CPU usage of your node, running locally
|
||||
* Nym's script [`prom_targets.py`](https://github.com/nymtech/nym/blob/develop/scripts/prom_targets.py) - a useful python program to request data from API and can be run on its own or plugged to more sophisticated flows
|
||||
|
||||
### Collecting Testing Metrics
|
||||
|
||||
For the purpose of the performance testing Nym core developers plan to run instances of Prometheus and Grafana connected to Node explorer in the house. The network overall key insights we seek from these tests are primarily internal. We're focused on pinpointing bottlenecks, capacity loads, and monitoring cpu usage on the nodes' machines.
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
```admonish info
|
||||
For the moment we paused Fast and Furious `perf` environment. Nym Mainnet environment will be used for future tests, please wait for further instructions.
|
||||
```
|
||||
|
||||
Nym asks its decentralised community of operators to join a series of performance testing events in order to **increase the overall quality of the Mixnet**. The main takeaways of such event are:
|
||||
|
||||
@@ -21,7 +47,7 @@ Visit [Fast and Furious web page]({{performance_testing_webpage}}) and [Nym Harb
|
||||
|
||||
* Nym runs a paralel network environment [validator.performance.nymte.ch]({{performance_validator}}) with a chain ID `perf`
|
||||
* Operators of Nym Nodes join by following easy steps on [performance testing web page]({{performance_testing_webpage}}), including simplified node authentication signature (while keep running their nodes on the mainnet)
|
||||
* Once signed in, operators will be asked to swap their binary for the modified version with metrics endpoint to be able to connect their own [monitoring system](templates.md)
|
||||
* Once signed in, operators will be asked to swap their binary for the modified version with metrics endpoint to be able to connect their own [monitoring system](#monitoring)
|
||||
* Core node data will be fed to a unique mixnet contract for the `perf` side chain
|
||||
* Nym starts a new API and start packet transition in high load through these nodes in both settings
|
||||
* Nym tracks packet flow using Prometheus and Grafana
|
||||
@@ -31,4 +57,5 @@ Visit [Fast and Furious web page]({{performance_testing_webpage}}) and [Nym Harb
|
||||
## More Information
|
||||
|
||||
* What happens after the test or what operators get for participating is shared up to date on the [performance testing web page]({{performance_testing_webpage}})
|
||||
* Visit our guides to [setup metrics template](templates.md) and learn how to operate them in self-custodial way
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ Begin with the steps listed in [*Connectivity Test and Configuration*](../nodes/
|
||||
2. Checkout your VPS dashboard and make sure your IPv6-public enabled.
|
||||
3. If you are able to add IPv6 address `/64` range, do it.
|
||||
|
||||
**Update:** Nym community started an ISP table called [*Where to host your nym node?*](../legal/isp-list.md), check it out and add your findings!
|
||||
|
||||

|
||||
|
||||
4. Search or ask your ISP for additional documentation related to IPv6 routing and ask them to provide you with `IPv6 IP address` and `IPv6 IP gateway address`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user