Compare commits

..

18 Commits

Author SHA1 Message Date
Jędrzej Stuczyński d5c598729b updated credential client 2024-04-09 17:03:59 +01:00
Mark Sinclair 7eb764cd5a Fix typing 2024-04-09 16:47:53 +01:00
Mark Sinclair 854128ee21 Fix up args to pass isSandbox 2024-04-09 16:47:53 +01:00
Mark Sinclair 6cd59124a2 Get ready for publishing Typescript SDK with credentials client 2024-04-09 16:47:53 +01:00
Gala d5cc31b7f0 remove commented code 2024-04-09 16:47:53 +01:00
Gala ea6f009c01 wip credential generation example 2024-04-09 16:47:52 +01:00
Gala 5881f6b6aa wip credentials sdk example 2024-04-09 16:47:52 +01:00
Gala aafddc78d1 sdk cononut compilation an wip coco example 2024-04-09 16:47:49 +01:00
Gala 29a0e8620c fixing typo 2024-04-09 16:47:41 +01:00
Gala 800016f682 Nym credentials sdk
Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2024-04-09 16:47:38 +01:00
Jędrzej Stuczyński 6151f15e7e missing panic hook feature 2024-04-09 16:46:53 +01:00
Jędrzej Stuczyński 5e6a945eef apply changes from 34c00a150140a55a1fc1c9b9f6118584f8bbfc0f 2024-04-09 16:46:53 +01:00
Jędrzej Stuczyński aec23a4f83 fixing panic hook weirdness 2024-04-09 16:46:52 +01:00
Jędrzej Stuczyński 3d563ece79 fixed browser storage extension build 2024-04-09 16:46:52 +01:00
Jędrzej Stuczyński f24a0dc8ae removed dead code 2024-04-09 16:46:52 +01:00
Jędrzej Stuczyński 50fbdb4f0b implemented acquireCredential method in the wasm credential client 2024-04-09 16:46:49 +01:00
Gala d445a26999 wip internal-dev 2024-04-09 16:41:59 +01:00
Mark Sinclair 30294062af WASM client for credentials - wip 2024-04-09 16:41:55 +01:00
438 changed files with 19090 additions and 62400 deletions
+3 -18
View File
@@ -9,7 +9,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install Dependencies (Linux) - 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
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
@@ -30,24 +30,9 @@ jobs:
- name: Remove existing Nym config directory (`~/.nym/`) - name: Remove existing Nym config directory (`~/.nym/`)
run: cd documentation && ./remove_existing_config.sh run: cd documentation && ./remove_existing_config.sh
continue-on-error: false continue-on-error: false
# This is the original flow - name: Build all projects in documentation/ & move to ~/dist/docs/
# - name: Build all projects in documentation/ & move to ~/dist/docs/
# run: cd documentation && ./build_all_to_dist.sh
# This is a workaround replacement which builds on the last working commit b332a6b55668f60988e36961f3f62a794ba82ddb and then on current branch
- name: Save current branch to ~/current_branch
run: git rev-parse --abbrev-ref HEAD > ~/current_branch
- name: Git pull & switch to b332a6b55668f60988e36961f3f62a794ba82ddb
run: git pull && git checkout b332a6b55668f60988e36961f3f62a794ba82ddb
- name: Build all projects in documentation/ & move to ~/dist/docs/ from b332a6b55668f60988e36961f3f62a794ba82ddb
run: cd documentation && ./build_all_to_dist.sh run: cd documentation && ./build_all_to_dist.sh
continue-on-error: false
- name: Switch to current branch
run: git checkout $echo "$(cat ~/current_branch)"
- name: Build all projects in documentation/ & move to ~/dist/docs/ on current branch
run: cd documentation && ./build_all_to_dist.sh && rm ~/current_branch
# End of replacemet
- name: Post process - name: Post process
run: cd documentation && ./post_process.sh run: cd documentation && ./post_process.sh
+3 -19
View File
@@ -13,7 +13,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install Dependencies (Linux) - 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
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
@@ -34,25 +34,9 @@ jobs:
- name: Remove existing Nym config directory (`~/.nym/`) - name: Remove existing Nym config directory (`~/.nym/`)
run: cd documentation && ./remove_existing_config.sh run: cd documentation && ./remove_existing_config.sh
continue-on-error: false continue-on-error: false
- name: Build all projects in documentation/ & move to ~/dist/docs/
# This is the original flow
# - name: Build all projects in documentation/ & move to ~/dist/docs/
# run: cd documentation && ./build_all_to_dist.sh
# This is a workaround replacement which builds on the last working commit b332a6b55668f60988e36961f3f62a794ba82ddb and then on current branch
- name: Save current branch to ~/current_branch
run: git rev-parse --abbrev-ref HEAD > ~/current_branch
- name: Git pull & switch to b332a6b55668f60988e36961f3f62a794ba82ddb
run: git pull && git checkout b332a6b55668f60988e36961f3f62a794ba82ddb
- name: Build all projects in documentation/ & move to ~/dist/docs/ from b332a6b55668f60988e36961f3f62a794ba82ddb
run: cd documentation && ./build_all_to_dist.sh run: cd documentation && ./build_all_to_dist.sh
continue-on-error: false
- name: Switch to current branch
run: git checkout $echo "$(cat ~/current_branch)"
- name: Build all projects in documentation/ & move to ~/dist/docs/ on current branch
run: cd documentation && ./build_all_to_dist.sh && rm ~/current_branch
# End of replacemet
- name: Deploy branch to CI www - name: Deploy branch to CI www
continue-on-error: true continue-on-error: true
@@ -30,7 +30,6 @@ jobs:
mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }} mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }}
gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }} gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }}
nymvisor_hash: ${{ steps.binary-hashes.outputs.nymvisor_hash }} nymvisor_hash: ${{ steps.binary-hashes.outputs.nymvisor_hash }}
nymnode_hash: ${{ steps.binary-hashes.outputs.nymnode_hash }}
socks5_hash: ${{ steps.binary-hashes.outputs.socks5_hash }} socks5_hash: ${{ steps.binary-hashes.outputs.socks5_hash }}
netreq_hash: ${{ steps.binary-hashes.outputs.netreq_hash }} netreq_hash: ${{ steps.binary-hashes.outputs.netreq_hash }}
cli_hash: ${{ steps.binary-hashes.outputs.cli_hash }} cli_hash: ${{ steps.binary-hashes.outputs.cli_hash }}
@@ -39,7 +38,6 @@ jobs:
mixnode_version: ${{ steps.binary-versions.outputs.mixnode_version }} mixnode_version: ${{ steps.binary-versions.outputs.mixnode_version }}
gateway_version: ${{ steps.binary-versions.outputs.gateway_version }} gateway_version: ${{ steps.binary-versions.outputs.gateway_version }}
nymvisor_version: ${{ steps.binary-versions.outputs.nymvisor_version }} nymvisor_version: ${{ steps.binary-versions.outputs.nymvisor_version }}
nymnode_version: ${{ steps.binary-versions.outputs.nymnode_version }}
socks5_version: ${{ steps.binary-versions.outputs.socks5_version }} socks5_version: ${{ steps.binary-versions.outputs.socks5_version }}
netreq_version: ${{ steps.binary-versions.outputs.netreq_version }} netreq_version: ${{ steps.binary-versions.outputs.netreq_version }}
cli_version: ${{ steps.binary-versions.outputs.cli_version }} cli_version: ${{ steps.binary-versions.outputs.cli_version }}
@@ -83,7 +81,6 @@ jobs:
target/release/nym-network-statistics target/release/nym-network-statistics
target/release/nym-cli target/release/nym-cli
target/release/nymvisor target/release/nymvisor
target/release/nym-node
retention-days: 30 retention-days: 30
- id: create-release - id: create-release
@@ -102,7 +99,6 @@ jobs:
target/release/nym-network-statistics target/release/nym-network-statistics
target/release/nym-cli target/release/nym-cli
target/release/nymvisor target/release/nymvisor
target/release/nym-node
push-release-data-client: push-release-data-client:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
@@ -102,18 +102,6 @@ jobs:
nym-wallet/target/release/bundle/dmg/*.dmg nym-wallet/target/release/bundle/dmg/*.dmg
nym-wallet/target/release/bundle/macos/*.app.tar.gz* nym-wallet/target/release/bundle/macos/*.app.tar.gz*
- name: Deploy artifacts to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-avzr"
SOURCE: "nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
EXCLUDE: "/dist/, /node_modules/"
push-release-data: push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml uses: ./.github/workflows/release-calculate-hash.yml
@@ -77,18 +77,6 @@ jobs:
nym-wallet/target/release/bundle/appimage/*.AppImage nym-wallet/target/release/bundle/appimage/*.AppImage
nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz* nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz*
- name: Deploy artifacts to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-avzr"
SOURCE: "nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
EXCLUDE: "/dist/, /node_modules/"
push-release-data: push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml uses: ./.github/workflows/release-calculate-hash.yml
@@ -97,18 +97,6 @@ jobs:
nym-wallet/target/release/bundle/msi/*.msi nym-wallet/target/release/bundle/msi/*.msi
nym-wallet/target/release/bundle/msi/*.msi.zip* nym-wallet/target/release/bundle/msi/*.msi.zip*
- name: Deploy artifacts to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-avzr"
SOURCE: "nym-wallet/target/release/bundle/msi/nym-wallet_1.*.msi"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
EXCLUDE: "/dist/, /node_modules/"
push-release-data: push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml uses: ./.github/workflows/release-calculate-hash.yml
-24
View File
@@ -4,30 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [Unreleased]
## [2024.4-nutella] (2024-05-08)
- [fix] apply disable_poisson_rate from internal NR/IPR cfgs ([#4579])
- updating sign commands to include nym-node ([#4578])
- changed nym-node redirects from 308 'Permanent Redirect' to 303: 'See Other' ([#4572])
[#4579]: https://github.com/nymtech/nym/pull/4579
[#4578]: https://github.com/nymtech/nym/pull/4578
[#4572]: https://github.com/nymtech/nym/pull/4572
## [2024.3-eclipse] (2024-04-22)
- Initial release of the first iteration of the Nym Node
- Improvements to gateway functionality
- IPR development
- Removal of allow list in favour of implementing an exit policy
- Explorer delegation: enables direct delegation to nodes via the Nym Explorer
## [2024.2-fast-and-furious] (2024-03-25)
- Internal testing pre-release
## [2024.1-marabou] (2024-02-15) ## [2024.1-marabou] (2024-02-15)
**New Features:** **New Features:**
Generated
+3178 -1312
View File
File diff suppressed because it is too large Load Diff
+12 -7
View File
@@ -122,6 +122,7 @@ members = [
# "wasm/full-nym-wasm", # "wasm/full-nym-wasm",
"wasm/mix-fetch", "wasm/mix-fetch",
"wasm/node-tester", "wasm/node-tester",
"wasm/credentials"
] ]
default-members = [ default-members = [
@@ -160,8 +161,7 @@ license = "Apache-2.0"
[workspace.dependencies] [workspace.dependencies]
anyhow = "1.0.71" anyhow = "1.0.71"
async-trait = "0.1.68" async-trait = "0.1.68"
axum = "0.7.5" axum = "0.6.20"
axum-extra = "0.9.3"
base64 = "0.21.4" base64 = "0.21.4"
bs58 = "0.5.0" bs58 = "0.5.0"
bip39 = { version = "2.0.0", features = ["zeroize"] } bip39 = { version = "2.0.0", features = ["zeroize"] }
@@ -172,16 +172,15 @@ dotenvy = "0.15.6"
futures = "0.3.28" futures = "0.3.28"
generic-array = "0.14.7" generic-array = "0.14.7"
getrandom = "0.2.10" getrandom = "0.2.10"
headers = "0.4.0"
humantime-serde = "1.1.1" humantime-serde = "1.1.1"
hyper = "1.3.1" hyper = "0.14.27"
k256 = "0.13" k256 = "0.13"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4" log = "0.4"
once_cell = "1.7.2" once_cell = "1.7.2"
parking_lot = "0.12.1" parking_lot = "0.12.1"
rand = "0.8.5" rand = "0.8.5"
reqwest = { version = "0.12.4", default-features = false } reqwest = { version = "0.11.22", default-features = false }
schemars = "0.8.1" schemars = "0.8.1"
serde = "1.0.152" serde = "1.0.152"
serde_json = "1.0.91" serde_json = "1.0.91"
@@ -195,8 +194,8 @@ tokio-tungstenite = { version = "0.20.1" }
tracing = "0.1.37" tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false } tungstenite = { version = "0.20.1", default-features = false }
ts-rs = "7.0.0" ts-rs = "7.0.0"
utoipa = "4.2.0" utoipa = "3.5.0"
utoipa-swagger-ui = "6.0.0" utoipa-swagger-ui = "3.1.5"
url = "2.4" url = "2.4"
zeroize = "1.6.0" zeroize = "1.6.0"
@@ -237,6 +236,12 @@ tendermint-rpc = "0.34" # same version as used by cosmrs
prost = "0.12" prost = "0.12"
# wasm-related dependencies # wasm-related dependencies
# The `console_error_panic_hook` crate provides better debugging of panics by
# 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 = "0.1.7"
gloo-utils = "0.1.7" gloo-utils = "0.1.7"
js-sys = "0.3.63" js-sys = "0.3.63"
serde-wasm-bindgen = "0.5.0" serde-wasm-bindgen = "0.5.0"
+1
View File
@@ -103,6 +103,7 @@ sdk-wasm: sdk-wasm-build sdk-wasm-test sdk-wasm-lint
sdk-wasm-build: sdk-wasm-build:
$(MAKE) -C nym-browser-extension/storage wasm-pack $(MAKE) -C nym-browser-extension/storage wasm-pack
$(MAKE) -C wasm/client $(MAKE) -C wasm/client
$(MAKE) -C wasm/credentials
$(MAKE) -C wasm/node-tester $(MAKE) -C wasm/node-tester
$(MAKE) -C wasm/mix-fetch $(MAKE) -C wasm/mix-fetch
#$(MAKE) -C wasm/full-nym-wasm #$(MAKE) -C wasm/full-nym-wasm
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.34" version = "1.1.33"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client" description = "Implementation of the Nym Client"
edition = "2021" edition = "2021"
@@ -8,7 +8,5 @@ use nym_client_core::cli_helpers::client_import_credential::{
}; };
pub(crate) async fn execute(args: CommonClientImportCredentialArgs) -> Result<(), ClientError> { pub(crate) async fn execute(args: CommonClientImportCredentialArgs) -> Result<(), ClientError> {
import_credential::<CliNativeClient, _>(args).await?; import_credential::<CliNativeClient, _>(args).await
println!("successfully imported credential!");
Ok(())
} }
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.34" version = "1.1.33"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address" description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021" edition = "2021"
@@ -10,7 +10,5 @@ use nym_client_core::cli_helpers::client_import_credential::{
pub(crate) async fn execute( pub(crate) async fn execute(
args: CommonClientImportCredentialArgs, args: CommonClientImportCredentialArgs,
) -> Result<(), Socks5ClientError> { ) -> Result<(), Socks5ClientError> {
import_credential::<CliSocks5Client, _>(args).await?; import_credential::<CliSocks5Client, _>(args).await
println!("successfully imported credential!");
Ok(())
} }
@@ -17,13 +17,17 @@ use zeroize::Zeroizing;
pub mod state; pub mod state;
pub async fn deposit<C>(client: &C, amount: Coin) -> Result<State, BandwidthControllerError> pub async fn deposit<C>(
client: &C,
amount: impl Into<Coin>,
) -> Result<State, BandwidthControllerError>
where where
C: CoconutBandwidthSigningClient + Sync, C: CoconutBandwidthSigningClient + Sync,
{ {
let mut rng = OsRng; let mut rng = OsRng;
let signing_key = identity::PrivateKey::new(&mut rng); let signing_key = identity::PrivateKey::new(&mut rng);
let encryption_key = encryption::PrivateKey::new(&mut rng); let encryption_key = encryption::PrivateKey::new(&mut rng);
let amount = amount.into();
let tx_hash = client let tx_hash = client
.deposit( .deposit(
-1
View File
@@ -18,7 +18,6 @@ pub mod acquire;
pub mod error; pub mod error;
mod utils; mod utils;
#[derive(Debug)]
pub struct BandwidthController<C, St> { pub struct BandwidthController<C, St> {
storage: St, storage: St,
client: C, client: C,
+5 -13
View File
@@ -3,7 +3,7 @@ name = "nym-client-core"
version = "1.1.15" version = "1.1.15"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021" edition = "2021"
rust-version = "1.70" rust-version = "1.66"
license.workspace = true license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -25,6 +25,7 @@ si-scale = "0.2.2"
tap = "1.0.1" tap = "1.0.1"
thiserror = { workspace = true } thiserror = { workspace = true }
url = { workspace = true, features = ["serde"] } url = { workspace = true, features = ["serde"] }
tungstenite = { workspace = true, default-features = false }
tokio = { workspace = true, features = ["macros"] } tokio = { workspace = true, features = ["macros"] }
time = { workspace = true } time = { workspace = true }
zeroize = { workspace = true } zeroize = { workspace = true }
@@ -47,7 +48,7 @@ nym-validator-client = { path = "../client-libs/validator-client", default-featu
nym-task = { path = "../task" } nym-task = { path = "../task" }
nym-credential-storage = { path = "../credential-storage" } nym-credential-storage = { path = "../credential-storage" }
nym-network-defaults = { path = "../network-defaults" } 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-surb-storage = { path = "./surb-storage" }
nym-client-core-gateways-storage = { path = "./gateways-storage" } nym-client-core-gateways-storage = { path = "./gateways-storage" }
@@ -73,17 +74,8 @@ workspace = true
features = ["time"] features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
workspace = true version = "0.20.1"
features = ["rustls-tls-webpki-roots"] features = ["rustls-tls-native-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] [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
workspace = true workspace = true
@@ -8,12 +8,3 @@ use thiserror::Error;
pub struct ConfigUpgradeFailure { pub struct ConfigUpgradeFailure {
pub current_version: String, pub current_version: String,
} }
#[derive(Error, Debug)]
pub enum InvalidTrafficModeFailure {
#[error("attempted to set medium toggle traffic mode with fast mode flag")]
MediumToggleWithFastMode,
#[error("attempted to set medium toggle traffic mode with no cover flag")]
MediumToggleWithNoCover,
}
@@ -56,7 +56,6 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
// 24 hours // 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60); const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
use crate::error::InvalidTrafficModeFailure;
pub use nym_country_group::CountryGroup; pub use nym_country_group::CountryGroup;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
@@ -128,56 +127,6 @@ impl Config {
self self
} }
// TODO: this should be refactored properly
// as of 12.09.23 the below is true (not sure how this comment will rot in the future)
// medium_toggle:
// - sets secondary packet size to 16kb
// - disables poisson distribution of the main traffic stream
// - sets the cover traffic stream to 1 packet / 5s (on average)
// - disables per hop delay
//
// fastmode (to be renamed to `fast-poisson`):
// - sets average per hop delay to 10ms
// - sets the cover traffic stream to 1 packet / 2000s (on average); for all intents and purposes it disables the stream
// - sets the poisson distribution of the main traffic stream to 4ms, i.e. 250 packets / s on average
//
// no_cover:
// - disables poisson distribution of the main traffic stream
// - disables the secondary cover traffic stream
#[doc(hidden)]
pub fn try_apply_traffic_modes(
&mut self,
disable_poisson_process: bool,
medium_toggle: bool,
fast_mode: bool,
no_cover: bool,
) -> Result<(), InvalidTrafficModeFailure> {
if disable_poisson_process {
self.set_no_poisson_process()
}
if medium_toggle {
if fast_mode {
return Err(InvalidTrafficModeFailure::MediumToggleWithFastMode);
}
if no_cover {
return Err(InvalidTrafficModeFailure::MediumToggleWithNoCover);
}
self.set_experimental_medium_toggle();
}
if fast_mode {
self.set_high_default_traffic_volume()
}
if no_cover {
self.set_no_cover_traffic();
}
Ok(())
}
pub fn set_high_default_traffic_volume(&mut self) { pub fn set_high_default_traffic_volume(&mut self) {
self.debug.traffic.average_packet_delay = Duration::from_millis(10); self.debug.traffic.average_packet_delay = Duration::from_millis(10);
// basically don't really send cover messages // basically don't really send cover messages
@@ -187,15 +136,6 @@ impl Config {
self.debug.traffic.message_sending_average_delay = Duration::from_millis(4); self.debug.traffic.message_sending_average_delay = Duration::from_millis(4);
} }
/// Enable medium mixnet traffic, for experiments only.
/// This includes things like disabling cover traffic, no per hop delays, etc.
#[doc(hidden)]
pub fn set_experimental_medium_toggle(&mut self) {
self.set_no_cover_traffic_with_keepalive();
self.set_no_per_hop_delays();
self.debug.traffic.secondary_packet_size = Some(PacketSize::ExtendedPacket16);
}
pub fn with_disabled_poisson_process(mut self, disabled: bool) -> Self { pub fn with_disabled_poisson_process(mut self, disabled: bool) -> Self {
if disabled { if disabled {
self.set_no_poisson_process() self.set_no_poisson_process()
@@ -39,7 +39,7 @@ use log::{debug, error, info, warn};
use nym_bandwidth_controller::BandwidthController; use nym_bandwidth_controller::BandwidthController;
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore}; use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::encryption;
use nym_gateway_client::{ use nym_gateway_client::{
AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter, AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter,
}; };
@@ -670,7 +670,6 @@ where
let self_address = Self::mix_address(&init_res); let self_address = Self::mix_address(&init_res);
let ack_key = init_res.client_keys.ack_key(); let ack_key = init_res.client_keys.ack_key();
let encryption_keys = init_res.client_keys.encryption_keypair(); 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, // the components are started in very specific order. Unless you know what you are doing,
// do not change that. // do not change that.
@@ -793,7 +792,6 @@ where
Ok(BaseClient { Ok(BaseClient {
address: self_address, address: self_address,
identity_keys,
client_input: ClientInputStatus::AwaitingProducer { client_input: ClientInputStatus::AwaitingProducer {
client_input: ClientInput { client_input: ClientInput {
connection_command_sender: client_connection_tx, connection_command_sender: client_connection_tx,
@@ -818,7 +816,6 @@ where
pub struct BaseClient { pub struct BaseClient {
pub address: Recipient, pub address: Recipient,
pub identity_keys: Arc<identity::KeyPair>,
pub client_input: ClientInputStatus, pub client_input: ClientInputStatus,
pub client_output: ClientOutputStatus, pub client_output: ClientOutputStatus,
pub client_state: ClientState, pub client_state: ClientState,
+3 -3
View File
@@ -201,7 +201,7 @@ where
log::debug!("Setting up gateway"); log::debug!("Setting up gateway");
match setup { match setup {
GatewaySetup::MustLoad { gateway_id } => { GatewaySetup::MustLoad { gateway_id } => {
log::debug!("GatewaySetup::MustLoad with id: {gateway_id:?}"); log::trace!("GatewaySetup::MustLoad with id: {gateway_id:?}");
use_loaded_gateway_details(key_store, details_store, gateway_id).await use_loaded_gateway_details(key_store, details_store, gateway_id).await
} }
GatewaySetup::New { GatewaySetup::New {
@@ -209,7 +209,7 @@ where
available_gateways, available_gateways,
wg_tun_address, wg_tun_address,
} => { } => {
log::debug!("GatewaySetup::New with spec: {specification:?}"); log::trace!("GatewaySetup::New with spec: {specification:?}");
setup_new_gateway( setup_new_gateway(
key_store, key_store,
details_store, details_store,
@@ -224,7 +224,7 @@ where
gateway_details, gateway_details,
client_keys: managed_keys, client_keys: managed_keys,
} => { } => {
log::debug!("GatewaySetup::ReuseConnection"); log::trace!("GatewaySetup::ReuseConnection");
Ok(reuse_gateway_connection( Ok(reuse_gateway_connection(
authenticated_ephemeral_client, authenticated_ephemeral_client,
*gateway_details, *gateway_details,
+4 -3
View File
@@ -16,8 +16,6 @@ thiserror = { workspace = true }
url = { workspace = true } url = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand = { version = "0.7.3", features = ["wasm-bindgen"] }
tokio = { version = "1.24.1", features = ["macros"] } tokio = { version = "1.24.1", features = ["macros"] }
si-scale = "0.2.2"
time.workspace = true
# internal # internal
nym-bandwidth-controller = { path = "../../bandwidth-controller" } nym-bandwidth-controller = { path = "../../bandwidth-controller" }
@@ -48,7 +46,10 @@ features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
workspace = true workspace = true
features = ["rustls-tls-webpki-roots"] # 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"]
# wasm-only dependencies # wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen] [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
@@ -79,7 +79,6 @@ impl GatewayConfig {
} }
// TODO: this should be refactored into a state machine that keeps track of its authentication state // TODO: this should be refactored into a state machine that keeps track of its authentication state
#[derive(Debug)]
pub struct GatewayClient<C, St = EphemeralCredentialStorage> { pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
authenticated: bool, authenticated: bool,
disabled_credentials_mode: bool, disabled_credentials_mode: bool,
@@ -442,7 +441,7 @@ impl<C, St> GatewayClient<C, St> {
} }
debug_assert!(self.connection.is_available()); debug_assert!(self.connection.is_available());
log::debug!("Registering gateway"); log::trace!("Registering gateway");
// it's fine to instantiate it here as it's only used once (during authentication or registration) // it's fine to instantiate it here as it's only used once (during authentication or registration)
// and putting it into the GatewayClient struct would be a hassle // and putting it into the GatewayClient struct would be a hassle
@@ -494,7 +493,6 @@ impl<C, St> GatewayClient<C, St> {
if !self.connection.is_established() { if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished); return Err(GatewayClientError::ConnectionNotEstablished);
} }
log::debug!("Authenticating with gateway");
// it's fine to instantiate it here as it's only used once (during authentication or registration) // it's fine to instantiate it here as it's only used once (during authentication or registration)
// and putting it into the GatewayClient struct would be a hassle // and putting it into the GatewayClient struct would be a hassle
@@ -530,7 +528,6 @@ impl<C, St> GatewayClient<C, St> {
self.authenticated = status; self.authenticated = status;
self.bandwidth_remaining = bandwidth_remaining; self.bandwidth_remaining = bandwidth_remaining;
self.negotiated_protocol = protocol_version; self.negotiated_protocol = protocol_version;
log::debug!("authenticated: {status}, bandwidth remaining: {bandwidth_remaining}");
Ok(()) Ok(())
} }
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)), ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
@@ -543,11 +540,10 @@ impl<C, St> GatewayClient<C, St> {
&mut self, &mut self,
) -> Result<Arc<SharedKeys>, GatewayClientError> { ) -> Result<Arc<SharedKeys>, GatewayClientError> {
if self.authenticated { if self.authenticated {
debug!("Already authenticated");
return if let Some(shared_key) = &self.shared_key { return if let Some(shared_key) = &self.shared_key {
Ok(Arc::clone(shared_key)) Ok(Arc::clone(shared_key))
} else { } else {
Err(GatewayClientError::AuthenticationFailureWithPreexistingSharedKey) Err(GatewayClientError::AuthenticationFailure)
}; };
} }
@@ -853,7 +849,6 @@ impl<C, St> GatewayClient<C, St> {
// type alias for an ease of use // type alias for an ease of use
pub type InitGatewayClient = GatewayClient<InitOnly>; pub type InitGatewayClient = GatewayClient<InitOnly>;
#[derive(Debug)]
pub struct InitOnly; pub struct InitOnly;
impl GatewayClient<InitOnly, EphemeralCredentialStorage> { impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
@@ -71,9 +71,6 @@ pub enum GatewayClientError {
#[error("Authentication failure")] #[error("Authentication failure")]
AuthenticationFailure, AuthenticationFailure,
#[error("Authentication failure with preexisting shared key")]
AuthenticationFailureWithPreexistingSharedKey,
#[error("Timed out")] #[error("Timed out")]
Timeout, Timeout,
@@ -10,14 +10,9 @@ use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use log::*; use log::*;
use nym_gateway_requests::registration::handshake::SharedKeys; use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_gateway_requests::ServerResponse;
use nym_task::TaskClient; use nym_task::TaskClient;
use si_scale::helpers::bibytes2;
use std::os::raw::c_int as RawFd; use std::os::raw::c_int as RawFd;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use time::OffsetDateTime;
use tungstenite::Message; use tungstenite::Message;
#[cfg(unix)] #[cfg(unix)]
@@ -53,22 +48,6 @@ pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
None None
} }
// disgusting? absolutely, but does the trick for now
static LAST_LOGGED_BANDWIDTH_TS: AtomicI64 = AtomicI64::new(0);
fn maybe_log_bandwidth(remaining: i64) {
// SAFETY: this value is always populated with valid timestamps
let last =
OffsetDateTime::from_unix_timestamp(LAST_LOGGED_BANDWIDTH_TS.load(Ordering::Relaxed))
.unwrap();
let now = OffsetDateTime::now_utc();
if last + Duration::from_secs(10) < now {
log::info!("remaining bandwidth: {}", bibytes2(remaining as f64));
LAST_LOGGED_BANDWIDTH_TS.store(now.unix_timestamp(), Ordering::Relaxed)
}
}
#[derive(Debug)]
pub(crate) struct PartiallyDelegated { pub(crate) struct PartiallyDelegated {
sink_half: SplitSink<WsConn, Message>, sink_half: SplitSink<WsConn, Message>,
delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>), delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>),
@@ -76,10 +55,7 @@ pub(crate) struct PartiallyDelegated {
} }
impl PartiallyDelegated { impl PartiallyDelegated {
fn recover_received_plaintexts( fn recover_received_plaintexts(ws_msgs: Vec<Message>, shared_key: &SharedKeys) -> Vec<Vec<u8>> {
ws_msgs: Vec<Message>,
shared_key: &SharedKeys,
) -> Result<Vec<Vec<u8>>, GatewayClientError> {
let mut plaintexts = Vec::with_capacity(ws_msgs.len()); let mut plaintexts = Vec::with_capacity(ws_msgs.len());
for ws_msg in ws_msgs { for ws_msg in ws_msgs {
match ws_msg { match ws_msg {
@@ -97,32 +73,15 @@ impl PartiallyDelegated {
// TODO: those can return the "send confirmations" - perhaps it should be somehow worked around? // TODO: those can return the "send confirmations" - perhaps it should be somehow worked around?
Message::Text(text) => { Message::Text(text) => {
trace!( trace!(
"received a text message - probably a response to some previous query! - {text}", "received a text message - probably a response to some previous query! - {}",
text
); );
match ServerResponse::try_from(text)
.map_err(|_| GatewayClientError::MalformedResponse)?
{
ServerResponse::Send {
remaining_bandwidth,
} => maybe_log_bandwidth(remaining_bandwidth),
ServerResponse::Error { message } => {
error!("gateway failure: {message}");
return Err(GatewayClientError::GatewayError(message));
}
other => {
warn!(
"received illegal message of type {} in an authenticated client",
other.name()
)
}
}
continue; continue;
} }
_ => continue, _ => continue,
} }
} }
Ok(plaintexts) plaintexts
} }
fn route_socket_messages( fn route_socket_messages(
@@ -130,7 +89,7 @@ impl PartiallyDelegated {
packet_router: &PacketRouter, packet_router: &PacketRouter,
shared_key: &SharedKeys, shared_key: &SharedKeys,
) -> Result<(), GatewayClientError> { ) -> Result<(), GatewayClientError> {
let plaintexts = Self::recover_received_plaintexts(ws_msgs, shared_key)?; let plaintexts = Self::recover_received_plaintexts(ws_msgs, shared_key);
packet_router.route_received(plaintexts) packet_router.route_received(plaintexts)
} }
@@ -170,8 +129,7 @@ impl PartiallyDelegated {
}; };
if let Err(err) = Self::route_socket_messages(ws_msgs, &packet_router, shared_key.as_ref()) { if let Err(err) = Self::route_socket_messages(ws_msgs, &packet_router, shared_key.as_ref()) {
log::error!("Route socket messages failed: {err}"); log::warn!("Route socket messages failed: {err}");
break Err(err)
} }
} }
}; };
@@ -263,7 +221,6 @@ impl PartiallyDelegated {
// we can either have the stream itself or an option to re-obtain it // we can either have the stream itself or an option to re-obtain it
// by notifying the future owning it to finish the execution and awaiting the result // by notifying the future owning it to finish the execution and awaiting the result
// which should be almost immediate (or an invalid state which should never, ever happen) // which should be almost immediate (or an invalid state which should never, ever happen)
#[derive(Debug)]
pub(crate) enum SocketState { pub(crate) enum SocketState {
Available(Box<WsConn>), Available(Box<WsConn>),
PartiallyDelegated(PartiallyDelegated), PartiallyDelegated(PartiallyDelegated),
@@ -24,6 +24,7 @@ nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contr
nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" } nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true } serde_json = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
nym-http-api-client = { path = "../../../common/http-api-client"} nym-http-api-client = { path = "../../../common/http-api-client"}
thiserror = { workspace = true } thiserror = { workspace = true }
log = { workspace = true } log = { workspace = true }
@@ -66,14 +67,6 @@ cosmwasm-std = { workspace = true }
workspace = true workspace = true
features = ["tokio"] 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] [dev-dependencies]
bip39 = { workspace = true } bip39 = { workspace = true }
cosmrs = { workspace = true, features = ["bip32"] } cosmrs = { workspace = true, features = ["bip32"] }
@@ -44,7 +44,7 @@ pub enum NyxdError {
#[error("{0} is not a valid tx hash")] #[error("{0} is not a valid tx hash")]
InvalidTxHash(String), InvalidTxHash(String),
#[error("Tendermint RPC request failed - {0}")] #[error("Tendermint RPC request failed: {0}")]
TendermintErrorRpc(#[from] TendermintRpcError), TendermintErrorRpc(#[from] TendermintRpcError),
#[error("tendermint library failure: {0}")] #[error("tendermint library failure: {0}")]
@@ -56,22 +56,22 @@ pub enum NyxdError {
#[error("Failed when attempting to deserialize data ({0})")] #[error("Failed when attempting to deserialize data ({0})")]
DeserializationError(String), DeserializationError(String),
#[error("Failed when attempting to encode our protobuf data - {0}")] #[error("Failed when attempting to encode our protobuf data: {0}")]
ProtobufEncodingError(#[from] prost::EncodeError), ProtobufEncodingError(#[from] prost::EncodeError),
#[error("Failed to decode our protobuf data - {0}")] #[error("Failed to decode our protobuf data: {0}")]
ProtobufDecodingError(#[from] prost::DecodeError), ProtobufDecodingError(#[from] prost::DecodeError),
#[error("Account {0} does not exist on the chain")] #[error("Account '{0}' does not exist on the chain")]
NonExistentAccountError(AccountId), NonExistentAccountError(AccountId),
#[error("Failed on json serialization/deserialization - {0}")] #[error("Failed on json serialization/deserialization: {0}")]
SerdeJsonError(#[from] serde_json::Error), SerdeJsonError(#[from] serde_json::Error),
#[error("Account {0} is not a valid account address")] #[error("Account '{0}' is not a valid account address")]
MalformedAccountAddress(String), MalformedAccountAddress(String),
#[error("Account {0} has an invalid associated public key")] #[error("Account '{0}' has an invalid associated public key")]
InvalidPublicKey(AccountId), InvalidPublicKey(AccountId),
#[error("Queried contract (code_id: {0}) did not have any code information attached")] #[error("Queried contract (code_id: {0}) did not have any code information attached")]
@@ -86,7 +86,7 @@ pub enum NyxdError {
#[error("Block has an invalid height (either negative or larger than i64::MAX")] #[error("Block has an invalid height (either negative or larger than i64::MAX")]
InvalidHeight, InvalidHeight,
#[error("Failed to compress provided wasm code - {0}")] #[error("Failed to compress provided wasm code: {0}")]
WasmCompressionError(io::Error), WasmCompressionError(io::Error),
#[error("Logs returned from the validator were malformed")] #[error("Logs returned from the validator were malformed")]
@@ -109,7 +109,8 @@ trait TendermintRpcErrorMap {
impl TendermintRpcErrorMap for reqwest::Error { impl TendermintRpcErrorMap for reqwest::Error {
fn into_rpc_err(self) -> Error { fn into_rpc_err(self) -> Error {
todo!() // that's not the best error converion, but it's better than a panic
Error::client_internal(self.to_string())
} }
} }
@@ -148,10 +148,6 @@ pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
bail!("the provided free pass request has too long expiry (expiry is set to on {expiration_date})") bail!("the provided free pass request has too long expiry (expiry is set to on {expiration_date})")
} }
if expiration_date < now {
bail!("the provided free pass expiry is set in the past!")
}
// issuance start // issuance start
block_until_coconut_is_available(&client).await?; block_until_coconut_is_available(&client).await?;
@@ -1,354 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::fs::OpenOptions;
use clap::Parser;
use comfy_table::Table;
use csv::WriterBuilder;
use log::info;
use nym_mixnet_contract_common::ExecuteMsg;
use nym_mixnet_contract_common::ExecuteMsg::{DelegateToMixnode, UndelegateFromMixnode};
use nym_mixnet_contract_common::PendingEpochEventKind::{Delegate, Undelegate};
use nym_validator_client::nyxd::contract_traits::{NymContractsProvider, PagedMixnetQueryClient};
use nym_validator_client::nyxd::Coin;
use crate::context::SigningClient;
use crate::utils::pretty_coin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub memo: Option<String>,
#[clap(
long,
help = "Input csv files with delegation amounts. Format: (mixID, amount(in NYM))"
)]
pub input: String,
#[clap(
long,
help = "An output file path (CSV format) to create or append a log of results to"
)]
pub output: Option<String>,
}
#[derive(Debug)]
pub struct InputFileRow {
pub mix_id: String,
pub amount: Coin,
}
#[derive(Debug)]
pub struct InputFileReader {
pub rows: Vec<InputFileRow>,
}
impl InputFileReader {
pub fn new(path: &str) -> Result<InputFileReader, anyhow::Error> {
let file_contents = fs::read_to_string(path)?;
let mut rows = Vec::new();
let mut mix_id_set = HashSet::new();
for line in file_contents
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
{
let tokens: Vec<_> = line.split(',').collect();
if tokens.len() != 2 {
anyhow::bail!("Incorrect format: {}", line);
}
let mix_id = tokens[0].trim().to_string();
let input_amount = tokens[1]
.trim()
.parse::<u128>()
.map_err(|_| anyhow::anyhow!("'{}' has an invalid amount", line))?;
let micro_nym_amount = input_amount * 1_000_000;
if !mix_id_set.insert(mix_id.clone()) {
anyhow::bail!("Duplicate mix_id found: {}", mix_id);
}
rows.push(InputFileRow {
mix_id,
amount: Coin {
amount: micro_nym_amount,
denom: "unym".to_string(),
},
});
}
Ok(InputFileReader { rows })
}
}
fn write_to_csv(
output_details: Vec<[String; 3]>,
output_file: Option<String>,
) -> Result<(), anyhow::Error> {
if let Some(file_path) = output_file {
// Determine if the file exists and is not empty
let file_exists = fs::metadata(&file_path)
.map(|metadata| metadata.len() > 0)
.unwrap_or(false);
// Open the file for appending or creation
let file = OpenOptions::new()
.append(true)
.create(true)
.open(&file_path)?;
if !file_exists {
let mut wtr = csv::Writer::from_writer(&file);
wtr.write_record(["Operation", "Transaction Hash", "Timestamp"])?;
wtr.flush()?;
}
let mut wtr = WriterBuilder::new()
.has_headers(!file_exists)
.from_writer(file);
// Write the details to the CSV file
for detail in output_details {
wtr.write_record(&detail)?;
}
wtr.flush()?;
info!("All operations saved to output file");
}
Ok(())
}
async fn fetch_delegation_data(
client: &SigningClient,
) -> Result<HashMap<String, Coin>, anyhow::Error> {
let address = client.address();
// Fetch all delegations for the user
let delegations = match client.get_all_delegator_delegations(&address).await {
Ok(delegations) => delegations,
Err(e) => {
anyhow::bail!("Error fetching delegations: {}", e)
}
};
// Build a map to make it easier to handle delegation data
let mut existing_delegation_map: HashMap<String, Coin> = HashMap::new();
let mut pending_delegation_map: HashMap<String, Coin> = HashMap::new();
for delegation in delegations {
existing_delegation_map
.insert(delegation.mix_id.to_string(), Coin::from(delegation.amount));
}
// Look for pending delegate / undelegate events which might be of interest to us
let pending_events = match client.get_all_pending_epoch_events().await {
Ok(events) => events,
Err(e) => {
anyhow::bail!("Error fetching pending epoch events: {}", e);
}
};
for event in pending_events {
match event.event.kind {
// If a pending undelegate tx is found, remove it from delegation map
Undelegate { owner, mix_id, .. } => {
if owner == address.as_ref()
&& existing_delegation_map.contains_key(&mix_id.to_string())
{
existing_delegation_map.remove(&mix_id.to_string());
}
}
// If a pending delegation event is found, gather them to consolidate later
Delegate {
owner,
mix_id,
amount,
..
} => {
if owner == address.as_ref() {
let mut amount = Coin::from(amount);
if let Some(pending_record) = pending_delegation_map.get(&mix_id.to_string()) {
amount.amount += pending_record.amount;
}
pending_delegation_map.insert(mix_id.to_string(), amount);
}
}
_ => {}
};
}
// Consolidate pending events into delegation map
for (mix_id, amount) in pending_delegation_map {
existing_delegation_map
.entry(mix_id)
.and_modify(|e| e.amount += amount.amount)
.or_insert(amount);
}
Ok(existing_delegation_map)
}
pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) {
let records = match InputFileReader::new(&args.input) {
Ok(records) => records,
Err(e) => {
println!("Error reading input file: {}", e);
return;
}
};
let existing_delegation_map = fetch_delegation_data(&client)
.await
.expect("Could not fetch existing delegations");
let mut delegation_table = Table::new();
let mut undelegation_table = Table::new();
let mut delegation_msgs: Vec<(ExecuteMsg, Vec<Coin>)> = Vec::new();
let mut undelegation_msgs: Vec<(ExecuteMsg, Vec<Coin>)> = Vec::new();
delegation_table.set_header(["Mix ID", "Input Amount", "Adjusted Amount"]);
undelegation_table.set_header(["Mix ID"]);
for row in &records.rows {
let input_amount = row.amount.amount;
let existing_delegation_amount = existing_delegation_map
.get(&row.mix_id)
.map_or(0, |coin| coin.amount);
match existing_delegation_amount.cmp(&input_amount) {
Ordering::Equal => continue, // No action needed if amounts are equal
Ordering::Less => {
// Delegate the difference if the existing delegation is less
let difference = Coin {
amount: input_amount - existing_delegation_amount,
denom: row.amount.denom.clone(),
};
let mix_id = row.mix_id.clone().parse::<u32>().unwrap();
delegation_msgs.push((DelegateToMixnode { mix_id }, vec![difference.clone()]));
delegation_table.add_row(&[
row.mix_id.clone(),
pretty_coin(&row.amount),
pretty_coin(&difference),
]);
}
Ordering::Greater => {
let mix_id = row.mix_id.clone().parse::<u32>().unwrap();
let coins: Vec<Coin> = vec![];
undelegation_msgs.push((UndelegateFromMixnode { mix_id }, coins));
undelegation_table.add_row(&[row.mix_id.clone()]);
if row.amount.amount > 0 {
delegation_msgs.push((DelegateToMixnode { mix_id }, vec![row.amount.clone()]));
delegation_table.add_row(&[
row.mix_id.clone(),
pretty_coin(&row.amount),
pretty_coin(&row.amount),
]);
}
}
}
}
if delegation_msgs.is_empty() && undelegation_msgs.is_empty() {
println!("Nothing to do. Delegations are up-to-date!");
return;
}
if !undelegation_msgs.is_empty() {
println!("Undelegation records : \n{}\n\n", undelegation_table);
}
if !delegation_msgs.is_empty() {
println!("Delegation records : \n{}\n\n", delegation_table);
}
let ans = inquire::Confirm::new("Do you want to continue with the shown operations?")
.with_default(false)
.with_help_message("You must confirm before the transactions are signed")
.prompt();
if let Err(e) = ans {
info!("Aborting, {}...", e);
return;
}
if let Ok(false) = ans {
info!("Aborting:: User denied proceeding with signing!");
return;
}
let mut output_details: Vec<[String; 3]> = Vec::new();
let now = time::OffsetDateTime::now_utc();
let now = now
.format(&time::format_description::well_known::Rfc3339)
.unwrap();
let mixnet_contract = client
.mixnet_contract_address()
.expect("mixnet contract address is not available");
// Execute all undelegation transactions
if !undelegation_msgs.is_empty() {
let res = client
.execute_multiple(
mixnet_contract,
undelegation_msgs.clone(),
None,
format!(
"Undelegate from {} nodes via nym-cli",
undelegation_msgs.len()
),
)
.await
.expect("Could not undelegate!");
println!(
"Undelegation transaction successful : {}",
res.transaction_hash
);
output_details.push([
"Undelegate".to_string(),
res.transaction_hash.to_string(),
now.clone(),
]);
}
// Execute all delegation delegations
if !delegation_msgs.is_empty() {
let res = client
.execute_multiple(
mixnet_contract,
delegation_msgs,
None,
format!(
"Delegatation to {} nodes via nym-cli",
undelegation_msgs.len()
),
)
.await
.expect("Could not delegate");
println!(
"Delegation transaction successful : {}",
res.transaction_hash
);
output_details.push([
"Delegate".to_string(),
res.transaction_hash.to_string(),
now.clone(),
]);
}
if args.output.is_some() {
if let Err(e) = write_to_csv(output_details, args.output) {
info!("Failed to write to CSV, {}", e);
}
}
}
@@ -6,7 +6,6 @@ use clap::{Args, Subcommand};
pub mod rewards; pub mod rewards;
pub mod delegate_to_mixnode; pub mod delegate_to_mixnode;
pub mod delegate_to_multiple_mixnodes;
pub mod query_for_delegations; pub mod query_for_delegations;
pub mod undelegate_from_mixnode; pub mod undelegate_from_mixnode;
pub mod vesting_delegate_to_mixnode; pub mod vesting_delegate_to_mixnode;
@@ -27,8 +26,6 @@ pub enum MixnetDelegatorsCommands {
Rewards(rewards::MixnetDelegatorsReward), Rewards(rewards::MixnetDelegatorsReward),
/// Delegate to a mixnode /// Delegate to a mixnode
Delegate(delegate_to_mixnode::Args), Delegate(delegate_to_mixnode::Args),
/// Perform bulk delegations from an input file
DelegateMulti(delegate_to_multiple_mixnodes::Args),
/// Undelegate from a mixnode /// Undelegate from a mixnode
Undelegate(undelegate_from_mixnode::Args), Undelegate(undelegate_from_mixnode::Args),
/// Delegate to a mixnode with locked tokens /// Delegate to a mixnode with locked tokens
@@ -328,8 +328,4 @@ impl EpochState {
pub fn is_dealing_exchange(&self) -> bool { pub fn is_dealing_exchange(&self) -> bool {
matches!(self, EpochState::DealingExchange { .. }) matches!(self, EpochState::DealingExchange { .. })
} }
pub fn is_waiting_initialisation(&self) -> bool {
matches!(self, EpochState::WaitingInitialisation)
}
} }
+1 -2
View File
@@ -8,7 +8,6 @@ license.workspace = true
[dependencies] [dependencies]
async-trait = { workspace = true } async-trait = { workspace = true }
log = { workspace = true } log = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tokio = { workspace = true, features = ["sync"]} tokio = { workspace = true, features = ["sync"]}
@@ -26,4 +25,4 @@ features = [ "rt-multi-thread", "net", "signal", "fs" ]
[build-dependencies] [build-dependencies]
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] } 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"] }
@@ -1,48 +0,0 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
ALTER TABLE coconut_credentials
RENAME TO old_coconut_credentials;
CREATE TABLE coconut_credentials
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
-- introduce a way for us to introduce breaking changes in serialization
serialization_revision INTEGER NOT NULL,
-- the best we can do without enums
credential_type TEXT CHECK ( credential_type IN ('BandwidthVoucher', 'FreeBandwidthPass') ) NOT NULL,
credential_data BLOB NOT NULL UNIQUE,
epoch_id INTEGER NOT NULL,
-- this field is only really applicable to free passes
expired BOOLEAN NOT NULL
);
ALTER TABLE credential_usage
RENAME TO old_credential_usage;
-- for bandwidth vouchers there's going to be only a single entry; for freepasses there can be as many as there are gateways
CREATE TABLE credential_usage
(
credential_id INTEGER NOT NULL REFERENCES coconut_credentials (id),
gateway_id_bs58 TEXT NOT NULL,
-- no matter credential type, we can't spend the same credential with the same gateway multiple times
UNIQUE (credential_id, gateway_id_bs58)
);
INSERT INTO coconut_credentials
SELECT *
FROM old_coconut_credentials;
INSERT INTO credential_usage
SELECT *
FROM old_credential_usage;
DROP TABLE old_coconut_credentials;
DROP TABLE old_credential_usage;
@@ -33,6 +33,12 @@ impl CoconutCredentialManager {
} }
} }
pub async fn take_credentials(self) -> Vec<StoredIssuedCredential> {
let mut inner = self.inner.write().await;
inner.credential_usage = Vec::new();
std::mem::take(&mut inner.credentials)
}
pub async fn insert_issued_credential( pub async fn insert_issued_credential(
&self, &self,
credential_type: String, credential_type: String,
@@ -44,7 +44,7 @@ impl CoconutCredentialManager {
r#" r#"
SELECT * SELECT *
FROM coconut_credentials FROM coconut_credentials
WHERE coconut_credentials.credential_type == "FreeBandwidthPass" AND coconut_credentials.expired = false WHERE coconut_credentials.credential_type == "FreeBandwidthPass"
AND NOT EXISTS (SELECT 1 AND NOT EXISTS (SELECT 1
FROM credential_usage FROM credential_usage
WHERE credential_usage.credential_id = coconut_credentials.id WHERE credential_usage.credential_id = coconut_credentials.id
@@ -1,8 +1,6 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use std::fmt::{self, Debug, Formatter};
use crate::backends::memory::CoconutCredentialManager; use crate::backends::memory::CoconutCredentialManager;
use crate::error::StorageError; use crate::error::StorageError;
use crate::models::{StorableIssuedCredential, StoredIssuedCredential}; use crate::models::{StorableIssuedCredential, StoredIssuedCredential};
@@ -17,6 +15,12 @@ pub struct EphemeralStorage {
coconut_credential_manager: CoconutCredentialManager, coconut_credential_manager: CoconutCredentialManager,
} }
impl EphemeralStorage {
pub async fn take_credentials(self) -> Vec<StoredIssuedCredential> {
self.coconut_credential_manager.take_credentials().await
}
}
impl Default for EphemeralStorage { impl Default for EphemeralStorage {
fn default() -> Self { fn default() -> Self {
EphemeralStorage { EphemeralStorage {
@@ -25,12 +29,6 @@ impl Default for EphemeralStorage {
} }
} }
impl Debug for EphemeralStorage {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "EphemeralStorage")
}
}
#[async_trait] #[async_trait]
impl Storage for EphemeralStorage { impl Storage for EphemeralStorage {
type StorageError = StorageError; type StorageError = StorageError;
-3
View File
@@ -18,7 +18,4 @@ pub enum StorageError {
#[error("No unused credential in database. You need to buy at least one")] #[error("No unused credential in database. You need to buy at least one")]
NoCredential, NoCredential,
#[error("Database unique constraint violation. Is the credential already imported?")]
ConstraintUnique,
} }
-13
View File
@@ -3,19 +3,6 @@
use zeroize::{Zeroize, ZeroizeOnDrop}; use zeroize::{Zeroize, ZeroizeOnDrop};
// #[derive(Clone)]
// pub struct CoconutCredential {
// #[allow(dead_code)]
// pub id: i64,
// pub voucher_value: String,
// pub voucher_info: String,
// pub serial_number: String,
// pub binding_number: String,
// pub signature: String,
// pub epoch_id: String,
// pub consumed: bool,
// }
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))] #[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
#[derive(Zeroize, ZeroizeOnDrop, Clone)] #[derive(Zeroize, ZeroizeOnDrop, Clone)]
pub struct StoredIssuedCredential { pub struct StoredIssuedCredential {
@@ -69,21 +69,9 @@ impl Storage for PersistentStorage {
bandwidth_credential.credential_data, bandwidth_credential.credential_data,
bandwidth_credential.epoch_id, bandwidth_credential.epoch_id,
) )
.await .await?;
.map_err(|err| {
// There is one error we want to handle specifically. Ok(())
// 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( async fn get_next_unspent_credential(
+5 -5
View File
@@ -8,11 +8,11 @@ use std::str::FromStr;
use thiserror::Error; use thiserror::Error;
pub use nym_coconut::{ pub use nym_coconut::{
aggregate_signature_shares, aggregate_signature_shares_and_verify, aggregate_verification_keys, aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar, keygen,
blind_sign, hash_to_scalar, keygen, prepare_blind_sign, prove_bandwidth_credential, prepare_blind_sign, prove_bandwidth_credential, verify_credential, Attribute, Base58,
verify_credential, Attribute, Base58, BlindSignRequest, BlindedSerialNumber, BlindedSignature, BlindSignRequest, BlindedSerialNumber, BlindedSignature, Bytable, CoconutError, KeyPair,
Bytable, CoconutError, KeyPair, Parameters, PrivateAttribute, PublicAttribute, SecretKey, Parameters, PrivateAttribute, PublicAttribute, SecretKey, Signature, SignatureShare,
Signature, SignatureShare, VerificationKey, VerifyCredentialRequest, VerificationKey, VerifyCredentialRequest,
}; };
pub const VOUCHER_INFO_TYPE: &str = "BandwidthVoucher"; pub const VOUCHER_INFO_TYPE: &str = "BandwidthVoucher";
@@ -12,8 +12,7 @@ use serde::{Deserialize, Serialize};
use time::{Duration, OffsetDateTime, Time}; use time::{Duration, OffsetDateTime, Time};
use zeroize::{Zeroize, ZeroizeOnDrop}; use zeroize::{Zeroize, ZeroizeOnDrop};
pub const DEFAULT_FREE_PASS_VALIDITY: Duration = Duration::WEEK; // 1 week pub const MAX_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)] #[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub struct FreePassIssuedData { pub struct FreePassIssuedData {
@@ -78,9 +77,9 @@ impl FreePassIssuanceData {
} }
pub fn default_expiry_date() -> OffsetDateTime { pub fn default_expiry_date() -> OffsetDateTime {
// set it to the furthest midnight in the future such as it's no more than a week away, // set it to 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 // 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() + DEFAULT_FREE_PASS_VALIDITY).replace_time(Time::MIDNIGHT) (OffsetDateTime::now_utc() + MAX_FREE_PASS_VALIDITY).replace_time(Time::MIDNIGHT)
} }
pub fn expiry_date_attribute(&self) -> &Attribute { pub fn expiry_date_attribute(&self) -> &Attribute {
@@ -10,19 +10,18 @@ use crate::coconut::bandwidth::{
use crate::coconut::utils::scalar_serde_helper; use crate::coconut::utils::scalar_serde_helper;
use crate::error::Error; use crate::error::Error;
use nym_credentials_interface::{ use nym_credentials_interface::{
aggregate_signature_shares, aggregate_signature_shares_and_verify, hash_to_scalar, aggregate_signature_shares, hash_to_scalar, prepare_blind_sign, Attribute, BlindedSerialNumber,
prepare_blind_sign, Attribute, BlindedSerialNumber, BlindedSignature, Parameters, BlindedSignature, Parameters, PrivateAttribute, PublicAttribute, Signature, SignatureShare,
PrivateAttribute, PublicAttribute, Signature, SignatureShare, VerificationKey, VerificationKey,
}; };
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::{encryption, identity};
use nym_validator_client::nym_api::EpochId; use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::{Coin, Hash};
use nym_validator_client::signing::AccountData; use nym_validator_client::signing::AccountData;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use time::OffsetDateTime; use time::OffsetDateTime;
use zeroize::{Zeroize, ZeroizeOnDrop}; use zeroize::{Zeroize, ZeroizeOnDrop};
pub use nym_validator_client::nyxd::{Coin, Hash};
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] #[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub enum BandwidthCredentialIssuanceDataVariant { pub enum BandwidthCredentialIssuanceDataVariant {
Voucher(BandwidthVoucherIssuanceData), Voucher(BandwidthVoucherIssuanceData),
@@ -266,13 +265,6 @@ impl IssuanceBandwidthCredential {
self.unblind_signature(validator_vk, &signing_data, blinded_signature) 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( pub fn aggregate_signature_shares(
&self, &self,
verification_key: &VerificationKey, verification_key: &VerificationKey,
@@ -287,7 +279,7 @@ impl IssuanceBandwidthCredential {
attributes.extend_from_slice(&private_attributes); attributes.extend_from_slice(&private_attributes);
attributes.extend_from_slice(&public_attributes); attributes.extend_from_slice(&public_attributes);
aggregate_signature_shares_and_verify(params, verification_key, &attributes, shares) aggregate_signature_shares(params, verification_key, &attributes, shares)
.map_err(Error::SignatureAggregationError) .map_err(Error::SignatureAggregationError)
} }
@@ -6,7 +6,7 @@ use crate::coconut::utils::scalar_serde_helper;
use crate::error::Error; use crate::error::Error;
use nym_api_requests::coconut::BlindSignRequestBody; use nym_api_requests::coconut::BlindSignRequestBody;
use nym_credentials_interface::{ use nym_credentials_interface::{
hash_to_scalar, Attribute, BlindSignRequest, BlindedSignature, CredentialType, PublicAttribute, hash_to_scalar, Attribute, BlindSignRequest, BlindedSignature, PublicAttribute,
}; };
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::{encryption, identity};
use nym_validator_client::nyxd::{Coin, Hash}; use nym_validator_client::nyxd::{Coin, Hash};
@@ -123,10 +123,6 @@ impl BandwidthVoucherIssuanceData {
&self.value_prehashed &self.value_prehashed
} }
pub fn typ() -> CredentialType {
CredentialType::Voucher
}
pub fn tx_hash(&self) -> Hash { pub fn tx_hash(&self) -> Hash {
self.deposit_tx_hash self.deposit_tx_hash
} }
+8
View File
@@ -17,3 +17,11 @@ schemars = { workspace = true, features = ["preserve_order"] }
serde = { workspace = true, features = ["derive"]} serde = { workspace = true, features = ["derive"]}
thiserror = { workspace = true } thiserror = { workspace = true }
url = { workspace = true } url = { workspace = true }
# 'wasm-serde-types' feature
tsify = { workspace = true, features = ["js"], optional = true }
wasm-bindgen = { workspace = true, optional = true }
[features]
default = []
wasm-serde-types = ["tsify", "wasm-bindgen"]
+91 -7
View File
@@ -11,39 +11,96 @@ use std::{
ffi::OsStr, ffi::OsStr,
ops::Not, ops::Not,
}; };
use url::Url; pub use url::{ParseError as UrlParseError, Url};
#[cfg(feature = "wasm-serde-types")]
use tsify::Tsify;
#[cfg(feature = "wasm-serde-types")]
use wasm_bindgen::prelude::wasm_bindgen;
pub mod mainnet; pub mod mainnet;
pub mod var_names; pub mod var_names;
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct ChainDetails { pub struct ChainDetails {
#[serde(alias = "bech32_account_prefix")]
pub bech32_account_prefix: String, pub bech32_account_prefix: String,
#[serde(alias = "mix_denom")]
pub mix_denom: DenomDetailsOwned, pub mix_denom: DenomDetailsOwned,
#[serde(alias = "stake_denom")]
pub stake_denom: DenomDetailsOwned, pub stake_denom: DenomDetailsOwned,
} }
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct NymContracts { pub struct NymContracts {
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "mixnet_contract_address")]
pub mixnet_contract_address: Option<String>, pub mixnet_contract_address: Option<String>,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "vesting_contract_address")]
pub vesting_contract_address: Option<String>, pub vesting_contract_address: Option<String>,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "coconut_bandwidth_contract_address")]
pub coconut_bandwidth_contract_address: Option<String>, pub coconut_bandwidth_contract_address: Option<String>,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "group_contract_address")]
pub group_contract_address: Option<String>, pub group_contract_address: Option<String>,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "multisig_contract_address")]
pub multisig_contract_address: Option<String>, pub multisig_contract_address: Option<String>,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "coconut_dkg_contract_address")]
pub coconut_dkg_contract_address: Option<String>, pub coconut_dkg_contract_address: Option<String>,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "ephemera_contract_address")]
pub ephemera_contract_address: Option<String>, pub ephemera_contract_address: Option<String>,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "service_provider_directory_contract_address")]
pub service_provider_directory_contract_address: Option<String>, pub service_provider_directory_contract_address: Option<String>,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "name_service_contract_address")]
pub name_service_contract_address: Option<String>, pub name_service_contract_address: Option<String>,
} }
// I wanted to use the simpler `NetworkDetails` name, but there's a clash // I wanted to use the simpler `NetworkDetails` name, but there's a clash
// with `NetworkDetails` defined in all.rs... // with `NetworkDetails` defined in all.rs...
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct NymNetworkDetails { pub struct NymNetworkDetails {
#[serde(alias = "network_name")]
pub network_name: String, pub network_name: String,
#[serde(alias = "chain_details")]
pub chain_details: ChainDetails, pub chain_details: ChainDetails,
pub endpoints: Vec<ValidatorDetails>, pub endpoints: Vec<ValidatorDetails>,
pub contracts: NymContracts, pub contracts: NymContracts,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "explorer_api")]
pub explorer_api: Option<String>, pub explorer_api: Option<String>,
} }
@@ -316,10 +373,17 @@ impl DenomDetails {
} }
#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq, JsonSchema)] #[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq, JsonSchema)]
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct DenomDetailsOwned { pub struct DenomDetailsOwned {
pub base: String, pub base: String,
pub display: String, pub display: String,
// i.e. display_amount * 10^display_exponent = base_amount // i.e. display_amount * 10^display_exponent = base_amount
#[serde(alias = "display_exponent")]
pub display_exponent: u32, pub display_exponent: u32,
} }
@@ -344,14 +408,24 @@ impl DenomDetailsOwned {
} }
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct ValidatorDetails { pub struct ValidatorDetails {
// it is assumed those values are always valid since they're being provided in our defaults file // it is assumed those values are always valid since they're being provided in our defaults file
#[serde(alias = "nyxd_url")]
pub nyxd_url: String, pub nyxd_url: String,
//
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "websocket_url")]
pub websocket_url: Option<String>, pub websocket_url: Option<String>,
// Right now api_url is optional as we are not running the api reliably on all validators // Right now api_url is optional as we are not running the api reliably on all validators
// however, later on it should be a mandatory field // however, later on it should be a mandatory field
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "api_url")]
pub api_url: Option<String>, pub api_url: Option<String>,
// TODO: I'd argue this one should also have a field like `gas_price` since its a validator-specific setting // TODO: I'd argue this one should also have a field like `gas_price` since its a validator-specific setting
} }
@@ -373,16 +447,26 @@ impl ValidatorDetails {
} }
} }
pub fn try_nyxd_url(&self) -> Result<Url, url::ParseError> {
self.nyxd_url.parse()
}
pub fn nyxd_url(&self) -> Url { pub fn nyxd_url(&self) -> Url {
self.nyxd_url self.try_nyxd_url()
.parse()
.expect("the provided nyxd url is invalid!") .expect("the provided nyxd url is invalid!")
} }
pub fn try_api_url(&self) -> Option<Result<Url, url::ParseError>> {
self.api_url.as_ref().map(|url| url.parse())
}
pub fn api_url(&self) -> Option<Url> { pub fn api_url(&self) -> Option<Url> {
self.api_url self.try_api_url()
.as_ref() .map(|url| url.expect("the provided api url is invalid!"))
.map(|url| url.parse().expect("the provided api url is invalid!")) }
pub fn try_websocket_url(&self) -> Option<Result<Url, url::ParseError>> {
self.websocket_url.as_ref().map(|url| url.parse())
} }
pub fn websocket_url(&self) -> Option<Url> { pub fn websocket_url(&self) -> Option<Url> {
+3 -6
View File
@@ -18,12 +18,9 @@ pub const VESTING_CONTRACT_ADDRESS: &str =
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw"; "n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
pub const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = ""; pub const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "";
pub const GROUP_CONTRACT_ADDRESS: &str = pub const GROUP_CONTRACT_ADDRESS: &str = "";
"n1e2zq4886zzewpvpucmlw8v9p7zv692f6yck4zjzxh699dkcmlrfqk2knsr"; pub const MULTISIG_CONTRACT_ADDRESS: &str = "";
pub const MULTISIG_CONTRACT_ADDRESS: &str = pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "";
"n1txayqfz5g9qww3rlflpg025xd26m9payz96u54x4fe3s2ktz39xqk67gzx";
pub const COCONUT_DKG_CONTRACT_ADDRESS: &str =
"n19604yflqggs9mk2z26mqygq43q2kr3n932egxx630svywd5mpxjsztfpvx";
pub const EPHEMERA_CONTRACT_ADDRESS: &str = ""; pub const EPHEMERA_CONTRACT_ADDRESS: &str = "";
pub const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy"; pub const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
+5 -5
View File
@@ -6,10 +6,10 @@ use criterion::{criterion_group, criterion_main, Criterion};
use ff::Field; use ff::Field;
use group::{Curve, Group}; use group::{Curve, Group};
use nym_coconut::{ use nym_coconut::{
aggregate_signature_shares_and_verify, aggregate_verification_keys, blind_sign, aggregate_signature_shares, aggregate_verification_keys, blind_sign, prepare_blind_sign,
prepare_blind_sign, prove_bandwidth_credential, random_scalars_refs, setup, ttp_keygen, prove_bandwidth_credential, random_scalars_refs, setup, ttp_keygen, verify_credential,
verify_credential, verify_partial_blind_signature, Attribute, BlindedSignature, Parameters, verify_partial_blind_signature, Attribute, BlindedSignature, Parameters, Signature,
Signature, SignatureShare, VerificationKey, SignatureShare, VerificationKey,
}; };
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use std::ops::Neg; use std::ops::Neg;
@@ -99,7 +99,7 @@ fn unblind_and_aggregate(
let mut attributes = vec![]; let mut attributes = vec![];
attributes.extend_from_slice(private_attributes); attributes.extend_from_slice(private_attributes);
attributes.extend_from_slice(public_attributes); attributes.extend_from_slice(public_attributes);
aggregate_signature_shares_and_verify( aggregate_signature_shares(
params, params,
verification_key, verification_key,
&attributes, &attributes,
-7
View File
@@ -4,18 +4,14 @@
#![warn(clippy::expect_used)] #![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)] #![warn(clippy::unwrap_used)]
pub use bls12_381::Scalar;
pub use elgamal::elgamal_keygen; pub use elgamal::elgamal_keygen;
pub use elgamal::ElGamalKeyPair; pub use elgamal::ElGamalKeyPair;
pub use elgamal::PublicKey; pub use elgamal::PublicKey;
pub use error::CoconutError; 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;
pub use scheme::aggregation::aggregate_signature_shares_and_verify;
pub use scheme::aggregation::aggregate_verification_keys; pub use scheme::aggregation::aggregate_verification_keys;
pub use scheme::issuance::blind_sign; pub use scheme::issuance::blind_sign;
pub use scheme::issuance::prepare_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::verify_partial_blind_signature;
pub use scheme::issuance::BlindSignRequest; pub use scheme::issuance::BlindSignRequest;
pub use scheme::keygen::keygen; pub use scheme::keygen::keygen;
@@ -23,19 +19,16 @@ pub use scheme::keygen::ttp_keygen;
pub use scheme::keygen::KeyPair; pub use scheme::keygen::KeyPair;
pub use scheme::keygen::SecretKey; pub use scheme::keygen::SecretKey;
pub use scheme::keygen::VerificationKey; pub use scheme::keygen::VerificationKey;
pub use scheme::keygen::VerificationKeyShare;
pub use scheme::setup::setup; pub use scheme::setup::setup;
pub use scheme::setup::Parameters; pub use scheme::setup::Parameters;
pub use scheme::verification::check_vk_pairing; pub use scheme::verification::check_vk_pairing;
pub use scheme::verification::prove_bandwidth_credential; pub use scheme::verification::prove_bandwidth_credential;
pub use scheme::verification::verify;
pub use scheme::verification::verify_credential; pub use scheme::verification::verify_credential;
pub use scheme::verification::BlindedSerialNumber; pub use scheme::verification::BlindedSerialNumber;
pub use scheme::verification::VerifyCredentialRequest; pub use scheme::verification::VerifyCredentialRequest;
pub use scheme::BlindedSignature; pub use scheme::BlindedSignature;
pub use scheme::Signature; pub use scheme::Signature;
pub use scheme::SignatureShare; pub use scheme::SignatureShare;
pub use scheme::SignerIndex;
pub use traits::Base58; pub use traits::Base58;
pub use traits::Bytable; pub use traits::Bytable;
pub use utils::hash_to_scalar; pub use utils::hash_to_scalar;
+25 -55
View File
@@ -12,7 +12,7 @@ use crate::error::{CoconutError, Result};
use crate::scheme::verification::check_bilinear_pairing; use crate::scheme::verification::check_bilinear_pairing;
use crate::scheme::{PartialSignature, Signature, SignatureShare, SignerIndex, VerificationKey}; use crate::scheme::{PartialSignature, Signature, SignatureShare, SignerIndex, VerificationKey};
use crate::utils::perform_lagrangian_interpolation_at_origin; use crate::utils::perform_lagrangian_interpolation_at_origin;
use crate::{Attribute, Parameters, VerificationKeyShare}; use crate::{Attribute, Parameters};
pub(crate) trait Aggregatable: Sized { pub(crate) trait Aggregatable: Sized {
fn aggregate(aggregatable: &[Self], indices: Option<&[SignerIndex]>) -> Result<Self>; fn aggregate(aggregatable: &[Self], indices: Option<&[SignerIndex]>) -> Result<Self>;
@@ -80,23 +80,7 @@ pub fn aggregate_verification_keys(
Aggregatable::aggregate(keys, indices) 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( pub fn aggregate_signatures(
signatures: &[PartialSignature],
indices: Option<&[SignerIndex]>,
) -> Result<Signature> {
Aggregatable::aggregate(signatures, indices)
}
pub fn aggregate_signatures_and_verify(
params: &Parameters, params: &Parameters,
verification_key: &VerificationKey, verification_key: &VerificationKey,
attributes: &[&Attribute], attributes: &[&Attribute],
@@ -104,7 +88,11 @@ pub fn aggregate_signatures_and_verify(
indices: Option<&[SignerIndex]>, indices: Option<&[SignerIndex]>,
) -> Result<Signature> { ) -> Result<Signature> {
// aggregate the signature // aggregate the signature
let signature = aggregate_signatures(signatures, indices)?;
let signature = match Aggregatable::aggregate(signatures, indices) {
Ok(res) => res,
Err(err) => return Err(err),
};
// Verify the signature // Verify the signature
let alpha = verification_key.alpha; let alpha = verification_key.alpha;
@@ -128,16 +116,7 @@ pub fn aggregate_signatures_and_verify(
Ok(signature) Ok(signature)
} }
pub fn aggregate_signature_shares(shares: &[SignatureShare]) -> Result<Signature> { pub fn aggregate_signature_shares(
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, params: &Parameters,
verification_key: &VerificationKey, verification_key: &VerificationKey,
attributes: &[&Attribute], attributes: &[&Attribute],
@@ -148,7 +127,7 @@ pub fn aggregate_signature_shares_and_verify(
.map(|share| (*share.signature(), share.index())) .map(|share| (*share.signature(), share.index()))
.unzip(); .unzip();
aggregate_signatures_and_verify( aggregate_signatures(
params, params,
verification_key, verification_key,
attributes, attributes,
@@ -231,7 +210,7 @@ mod tests {
#[test] #[test]
fn signature_aggregation_works_for_any_subset_of_signatures() { fn signature_aggregation_works_for_any_subset_of_signatures() {
let params = Parameters::new(2).unwrap(); let mut params = Parameters::new(2).unwrap();
random_scalars_refs!(attributes, params, 2); random_scalars_refs!(attributes, params, 2);
let keypairs = ttp_keygen(&params, 3, 5).unwrap(); let keypairs = ttp_keygen(&params, 3, 5).unwrap();
@@ -248,12 +227,12 @@ mod tests {
let sigs = sks let sigs = sks
.iter() .iter()
.map(|sk| sign(&params, sk, &attributes).unwrap()) .map(|sk| sign(&mut params, sk, &attributes).unwrap())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// aggregating (any) threshold works // aggregating (any) threshold works
let aggr_vk_1 = aggregate_verification_keys(&vks[..3], Some(&[1, 2, 3])).unwrap(); let aggr_vk_1 = aggregate_verification_keys(&vks[..3], Some(&[1, 2, 3])).unwrap();
let aggr_sig1 = aggregate_signatures_and_verify( let aggr_sig1 = aggregate_signatures(
&params, &params,
&aggr_vk_1, &aggr_vk_1,
&attributes, &attributes,
@@ -263,7 +242,7 @@ mod tests {
.unwrap(); .unwrap();
let aggr_vk_2 = aggregate_verification_keys(&vks[2..], Some(&[3, 4, 5])).unwrap(); let aggr_vk_2 = aggregate_verification_keys(&vks[2..], Some(&[3, 4, 5])).unwrap();
let aggr_sig2 = aggregate_signatures_and_verify( let aggr_sig2 = aggregate_signatures(
&params, &params,
&aggr_vk_1, &aggr_vk_1,
&attributes, &attributes,
@@ -279,7 +258,7 @@ mod tests {
// aggregating threshold+1 works // aggregating threshold+1 works
let aggr_vk_more = aggregate_verification_keys(&vks[1..], Some(&[2, 3, 4, 5])).unwrap(); let aggr_vk_more = aggregate_verification_keys(&vks[1..], Some(&[2, 3, 4, 5])).unwrap();
let aggr_more = aggregate_signatures_and_verify( let aggr_more = aggregate_signatures(
&params, &params,
&aggr_vk_more, &aggr_vk_more,
&attributes, &attributes,
@@ -291,7 +270,7 @@ mod tests {
// aggregating all // aggregating all
let aggr_vk_all = aggregate_verification_keys(&vks, Some(&[1, 2, 3, 4, 5])).unwrap(); let aggr_vk_all = aggregate_verification_keys(&vks, Some(&[1, 2, 3, 4, 5])).unwrap();
let aggr_all = aggregate_signatures_and_verify( let aggr_all = aggregate_signatures(
&params, &params,
&aggr_vk_all, &aggr_vk_all,
&attributes, &attributes,
@@ -303,7 +282,7 @@ mod tests {
// not taking enough points (threshold was 3) should fail // 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_vk_not_enough = aggregate_verification_keys(&vks[..2], Some(&[1, 2])).unwrap();
let aggr_not_enough = aggregate_signatures_and_verify( let aggr_not_enough = aggregate_signatures(
&params, &params,
&aggr_vk_not_enough, &aggr_vk_not_enough,
&attributes, &attributes,
@@ -315,7 +294,7 @@ mod tests {
// taking wrong index should fail // taking wrong index should fail
let aggr_vk_bad = aggregate_verification_keys(&vks[2..], Some(&[1, 2, 3])).unwrap(); let aggr_vk_bad = aggregate_verification_keys(&vks[2..], Some(&[1, 2, 3])).unwrap();
assert!(aggregate_signatures_and_verify( assert!(aggregate_signatures(
&params, &params,
&aggr_vk_bad, &aggr_vk_bad,
&attributes, &attributes,
@@ -351,14 +330,9 @@ mod tests {
.unzip(); .unzip();
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap(); let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
assert!(aggregate_signatures_and_verify( assert!(
&params, aggregate_signatures(&params, &aggr_vk_all, &attributes, &signatures, None).is_err()
&aggr_vk_all, );
&attributes,
&signatures,
None
)
.is_err());
} }
#[test] #[test]
@@ -378,15 +352,11 @@ mod tests {
.unzip(); .unzip();
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap(); let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
assert!(aggregate_signatures_and_verify( assert!(
&params, aggregate_signatures(&params, &aggr_vk_all, &attributes, &signatures, Some(&[]))
&aggr_vk_all, .is_err()
&attributes, );
&signatures, assert!(aggregate_signatures(
Some(&[])
)
.is_err());
assert!(aggregate_signatures_and_verify(
&params, &params,
&aggr_vk_all, &aggr_vk_all,
&attributes, &attributes,
@@ -413,7 +383,7 @@ mod tests {
.unzip(); .unzip();
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap(); let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
assert!(aggregate_signatures_and_verify( assert!(aggregate_signatures(
&params, &params,
&aggr_vk_all, &aggr_vk_all,
&attributes, &attributes,
+4 -7
View File
@@ -13,8 +13,9 @@ use crate::scheme::setup::Parameters;
use crate::scheme::BlindedSignature; use crate::scheme::BlindedSignature;
use crate::scheme::SecretKey; use crate::scheme::SecretKey;
use crate::Attribute; use crate::Attribute;
/// Creates a Coconut Signature under a given secret key on a set of public attributes only.
#[cfg(test)]
use crate::Signature; use crate::Signature;
// TODO: possibly completely remove those two functions. // TODO: possibly completely remove those two functions.
// They only exist to have a simpler and smaller code snippets to test // They only exist to have a simpler and smaller code snippets to test
// basic functionalities. // basic functionalities.
@@ -157,10 +158,6 @@ 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 { pub fn get_commitment_hash(&self) -> G1Projective {
self.commitment_hash self.commitment_hash
} }
@@ -429,9 +426,9 @@ pub fn verify_partial_blind_signature(
.into() .into()
} }
/// Creates a Coconut Signature under a given secret key on a set of public attributes only. #[cfg(test)]
pub fn sign( pub fn sign(
params: &Parameters, params: &mut Parameters,
secret_key: &SecretKey, secret_key: &SecretKey,
public_attributes: &[&Attribute], public_attributes: &[&Attribute],
) -> Result<Signature> { ) -> Result<Signature> {
+8 -28
View File
@@ -151,6 +151,10 @@ impl Base58 for SecretKey {}
// TODO: perhaps change points to affine representation // TODO: perhaps change points to affine representation
// to make verification slightly more efficient? // to make verification slightly more efficient?
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(
feature = "key-zeroize",
derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)
)]
pub struct VerificationKey { pub struct VerificationKey {
// TODO add gen2 as per the paper or imply it from the fact library is using bls381? // TODO add gen2 as per the paper or imply it from the fact library is using bls381?
pub(crate) alpha: G2Projective, pub(crate) alpha: G2Projective,
@@ -407,23 +411,12 @@ impl Bytable for VerificationKey {
impl Base58 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)] #[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq, Eq, Clone))] #[cfg_attr(test, derive(PartialEq, Eq, Clone))]
#[cfg_attr(
feature = "key-zeroize",
derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)
)]
pub struct KeyPair { pub struct KeyPair {
secret_key: SecretKey, secret_key: SecretKey,
verification_key: VerificationKey, verification_key: VerificationKey,
@@ -432,12 +425,6 @@ pub struct KeyPair {
pub index: Option<SignerIndex>, 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 { impl PemStorableKeyPair for KeyPair {
type PrivatePemKey = SecretKey; type PrivatePemKey = SecretKey;
type PublicPemKey = VerificationKey; type PublicPemKey = VerificationKey;
@@ -474,13 +461,6 @@ impl KeyPair {
&self.verification_key &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> { 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 // 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() self.to_byte_vec()
+13 -31
View File
@@ -70,11 +70,6 @@ impl Signature {
&self.1 &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) { pub fn randomise(&self, params: &Parameters) -> (Signature, Scalar) {
let r = params.random_scalar(); let r = params.random_scalar();
let r_prime = params.random_scalar(); let r_prime = params.random_scalar();
@@ -196,7 +191,7 @@ impl BlindedSignature {
&self, &self,
partial_verification_key: &VerificationKey, partial_verification_key: &VerificationKey,
pedersen_commitments_openings: &[Scalar], pedersen_commitments_openings: &[Scalar],
) -> Signature { ) -> Result<Signature> {
// parse the signature // parse the signature
let h = &self.0; let h = &self.0;
let c = &self.1; let c = &self.1;
@@ -209,7 +204,7 @@ impl BlindedSignature {
let unblinded_c = c - blinding_removers; let unblinded_c = c - blinding_removers;
Signature(*h, unblinded_c) Ok(Signature(*h, unblinded_c))
} }
pub fn unblind_and_verify( pub fn unblind_and_verify(
@@ -221,7 +216,7 @@ impl BlindedSignature {
commitment_hash: &G1Projective, commitment_hash: &G1Projective,
pedersen_commitments_openings: &[Scalar], pedersen_commitments_openings: &[Scalar],
) -> Result<Signature> { ) -> Result<Signature> {
let unblinded = self.unblind(partial_verification_key, pedersen_commitments_openings); let unblinded = self.unblind(partial_verification_key, pedersen_commitments_openings)?;
unblinded.verify( unblinded.verify(
params, params,
partial_verification_key, partial_verification_key,
@@ -245,7 +240,6 @@ impl BlindedSignature {
} }
// perhaps this should take signature by reference? we'll see how it goes // perhaps this should take signature by reference? we'll see how it goes
#[derive(Clone, Copy)]
pub struct SignatureShare { pub struct SignatureShare {
signature: Signature, signature: Signature,
index: SignerIndex, index: SignerIndex,
@@ -282,9 +276,7 @@ impl SignatureShare {
mod tests { mod tests {
use super::*; use super::*;
use crate::hash_to_scalar; use crate::hash_to_scalar;
use crate::scheme::aggregation::{ use crate::scheme::aggregation::{aggregate_signatures, aggregate_verification_keys};
aggregate_signatures_and_verify, aggregate_verification_keys,
};
use crate::scheme::issuance::{blind_sign, compute_hash, prepare_blind_sign, sign}; use crate::scheme::issuance::{blind_sign, compute_hash, prepare_blind_sign, sign};
use crate::scheme::keygen::{keygen, ttp_keygen}; use crate::scheme::keygen::{keygen, ttp_keygen};
use crate::scheme::verification::{prove_bandwidth_credential, verify, verify_credential}; use crate::scheme::verification::{prove_bandwidth_credential, verify, verify_credential};
@@ -426,13 +418,13 @@ mod tests {
#[test] #[test]
fn verification_on_two_public_attributes() { fn verification_on_two_public_attributes() {
let params = Parameters::new(2).unwrap(); let mut params = Parameters::new(2).unwrap();
random_scalars_refs!(attributes, params, 2); random_scalars_refs!(attributes, params, 2);
let keypair1 = keygen(&params); let keypair1 = keygen(&params);
let keypair2 = keygen(&params); let keypair2 = keygen(&params);
let sig1 = sign(&params, keypair1.secret_key(), &attributes).unwrap(); let sig1 = sign(&mut params, keypair1.secret_key(), &attributes).unwrap();
let sig2 = sign(&params, keypair2.secret_key(), &attributes).unwrap(); let sig2 = sign(&mut params, keypair2.secret_key(), &attributes).unwrap();
assert!(verify( assert!(verify(
&params, &params,
@@ -576,14 +568,9 @@ mod tests {
attributes.extend_from_slice(&public_attributes); attributes.extend_from_slice(&public_attributes);
let aggr_vk = aggregate_verification_keys(&vks[..2], Some(&[1, 2])).unwrap(); let aggr_vk = aggregate_verification_keys(&vks[..2], Some(&[1, 2])).unwrap();
let aggr_sig = aggregate_signatures_and_verify( let aggr_sig =
&params, aggregate_signatures(&params, &aggr_vk, &attributes, &sigs[..2], Some(&[1, 2]))
&aggr_vk, .unwrap();
&attributes,
&sigs[..2],
Some(&[1, 2]),
)
.unwrap();
let theta = prove_bandwidth_credential( let theta = prove_bandwidth_credential(
&params, &params,
@@ -603,14 +590,9 @@ mod tests {
// taking different subset of keys and credentials // taking different subset of keys and credentials
let aggr_vk = aggregate_verification_keys(&vks[1..], Some(&[2, 3])).unwrap(); let aggr_vk = aggregate_verification_keys(&vks[1..], Some(&[2, 3])).unwrap();
let aggr_sig = aggregate_signatures_and_verify( let aggr_sig =
&params, aggregate_signatures(&params, &aggr_vk, &attributes, &sigs[1..], Some(&[2, 3]))
&aggr_vk, .unwrap();
&attributes,
&sigs[1..],
Some(&[2, 3]),
)
.unwrap();
let theta = prove_bandwidth_credential( let theta = prove_bandwidth_credential(
&params, &params,
-1
View File
@@ -10,7 +10,6 @@ use crate::error::{CoconutError, Result};
use crate::utils::hash_g1; use crate::utils::hash_g1;
/// System-wide parameters used for the protocol /// System-wide parameters used for the protocol
#[derive(Clone)]
pub struct Parameters { pub struct Parameters {
/// Generator of the G1 group /// Generator of the G1 group
g1: G1Affine, g1: G1Affine,
@@ -288,6 +288,7 @@ pub fn verify_credential(
} }
// Used in tests only // Used in tests only
#[cfg(test)]
pub fn verify( pub fn verify(
params: &Parameters, params: &Parameters,
verification_key: &VerificationKey, verification_key: &VerificationKey,
+2 -6
View File
@@ -75,12 +75,8 @@ pub fn theta_from_keys_and_attributes(
attributes.extend_from_slice(public_attributes); attributes.extend_from_slice(public_attributes);
// Randomize credentials and generate any cryptographic material to verify them // Randomize credentials and generate any cryptographic material to verify them
let signature = aggregate_signature_shares_and_verify( let signature =
params, aggregate_signature_shares(params, &verification_key, &attributes, &signature_shares)?;
&verification_key,
&attributes,
&signature_shares,
)?;
// Generate cryptographic material to verify them // Generate cryptographic material to verify them
let theta = prove_bandwidth_credential( let theta = prove_bandwidth_credential(
+2 -4
View File
@@ -16,9 +16,7 @@ const_format = "0.2.32"
cosmrs.workspace = true cosmrs.workspace = true
eyre = "0.6.9" eyre = "0.6.9"
futures.workspace = true futures.workspace = true
humantime = "2.1.0"
sha2 = "0.10.8" sha2 = "0.10.8"
serde = { workspace = true, features = ["derive"] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"] } sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"] }
tendermint.workspace = true tendermint.workspace = true
tendermint-rpc = { workspace = true, features = ["websocket-client", "http-client"] } tendermint-rpc = { workspace = true, features = ["websocket-client", "http-client"] }
@@ -26,13 +24,13 @@ thiserror.workspace = true
time = { workspace = true } time = { workspace = true }
tokio = { workspace = true, features = ["full"] } tokio = { workspace = true, features = ["full"] }
tokio-stream = "0.1.14" tokio-stream = "0.1.14"
tokio-util = { version = "0.7.10", features = ["rt"] } tokio-util = { version = "0.7.10", features = ["rt"]}
tracing.workspace = true tracing.workspace = true
url.workspace = true url.workspace = true
# TEMP # TEMP
#nym-bin-common = { path = "../bin-common", features = ["basic_tracing"]} nym-bin-common = { path = "../bin-common", features = ["basic_tracing"]}
[build-dependencies] [build-dependencies]
-23
View File
@@ -1,23 +0,0 @@
# 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.
+2 -75
View File
@@ -8,7 +8,6 @@ use crate::error::ScraperError;
use crate::modules::{BlockModule, MsgModule, TxModule}; use crate::modules::{BlockModule, MsgModule, TxModule};
use crate::rpc_client::RpcClient; use crate::rpc_client::RpcClient;
use crate::storage::{persist_block, ScraperStorage}; use crate::storage::{persist_block, ScraperStorage};
use crate::PruningOptions;
use futures::StreamExt; use futures::StreamExt;
use std::collections::{BTreeMap, HashSet, VecDeque}; use std::collections::{BTreeMap, HashSet, VecDeque};
use std::ops::{Add, Range}; use std::ops::{Add, Range};
@@ -19,10 +18,9 @@ use tokio::sync::Notify;
use tokio::time::{interval_at, Instant}; use tokio::time::{interval_at, Instant};
use tokio_stream::wrappers::UnboundedReceiverStream; use tokio_stream::wrappers::UnboundedReceiverStream;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use tracing::{debug, error, info, instrument, trace, warn}; use tracing::{debug, error, info, warn};
mod helpers; mod helpers;
pub(crate) mod pruning;
pub(crate) mod types; pub(crate) mod types;
const MISSING_BLOCKS_CHECK_INTERVAL: Duration = Duration::from_secs(30); const MISSING_BLOCKS_CHECK_INTERVAL: Duration = Duration::from_secs(30);
@@ -42,11 +40,9 @@ impl PendingSync {
} }
pub struct BlockProcessor { pub struct BlockProcessor {
pruning_options: PruningOptions,
cancel: CancellationToken, cancel: CancellationToken,
synced: Arc<Notify>, synced: Arc<Notify>,
last_processed_height: u32, last_processed_height: u32,
last_pruned_height: u32,
last_processed_at: Instant, last_processed_at: Instant,
pending_sync: PendingSync, pending_sync: PendingSync,
queued_blocks: BTreeMap<u32, BlockToProcess>, queued_blocks: BTreeMap<u32, BlockToProcess>,
@@ -66,7 +62,6 @@ pub struct BlockProcessor {
impl BlockProcessor { impl BlockProcessor {
pub async fn new( pub async fn new(
pruning_options: PruningOptions,
cancel: CancellationToken, cancel: CancellationToken,
synced: Arc<Notify>, synced: Arc<Notify>,
incoming: UnboundedReceiver<BlockToProcess>, incoming: UnboundedReceiver<BlockToProcess>,
@@ -75,17 +70,11 @@ impl BlockProcessor {
rpc_client: RpcClient, rpc_client: RpcClient,
) -> Result<Self, ScraperError> { ) -> Result<Self, ScraperError> {
let last_processed = storage.get_last_processed_height().await?; 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 { Ok(BlockProcessor {
pruning_options,
cancel, cancel,
synced, synced,
last_processed_height, last_processed_height: last_processed.try_into().unwrap_or_default(),
last_pruned_height,
last_processed_at: Instant::now(), last_processed_at: Instant::now(),
pending_sync: Default::default(), pending_sync: Default::default(),
queued_blocks: Default::default(), queued_blocks: Default::default(),
@@ -142,17 +131,12 @@ impl BlockProcessor {
} }
} }
let commit_start = Instant::now();
tx.commit() tx.commit()
.await .await
.map_err(|source| ScraperError::StorageTxCommitFailure { source })?; .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_height = full_info.block.header.height.value() as u32;
self.last_processed_at = Instant::now(); self.last_processed_at = Instant::now();
if let Err(err) = self.maybe_prune_storage().await {
error!("failed to prune the storage: {err}");
}
Ok(()) Ok(())
} }
@@ -226,61 +210,6 @@ impl BlockProcessor {
Ok(()) 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) { async fn next_incoming(&mut self, block: BlockToProcess) {
let height = block.height; let height = block.height;
@@ -350,8 +279,6 @@ impl BlockProcessor {
async fn startup_resync(&mut self) -> Result<(), ScraperError> { async fn startup_resync(&mut self) -> Result<(), ScraperError> {
assert!(self.pending_sync.is_empty()); assert!(self.pending_sync.is_empty());
self.maybe_prune_storage().await?;
let latest_block = self.rpc_client.current_block_height().await? as u32; let latest_block = self.rpc_client.current_block_height().await? as u32;
if latest_block > self.last_processed_height && self.last_processed_height != 0 { if latest_block > self.last_processed_height && self.last_processed_height != 0 {
let request_range = self.last_processed_height + 1..latest_block + 1; let request_range = self.last_processed_height + 1..latest_block + 1;
@@ -1,122 +0,0 @@
// 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 conveniences 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(),
}
}
}
-12
View File
@@ -1,9 +1,6 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::block_processor::pruning::{
EVERYTHING_PRUNING_INTERVAL, EVERYTHING_PRUNING_KEEP_RECENT,
};
use tendermint::Hash; use tendermint::Hash;
use thiserror::Error; use thiserror::Error;
use tokio::sync::mpsc::error::SendError; use tokio::sync::mpsc::error::SendError;
@@ -125,15 +122,6 @@ pub enum ScraperError {
"could not find validator information for {address}; the validator has signed a commit" "could not find validator information for {address}; the validator has signed a commit"
)] )]
MissingValidatorInfoCommitted { address: String }, 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 { impl<T> From<SendError<T>> for ScraperError {
-1
View File
@@ -14,7 +14,6 @@ pub(crate) mod rpc_client;
pub(crate) mod scraper; pub(crate) mod scraper;
pub mod storage; pub mod storage;
pub use block_processor::pruning::{PruningOptions, PruningStrategy};
pub use modules::{BlockModule, MsgModule, TxModule}; pub use modules::{BlockModule, MsgModule, TxModule};
pub use scraper::{Config, NyxdScraper}; pub use scraper::{Config, NyxdScraper};
pub use storage::models; pub use storage::models;
-6
View File
@@ -8,7 +8,6 @@ use crate::modules::{BlockModule, MsgModule, TxModule};
use crate::rpc_client::RpcClient; use crate::rpc_client::RpcClient;
use crate::scraper::subscriber::ChainSubscriber; use crate::scraper::subscriber::ChainSubscriber;
use crate::storage::ScraperStorage; use crate::storage::ScraperStorage;
use crate::PruningOptions;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc::{channel, unbounded_channel}; use tokio::sync::mpsc::{channel, unbounded_channel};
@@ -28,8 +27,6 @@ pub struct Config {
pub rpc_url: Url, pub rpc_url: Url,
pub database_path: PathBuf, pub database_path: PathBuf,
pub pruning_options: PruningOptions,
} }
pub struct NyxdScraperBuilder { pub struct NyxdScraperBuilder {
@@ -57,7 +54,6 @@ impl NyxdScraperBuilder {
processing_tx.clone(), processing_tx.clone(),
); );
let mut block_processor = BlockProcessor::new( let mut block_processor = BlockProcessor::new(
scraper.config.pruning_options,
scraper.cancel_token.clone(), scraper.cancel_token.clone(),
scraper.startup_sync.clone(), scraper.startup_sync.clone(),
processing_rx, processing_rx,
@@ -123,7 +119,6 @@ impl NyxdScraper {
} }
pub async fn new(config: Config) -> Result<Self, ScraperError> { pub async fn new(config: Config) -> Result<Self, ScraperError> {
config.pruning_options.validate()?;
let storage = ScraperStorage::init(&config.database_path).await?; let storage = ScraperStorage::init(&config.database_path).await?;
Ok(NyxdScraper { Ok(NyxdScraper {
@@ -165,7 +160,6 @@ impl NyxdScraper {
processing_tx.clone(), processing_tx.clone(),
); );
let block_processor = BlockProcessor::new( let block_processor = BlockProcessor::new(
self.config.pruning_options,
self.cancel_token.clone(), self.cancel_token.clone(),
self.startup_sync.clone(), self.startup_sync.clone(),
processing_rx, processing_rx,
+11 -191
View File
@@ -1,11 +1,9 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::storage::log_db_operation_time;
use crate::storage::models::{CommitSignature, Validator}; use crate::storage::models::{CommitSignature, Validator};
use sqlx::types::time::OffsetDateTime; use sqlx::types::time::OffsetDateTime;
use sqlx::{Executor, Sqlite}; use sqlx::{Executor, Sqlite};
use tokio::time::Instant;
use tracing::{instrument, trace}; use tracing::{instrument, trace};
#[derive(Clone)] #[derive(Clone)]
@@ -27,36 +25,10 @@ impl StorageManager {
Ok(()) 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( pub(crate) async fn get_first_block_height_after(
&self, &self,
time: OffsetDateTime, time: OffsetDateTime,
) -> Result<Option<i64>, sqlx::Error> { ) -> Result<Option<i64>, sqlx::Error> {
trace!("get_first_block_height_after");
let start = Instant::now();
let maybe_record = sqlx::query!( let maybe_record = sqlx::query!(
r#" r#"
SELECT height SELECT height
@@ -69,7 +41,6 @@ impl StorageManager {
) )
.fetch_optional(&self.connection_pool) .fetch_optional(&self.connection_pool)
.await?; .await?;
log_db_operation_time("get_first_block_height_after", start);
if let Some(row) = maybe_record { if let Some(row) = maybe_record {
Ok(row.height) Ok(row.height)
@@ -82,9 +53,6 @@ impl StorageManager {
&self, &self,
time: OffsetDateTime, time: OffsetDateTime,
) -> Result<Option<i64>, sqlx::Error> { ) -> Result<Option<i64>, sqlx::Error> {
trace!("get_last_block_height_before");
let start = Instant::now();
let maybe_record = sqlx::query!( let maybe_record = sqlx::query!(
r#" r#"
SELECT height SELECT height
@@ -97,7 +65,6 @@ impl StorageManager {
) )
.fetch_optional(&self.connection_pool) .fetch_optional(&self.connection_pool)
.await?; .await?;
log_db_operation_time("get_last_block_height_before", start);
if let Some(row) = maybe_record { if let Some(row) = maybe_record {
Ok(row.height) Ok(row.height)
@@ -112,9 +79,6 @@ impl StorageManager {
start_height: i64, start_height: i64,
end_height: i64, end_height: i64,
) -> Result<i32, sqlx::Error> { ) -> Result<i32, sqlx::Error> {
trace!("get_signed_between");
let start = Instant::now();
let count = sqlx::query!( let count = sqlx::query!(
r#" r#"
SELECT COUNT(*) as count FROM pre_commit SELECT COUNT(*) as count FROM pre_commit
@@ -130,7 +94,6 @@ impl StorageManager {
.fetch_one(&self.connection_pool) .fetch_one(&self.connection_pool)
.await? .await?
.count; .count;
log_db_operation_time("get_signed_between", start);
Ok(count) Ok(count)
} }
@@ -140,10 +103,7 @@ impl StorageManager {
consensus_address: &str, consensus_address: &str,
height: i64, height: i64,
) -> Result<Option<CommitSignature>, sqlx::Error> { ) -> Result<Option<CommitSignature>, sqlx::Error> {
trace!("get_precommit"); sqlx::query_as(
let start = Instant::now();
let res = sqlx::query_as(
r#" r#"
SELECT * FROM pre_commit SELECT * FROM pre_commit
WHERE validator_address = ? WHERE validator_address = ?
@@ -153,20 +113,14 @@ impl StorageManager {
.bind(consensus_address) .bind(consensus_address)
.bind(height) .bind(height)
.fetch_optional(&self.connection_pool) .fetch_optional(&self.connection_pool)
.await?; .await
log_db_operation_time("get_precommit", start);
Ok(res)
} }
pub(crate) async fn get_block_validators( pub(crate) async fn get_block_validators(
&self, &self,
height: i64, height: i64,
) -> Result<Vec<Validator>, sqlx::Error> { ) -> Result<Vec<Validator>, sqlx::Error> {
trace!("get_block_validators"); sqlx::query_as!(
let start = Instant::now();
let res = sqlx::query_as!(
Validator, Validator,
r#" r#"
SELECT * FROM validator SELECT * FROM validator
@@ -179,28 +133,16 @@ impl StorageManager {
height height
) )
.fetch_all(&self.connection_pool) .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> { pub(crate) async fn get_validators(&self) -> Result<Vec<Validator>, sqlx::Error> {
trace!("get_validators"); sqlx::query_as("SELECT * FROM validator")
let start = Instant::now();
let res = sqlx::query_as("SELECT * FROM validator")
.fetch_all(&self.connection_pool) .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> { 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!( let maybe_record = sqlx::query!(
r#" r#"
SELECT last_processed_height FROM metadata SELECT last_processed_height FROM metadata
@@ -208,7 +150,6 @@ impl StorageManager {
) )
.fetch_optional(&self.connection_pool) .fetch_optional(&self.connection_pool)
.await?; .await?;
log_db_operation_time("get_last_processed_height", start);
if let Some(row) = maybe_record { if let Some(row) = maybe_record {
Ok(row.last_processed_height) Ok(row.last_processed_height)
@@ -216,27 +157,6 @@ impl StorageManager {
Ok(-1) 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 // make those generic over executor so that they could be performed over connection pool and a tx
@@ -250,8 +170,7 @@ pub(crate) async fn insert_validator<'a, E>(
where where
E: Executor<'a, Database = Sqlite>, E: Executor<'a, Database = Sqlite>,
{ {
trace!("insert_validator"); trace!("insert validator");
let start = Instant::now();
sqlx::query!( sqlx::query!(
r#" r#"
@@ -264,7 +183,6 @@ where
) )
.execute(executor) .execute(executor)
.await?; .await?;
log_db_operation_time("insert_validator", start);
Ok(()) Ok(())
} }
@@ -282,8 +200,7 @@ pub(crate) async fn insert_block<'a, E>(
where where
E: Executor<'a, Database = Sqlite>, E: Executor<'a, Database = Sqlite>,
{ {
trace!("insert_block"); trace!("insert block");
let start = Instant::now();
sqlx::query!( sqlx::query!(
r#" r#"
@@ -300,7 +217,6 @@ where
) )
.execute(executor) .execute(executor)
.await?; .await?;
log_db_operation_time("insert_block", start);
Ok(()) Ok(())
} }
@@ -317,8 +233,7 @@ pub(crate) async fn insert_precommit<'a, E>(
where where
E: Executor<'a, Database = Sqlite>, E: Executor<'a, Database = Sqlite>,
{ {
trace!("insert_precommit"); trace!("insert precommit");
let start = Instant::now();
sqlx::query!( sqlx::query!(
r#" r#"
@@ -334,7 +249,6 @@ where
) )
.execute(executor) .execute(executor)
.await?; .await?;
log_db_operation_time("insert_precommit", start);
Ok(()) Ok(())
} }
@@ -356,8 +270,7 @@ pub(crate) async fn insert_transaction<'a, E>(
where where
E: Executor<'a, Database = Sqlite>, E: Executor<'a, Database = Sqlite>,
{ {
trace!("insert_transaction"); trace!("insert transaction");
let start = Instant::now();
sqlx::query!( sqlx::query!(
r#" r#"
@@ -385,7 +298,6 @@ where
) )
.execute(executor) .execute(executor)
.await?; .await?;
log_db_operation_time("insert_transaction", start);
Ok(()) Ok(())
} }
@@ -401,8 +313,7 @@ pub(crate) async fn insert_message<'a, E>(
where where
E: Executor<'a, Database = Sqlite>, E: Executor<'a, Database = Sqlite>,
{ {
trace!("insert_message"); trace!("insert message");
let start = Instant::now();
sqlx::query!( sqlx::query!(
r#" r#"
@@ -419,7 +330,6 @@ where
) )
.execute(executor) .execute(executor)
.await?; .await?;
log_db_operation_time("insert_message", start);
Ok(()) Ok(())
} }
@@ -433,100 +343,10 @@ where
E: Executor<'a, Database = Sqlite>, E: Executor<'a, Database = Sqlite>,
{ {
trace!("update_last_processed"); trace!("update_last_processed");
let start = Instant::now();
sqlx::query!("UPDATE metadata SET last_processed_height = ?", height) sqlx::query!("UPDATE metadata SET last_processed_height = ?", height)
.execute(executor) .execute(executor)
.await?; .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(()) Ok(())
} }
+1 -50
View File
@@ -5,8 +5,7 @@ use crate::block_processor::types::{FullBlockInformation, ParsedTransactionRespo
use crate::error::ScraperError; use crate::error::ScraperError;
use crate::storage::manager::{ use crate::storage::manager::{
insert_block, insert_message, insert_precommit, insert_transaction, insert_validator, insert_block, insert_message, insert_precommit, insert_transaction, insert_validator,
prune_blocks, prune_messages, prune_pre_commits, prune_transactions, update_last_processed, update_last_processed, StorageManager,
update_last_pruned, StorageManager,
}; };
use crate::storage::models::{CommitSignature, Validator}; use crate::storage::models::{CommitSignature, Validator};
use sqlx::types::time::OffsetDateTime; use sqlx::types::time::OffsetDateTime;
@@ -16,7 +15,6 @@ use std::path::Path;
use tendermint::block::{Commit, CommitSig}; use tendermint::block::{Commit, CommitSig};
use tendermint::Block; use tendermint::Block;
use tendermint_rpc::endpoint::validators; use tendermint_rpc::endpoint::validators;
use tokio::time::Instant;
use tracing::{debug, error, info, instrument, trace, warn}; use tracing::{debug, error, info, instrument, trace, warn};
mod helpers; mod helpers;
@@ -30,19 +28,6 @@ pub struct ScraperStorage {
pub(crate) manager: StorageManager, 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 { impl ScraperStorage {
#[instrument] #[instrument]
pub async fn init<P: AsRef<Path> + Debug>(database_path: P) -> Result<Self, ScraperError> { pub async fn init<P: AsRef<Path> + Debug>(database_path: P) -> Result<Self, ScraperError> {
@@ -80,32 +65,6 @@ impl ScraperStorage {
Ok(storage) 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)] #[instrument(skip_all)]
pub async fn begin_processing_tx(&self) -> Result<StorageTransaction, ScraperError> { pub async fn begin_processing_tx(&self) -> Result<StorageTransaction, ScraperError> {
debug!("starting storage tx"); debug!("starting storage tx");
@@ -116,10 +75,6 @@ impl ScraperStorage {
.map_err(|source| ScraperError::StorageTxBeginFailure { source }) .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( pub async fn get_first_block_height_after(
&self, &self,
time: OffsetDateTime, time: OffsetDateTime,
@@ -200,10 +155,6 @@ impl ScraperStorage {
pub async fn get_last_processed_height(&self) -> Result<i64, ScraperError> { pub async fn get_last_processed_height(&self) -> Result<i64, ScraperError> {
Ok(self.manager.get_last_processed_height().await?) 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( pub async fn persist_block(
-4
View File
@@ -474,10 +474,6 @@ impl TaskClient {
self.mode.set_should_not_signal_on_drop(); self.mode.set_should_not_signal_on_drop();
} }
pub fn disarm(&mut self) {
self.mark_as_success();
}
pub fn send_we_stopped(&mut self, err: SentError) { pub fn send_we_stopped(&mut self, err: SentError) {
if self.mode.is_dummy() { if self.mode.is_dummy() {
return; return;
+2 -2
View File
@@ -205,10 +205,10 @@ impl<'a> TryFrom<&'a DescribedGateway> for Node {
clients_ws_port: self_described.mixnet_websockets.ws_port, clients_ws_port: self_described.mixnet_websockets.ws_port,
clients_wss_port: self_described.mixnet_websockets.wss_port, clients_wss_port: self_described.mixnet_websockets.wss_port,
identity_key: identity::PublicKey::from_base58_string( identity_key: identity::PublicKey::from_base58_string(
&self_described.host_information.keys.ed25519, &self_described.host_information.keys.ed25519_identity,
)?, )?,
sphinx_key: encryption::PublicKey::from_base58_string( sphinx_key: encryption::PublicKey::from_base58_string(
&self_described.host_information.keys.x25519, &self_described.host_information.keys.x25519_sphinx,
)?, )?,
version: self_described version: self_described
.build_information .build_information
+1 -1
View File
@@ -159,7 +159,7 @@ impl TunDevice {
"add", "add",
&format!("{}/{}", ipv6, netmaskv6), &format!("{}/{}", ipv6, netmaskv6),
"dev", "dev",
(tun.name()), &tun.name(),
]) ])
.output()?; .output()?;
Ok(tun) Ok(tun)
+1 -3
View File
@@ -50,7 +50,7 @@ pub struct DelegationWithEverything {
pub accumulated_by_delegates: Option<DecCoin>, pub accumulated_by_delegates: Option<DecCoin>,
pub accumulated_by_operator: Option<DecCoin>, pub accumulated_by_operator: Option<DecCoin>,
pub block_height: u64, pub block_height: u64,
pub delegated_on_iso_datetime: Option<String>, pub delegated_on_iso_datetime: String,
pub cost_params: Option<MixNodeCostParams>, pub cost_params: Option<MixNodeCostParams>,
pub avg_uptime_percent: Option<u8>, pub avg_uptime_percent: Option<u8>,
@@ -60,8 +60,6 @@ pub struct DelegationWithEverything {
pub uses_vesting_contract_tokens: bool, pub uses_vesting_contract_tokens: bool,
pub unclaimed_rewards: Option<DecCoin>, pub unclaimed_rewards: Option<DecCoin>,
pub errors: Option<String>,
// DEPRECATED, IF POSSIBLE TRY TO DISCONTINUE USE OF IT! // DEPRECATED, IF POSSIBLE TRY TO DISCONTINUE USE OF IT!
pub pending_events: Vec<DelegationEvent>, pub pending_events: Vec<DelegationEvent>,
pub mixnode_is_unbonding: Option<bool>, pub mixnode_is_unbonding: Option<bool>,
-22
View File
@@ -171,25 +171,3 @@ impl fmt::Display for GatewayIpPacketRouterDetails {
writeln!(f, "\taddress: {}", self.address) writeln!(f, "\taddress: {}", self.address)
} }
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct GatewayWireguardDetails {
pub enabled: bool,
pub announced_port: u16,
pub private_network_prefix: u8,
}
impl fmt::Display for GatewayWireguardDetails {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "wireguard:")?;
writeln!(f, "\tenabled: {}", self.enabled)?;
writeln!(f, "\tannounced_port: {}", self.announced_port)?;
writeln!(
f,
"\tprivate_network_prefix: {}",
self.private_network_prefix
)
}
}
+1 -7
View File
@@ -37,11 +37,5 @@ wasm-utils = { path = "../utils" }
wasm-storage = { path = "../storage" } wasm-storage = { path = "../storage" }
# The `console_error_panic_hook` crate provides better debugging of panics by
# 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 }
[features] [features]
default = ["console_error_panic_hook"] default = ["wasm-utils/console_error_panic_hook"]
+7
View File
@@ -17,6 +17,12 @@ gloo-utils = { workspace = true }
gloo-net = { version = "0.3.1", features = ["websocket"], optional = true } gloo-net = { version = "0.3.1", features = ["websocket"], optional = true }
#gloo-net = { path = "../../../../gloo/crates/net", features = ["websocket"], optional = true } #gloo-net = { path = "../../../../gloo/crates/net", features = ["websocket"], optional = true }
# The `console_error_panic_hook` crate provides better debugging of panics by
# 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 = { workspace = true, optional = true }
# we don't want entire tokio-tungstenite, tungstenite itself is just fine - we just want message and error enums # we don't want entire tokio-tungstenite, tungstenite itself is just fine - we just want message and error enums
[dependencies.tungstenite] [dependencies.tungstenite]
workspace = true workspace = true
@@ -29,6 +35,7 @@ optional = true
[features] [features]
default = ["sleep"] default = ["sleep"]
panic-hook = ["console_error_panic_hook"]
sleep = ["web-sys", "web-sys/Window"] sleep = ["web-sys", "web-sys/Window"]
websocket = [ websocket = [
"getrandom", "getrandom",
+12
View File
@@ -70,3 +70,15 @@ pub async fn sleep(ms: i32) -> Result<(), wasm_bindgen::JsValue> {
js_fut.await?; js_fut.await?;
Ok(()) Ok(())
} }
#[wasm_bindgen]
#[cfg(feature = "panic-hook")]
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
console_error_panic_hook::set_once();
}
-7
View File
@@ -6,7 +6,6 @@ use futures::{Sink, Stream};
use gloo_net::websocket::futures::WebSocket; use gloo_net::websocket::futures::WebSocket;
use gloo_net::websocket::{Message, WebSocketError}; use gloo_net::websocket::{Message, WebSocketError};
use gloo_utils::errors::JsError; use gloo_utils::errors::JsError;
use std::fmt::{self, Formatter};
use std::io; use std::io;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@@ -78,12 +77,6 @@ impl Stream for JSWebsocket {
} }
} }
impl fmt::Debug for JSWebsocket {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "JSWebSocket")
}
}
impl Sink<WsMessage> for JSWebsocket { impl Sink<WsMessage> for JSWebsocket {
type Error = WsError; type Error = WsError;
-2
View File
@@ -17,9 +17,7 @@ log = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true } thiserror = { workspace = true }
nym-config = { path = "../config" }
nym-crypto = { path = "../crypto", features = ["asymmetric"] } nym-crypto = { path = "../crypto", features = ["asymmetric"] }
nym-network-defaults = { path = "../network-defaults" }
# feature-specific dependencies: # feature-specific dependencies:
-23
View File
@@ -1,23 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::net::{IpAddr, SocketAddr};
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct Config {
/// Socket address this node will use for binding its wireguard interface.
/// default: `0.0.0.0:51822`
pub bind_address: SocketAddr,
/// Private IP address of the wireguard gateway.
/// default: `10.1.0.1`
pub private_ip: IpAddr,
/// Port announced to external clients wishing to connect to the wireguard interface.
/// Useful in the instances where the node is behind a proxy.
pub announced_port: u16,
/// The prefix denoting the maximum number of the clients that can be connected via Wireguard.
/// The maximum value for IPv4 is 32 and for IPv6 is 128
pub private_network_prefix: u8,
}
+1 -37
View File
@@ -1,51 +1,15 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use dashmap::DashMap;
use nym_crypto::asymmetric::encryption::KeyPair;
use std::sync::Arc;
pub mod config;
pub mod error; pub mod error;
pub mod public_key; pub mod public_key;
pub mod registration; pub mod registration;
pub use config::Config;
pub use error::Error; pub use error::Error;
pub use public_key::PeerPublicKey; pub use public_key::PeerPublicKey;
pub use registration::{ pub use registration::{
ClientMac, ClientMessage, ClientRegistrationResponse, GatewayClient, GatewayClientRegistry, ClientMac, ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, Nonce,
InitMessage, Nonce,
}; };
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
pub use registration::HmacSha256; pub use registration::HmacSha256;
#[derive(Clone)]
pub struct WireguardGatewayData {
config: Config,
keypair: Arc<KeyPair>,
client_registry: Arc<GatewayClientRegistry>,
}
impl WireguardGatewayData {
pub fn new(config: Config, keypair: Arc<KeyPair>) -> Self {
WireguardGatewayData {
config,
keypair,
client_registry: Arc::new(DashMap::default()),
}
}
pub fn config(&self) -> Config {
self.config
}
pub fn keypair(&self) -> &Arc<KeyPair> {
&self.keypair
}
pub fn client_registry(&self) -> &Arc<GatewayClientRegistry> {
&self.client_registry
}
}
-4
View File
@@ -24,10 +24,6 @@ impl PeerPublicKey {
pub fn as_bytes(&self) -> &[u8] { pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes() self.0.as_bytes()
} }
pub fn inner(&self) -> PublicKey {
self.0
}
} }
impl fmt::Display for PeerPublicKey { impl fmt::Display for PeerPublicKey {
+4 -2
View File
@@ -12,7 +12,7 @@ use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
use hmac::{Hmac, Mac}; use hmac::{Hmac, Mac};
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
use nym_crypto::asymmetric::encryption::PrivateKey; use nym_crypto::asymmetric::encryption::{PrivateKey, PublicKey};
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
use sha2::Sha256; use sha2::Sha256;
@@ -87,7 +87,7 @@ impl GatewayClient {
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
pub fn new( pub fn new(
local_secret: &PrivateKey, local_secret: &PrivateKey,
remote_public: x25519_dalek::PublicKey, remote_public: PublicKey,
private_ip: IpAddr, private_ip: IpAddr,
nonce: u64, nonce: u64,
) -> Self { ) -> Self {
@@ -96,6 +96,8 @@ impl GatewayClient {
let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes()); let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes());
let local_public: x25519_dalek::PublicKey = (&static_secret).into(); let local_public: x25519_dalek::PublicKey = (&static_secret).into();
let remote_public = x25519_dalek::PublicKey::from(remote_public.to_bytes());
let dh = static_secret.diffie_hellman(&remote_public); let dh = static_secret.diffie_hellman(&remote_public);
// TODO: change that to use our nym_crypto::hmac module instead // TODO: change that to use our nym_crypto::hmac module instead
+18 -15
View File
@@ -3,37 +3,40 @@
// #![warn(clippy::expect_used)] // #![warn(clippy::expect_used)]
// #![warn(clippy::unwrap_used)] // #![warn(clippy::unwrap_used)]
pub mod setup;
/// Start wireguard device /// Start wireguard device
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub async fn start_wireguard( pub async fn start_wireguard(
mut task_client: nym_task::TaskClient, mut task_client: nym_task::TaskClient,
wireguard_data: std::sync::Arc<nym_wireguard_types::WireguardGatewayData>, _gateway_client_registry: std::sync::Arc<
nym_wireguard_types::registration::GatewayClientRegistry,
>,
) -> Result<defguard_wireguard_rs::WGApi, Box<dyn std::error::Error + Send + Sync + 'static>> { ) -> Result<defguard_wireguard_rs::WGApi, Box<dyn std::error::Error + Send + Sync + 'static>> {
use base64::{prelude::BASE64_STANDARD, Engine}; use crate::setup::{peer_allowed_ips, peer_static_public_key, PRIVATE_KEY};
use defguard_wireguard_rs::{ use defguard_wireguard_rs::{
host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi, host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi,
}; };
use nym_network_defaults::{WG_PORT, WG_TUN_DEVICE_ADDRESS};
let mut peers = vec![];
for peer_client in wireguard_data.client_registry().iter() {
let mut peer = Peer::new(Key::new(peer_client.pub_key.to_bytes()));
let peer_ip_mask = IpAddrMask::new(peer_client.private_ip, 32);
peer.set_allowed_ips(vec![peer_ip_mask]);
peers.push(peer);
}
let ifname = String::from("wg0"); let ifname = String::from("wg0");
let wgapi = WGApi::new(ifname.clone(), false)?; let wgapi = WGApi::new(ifname.clone(), false)?;
wgapi.create_interface()?; wgapi.create_interface()?;
let interface_config = InterfaceConfiguration { let interface_config = InterfaceConfiguration {
name: ifname.clone(), name: ifname.clone(),
prvkey: BASE64_STANDARD.encode(wireguard_data.keypair().private_key().to_bytes()), prvkey: PRIVATE_KEY.to_string(),
address: wireguard_data.config().private_ip.to_string(), address: WG_TUN_DEVICE_ADDRESS.to_string(),
port: wireguard_data.config().announced_port as u32, port: WG_PORT as u32,
peers, peers: vec![],
}; };
wgapi.configure_interface(&interface_config)?; wgapi.configure_interface(&interface_config)?;
// wgapi.configure_peer_routing(&peers)?; let peer = peer_static_public_key();
let mut peer = Peer::new(Key::new(peer.to_bytes()));
let peer_ip = peer_allowed_ips();
let peer_ip_mask = IpAddrMask::new(peer_ip.network_address(), peer_ip.netmask());
peer.set_allowed_ips(vec![peer_ip_mask]);
wgapi.configure_peer(&peer)?;
wgapi.configure_peer_routing(&[peer.clone()])?;
tokio::spawn(async move { task_client.recv().await }); tokio::spawn(async move { task_client.recv().await });
+56
View File
@@ -0,0 +1,56 @@
use std::net::IpAddr;
use base64::{engine::general_purpose, Engine as _};
use log::info;
// The wireguard UDP listener
pub const WG_ADDRESS: &str = "0.0.0.0";
// The private key of the listener
// Corresponding public key: "WM8s8bYegwMa0TJ+xIwhk+dImk2IpDUKslDBCZPizlE="
pub(crate) const PRIVATE_KEY: &str = "AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=";
// The AllowedIPs for the connected peer, which is one a single IP and the same as the IP that the
// peer has configured on their side.
const ALLOWED_IPS: &str = "10.1.0.2";
fn decode_base64_key(base64_key: &str) -> [u8; 32] {
general_purpose::STANDARD
.decode(base64_key)
.unwrap()
.try_into()
.unwrap()
}
pub fn server_static_private_key() -> x25519_dalek::StaticSecret {
// TODO: this is a temporary solution for development
let static_private_bytes: [u8; 32] = decode_base64_key(PRIVATE_KEY);
let static_private = x25519_dalek::StaticSecret::from(static_private_bytes);
let static_public = x25519_dalek::PublicKey::from(&static_private);
info!(
"wg public key: {}",
general_purpose::STANDARD.encode(static_public)
);
static_private
}
pub fn peer_static_public_key() -> x25519_dalek::PublicKey {
// A single static public key is used during development
// Read from NYM_PEER_PUBLIC_KEY env variable
let peer = std::env::var("NYM_PEER_PUBLIC_KEY").expect("NYM_PEER_PUBLIC_KEY must be set");
let peer_static_public_bytes: [u8; 32] = decode_base64_key(&peer);
let peer_static_public = x25519_dalek::PublicKey::from(peer_static_public_bytes);
info!(
"Adding wg peer public key: {}",
general_purpose::STANDARD.encode(peer_static_public)
);
peer_static_public
}
pub fn peer_allowed_ips() -> ip_network::IpNetwork {
let key: IpAddr = ALLOWED_IPS.parse().unwrap();
let cidr = 32u8;
ip_network::IpNetwork::new_truncate(key, cidr).unwrap()
}
-1
View File
@@ -26,7 +26,6 @@ else
echo "cleaning old book" echo "cleaning old book"
rm -rf ./book/ rm -rf ./book/
# build book # build book
# mdbook test || true
mdbook build mdbook build
# check for destination, if ! then mkdir & check again else echo thumbs up # check for destination, if ! then mkdir & check again else echo thumbs up
if [ ! -d ../../dist/docs/$i ]; then if [ ! -d ../../dist/docs/$i ]; then
+1 -1
View File
@@ -24,7 +24,7 @@ turn-off = false
[preprocessor.admonish] [preprocessor.admonish]
command = "mdbook-admonish" command = "mdbook-admonish"
assets_version = "3.0.2" # do not edit: managed by `mdbook-admonish install` assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
# https://gitlab.com/tglman/mdbook-variables/ # https://gitlab.com/tglman/mdbook-variables/
[preprocessor.variables.variables] [preprocessor.variables.variables]
@@ -1,4 +1,20 @@
@charset "UTF-8"; @charset "UTF-8";
:root {
--md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
--md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
--md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
--md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
--md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
--md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
--md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
--md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
--md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
--md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
--md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
--md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
--md-details-icon: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
}
:is(.admonition) { :is(.admonition) {
display: flow-root; display: flow-root;
margin: 1.5625em 0; margin: 1.5625em 0;
@@ -55,8 +71,6 @@ a.admonition-anchor-link::before {
padding-inline: 4.4rem 1.2rem; padding-inline: 4.4rem 1.2rem;
font-weight: 700; font-weight: 700;
background-color: rgba(68, 138, 255, 0.1); background-color: rgba(68, 138, 255, 0.1);
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
display: flex; display: flex;
} }
:is(.admonition-title, summary.admonition-title) p { :is(.admonition-title, summary.admonition-title) p {
@@ -72,8 +86,6 @@ html :is(.admonition-title, summary.admonition-title):last-child {
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
background-color: #448aff; background-color: #448aff;
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>'); mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
-webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>'); -webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
mask-repeat: no-repeat; mask-repeat: no-repeat;
@@ -107,25 +119,6 @@ details[open].admonition > summary.admonition-title::after {
transform: rotate(90deg); transform: rotate(90deg);
} }
:root {
--md-details-icon: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
}
:root {
--md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
--md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
--md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
--md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
--md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
--md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
--md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
--md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
--md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
--md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
--md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
--md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
}
:is(.admonition):is(.admonish-note) { :is(.admonition):is(.admonish-note) {
border-color: #448aff; border-color: #448aff;
} }
+7 -3
View File
@@ -20,15 +20,19 @@
# User Manuals # User Manuals
- [NymVPN alpha](nymvpn/intro.md) - [NymVPN alpha](nymvpn/intro.md)
- [GUI](nymvpn/gui.md)
- [Linux](nymvpn/gui-linux.md)
- [MacOS](nymvpn/gui-mac.md)
- [CLI](nymvpn/cli.md) - [CLI](nymvpn/cli.md)
- [Linux](nymvpn/cli-linux.md)
<!-- OUTDATED STUFF: - [MacOS](nymvpn/cli-mac.md)
- [Troubleshooting](nymvpn/troubleshooting.md)
- [NymVPN FAQ](nymvpn/faq.md)
- [NymConnect X Monero](tutorials/monero.md) - [NymConnect X Monero](tutorials/monero.md)
- [NymConnect X Matrix](tutorials/matrix.md) - [NymConnect X Matrix](tutorials/matrix.md)
- [NymConnect X Telegram](tutorials/telegram.md) - [NymConnect X Telegram](tutorials/telegram.md)
- [NymConnect X Electrum](tutorials/electrum.md) - [NymConnect X Electrum](tutorials/electrum.md)
- [NymConnect X Firo wallet](tutorials/firo.md) - [NymConnect X Firo wallet](tutorials/firo.md)
-->
# Code Examples # Code Examples
@@ -1,4 +1,4 @@
# NymVPN alpha CLI Guide # NymVPN alpha CLI: Guide for GNU/Linux
```admonish info ```admonish info
NymVPN is an experimental software and it's for testing purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results. NymVPN is an experimental software and it's for testing purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
@@ -8,7 +8,7 @@ NymVPN is an experimental software and it's for testing purposes only. All users
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets. > Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
1. Open Github [releases page]({{nym_vpn_releases}}) and download the CLI latest binary for your system 1. Open Github [releases page]({{nym_vpn_releases}}) and download the binary for Debian based Linux
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example: 2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
```sh ```sh
@@ -25,12 +25,17 @@ tar -xvf <BINARY>.tar.gz
# tar -xvf nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_x86_64.tar.gz # tar -xvf nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_x86_64.tar.gz
``` ```
4. Make executable: 4. Make executable by running:
```sh ```sh
# make sure you are in the right sub-directory # make sure you are in the right sub-directory
chmod u+x ./nym-vpn-cli chmod u+x ./nym-vpn-cli
``` ```
5. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries by running:
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
```
## Run NymVPN ## Run NymVPN
**For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds. **For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds.
@@ -42,7 +47,7 @@ Make sure your terminal is open in the same directory as your `nym-vpn-cli` bina
```sh ```sh
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WIREGUARD_IP> sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WIREGUARD_IP>
``` ```
3. To choose different Gateways, visit [explorer.nymtech.net/network-components/gateways](https://explorer.nymtech.net/network-components/gateways) and copy-paste an identity key of your choice 3. To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways) and pick one
4. See all possibilities in [command explanation](#cli-commands-and-options) section below 4. See all possibilities in [command explanation](#cli-commands-and-options) section below
In case of errors, see [troubleshooting section](troubleshooting.md). In case of errors, see [troubleshooting section](troubleshooting.md).
@@ -51,16 +56,16 @@ In case of errors, see [troubleshooting section](troubleshooting.md).
The basic syntax of `nym-vpn-cli` is: The basic syntax of `nym-vpn-cli` is:
```sh ```sh
sudo ./nym-vpn-cli <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>> sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
``` ```
* To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://explorer.nymtech.net/network-components/gateways) * To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
* To see all possibilities run with `--help` flag: * To see all possibilities run with `--help` flag:
```sh ```sh
./nym-vpn-cli --help ./nym-vpn-cli --help
``` ```
~~~admonish example collapsible=true title="Console output" ~~~admonish example collapsible=true title="Console output"
```sh ```sh
Usage: nym-vpn-cli [OPTIONS] <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>> Usage: nym-vpn-cli [OPTIONS]
Options: Options:
-c, --config-env-file <CONFIG_ENV_FILE> -c, --config-env-file <CONFIG_ENV_FILE>
@@ -71,13 +76,11 @@ Options:
Mixnet public ID of the entry gateway Mixnet public ID of the entry gateway
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY> --entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
Auto-select entry gateway by country ISO Auto-select entry gateway by country ISO
--entry-gateway-low-latency
Auto-select entry gateway by latency
--exit-router-address <EXIT_ROUTER_ADDRESS> --exit-router-address <EXIT_ROUTER_ADDRESS>
Mixnet recipient address Mixnet recipient address
--exit-gateway-id <EXIT_GATEWAY_ID> --exit-gateway-id <EXIT_GATEWAY_ID>
--exit-gateway-country <EXIT_GATEWAY_COUNTRY> --exit-router-country <EXIT_ROUTER_COUNTRY>
Mixnet recipient address Mixnet recipient address
--enable-wireguard --enable-wireguard
Enable the wireguard traffic between the client and the entry gateway Enable the wireguard traffic between the client and the entry gateway
@@ -85,10 +88,8 @@ Options:
Associated private key Associated private key
--wg-ip <WG_IP> --wg-ip <WG_IP>
The IP address of the wireguard interface used for the first hop to the entry gateway The IP address of the wireguard interface used for the first hop to the entry gateway
--nym-ipv4 <NYM_IPV4> --nym-ip <NYM_IP>
The IPv4 address of the nym TUN device that wraps IP packets in sphinx packets The IP address of the nym TUN device that wraps IP packets in sphinx packets
--nym-ipv6 <NYM_IPV6>
The IPv6 address of the nym TUN device that wraps IP packets in sphinx packets
--nym-mtu <NYM_MTU> --nym-mtu <NYM_MTU>
The MTU of the nym TUN device that wraps IP packets in sphinx packets The MTU of the nym TUN device that wraps IP packets in sphinx packets
--disable-routing --disable-routing
@@ -124,19 +125,3 @@ Here is a list of the options and their descriptions. Some are essential, some a
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway. - `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway. - `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
- `--disable-routing`: Disable routing all traffic through the VPN TUN device. - `--disable-routing`: Disable routing all traffic through the VPN TUN device.
## Testnet environment
If you want to run NymVPN CLI in Nym Sandbox environment, there are a few adjustments to be done:
1. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries by running:
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
```
1. Check available Gateways at [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
2. Run with a flag `-c`
```sh
sudo ./nym-vpn-cli -c <PATH_TO>/sandbox.env <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
```
@@ -1,35 +0,0 @@
# NymVPN Command Line Interface (CLI)
```admonish info
Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
```
Follow the simple [automated script](#automated-script-for-cli-installation) below to install and run NymVPN CLI. If you prefer to do a manual setup follow the steps in the guide for [Linux](cli-linux.md) or [MacOS](cli-mac.md).
Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums or download the binaries directly.
## Automated Script for CLI Installation
We wrote a [script](https://gist.github.com/serinko/d65450653d6bbafacbcee71c9cb8fb31) which does download of the CLI, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically following the steps below:
1. Open a terminal window in a directory where you want the script and NymVPN CLI binary be downloaded and run
```sh
curl -o execute-nym-vpn-cli-binary.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/d39f98dbb36ccff761a7e940073388a6fe7b73fe/execute-nym-vpn-cli-binary.sh && chmod u+x execute-nym-vpn-cli-binary.sh && sudo -E ./execute-nym-vpn-cli-binary.sh
```
2. Follow the prompts in the program
3. The script will automatically start the client. Make sure to **turn off any other VPNs** and follow the prompts:
* It prints a JSON view of existing Gateways and prompt you to:
- *Make sure to use two different Gateways for entry and exit!*
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
- `do you want five hop or two hop?`: type `five` or `two`
- `enable WireGuard? (yes/no):` if you chose yes, find your private key and wireguard IP [here](https://nymvpn.com/en/alpha)
To run `nym-vpn-cli` again, reconnect your wifi, move to the directory of your CLI binary `cd ~/nym-vpn-cli-dir` and follow the guide for [Linux](cli-linux.md#run-nymvpn) or [MacOS](cli-mac.md#run-nymvpn). If you find it too difficult, just run this script again - like in step \#3 above.
In case of errors check out the [troubleshooting](troubleshooting.md) section.
+20 -162
View File
@@ -1,177 +1,35 @@
# NymVPN CLI Guide # NymVPN Command Line Interface (CLI)
```admonish info ```admonish info
NymVPN is an experimental software and it's for testing purposes only. Anyone can submit a registration to the private alpha round on [nymvpn.com](https://nymvpn.com/en). Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
``` ```
## Overview Follow the simple [automated script](#automated-script-for-cli-installation) below to install and run NymVPN CLI. If you prefer to do a manual setup follow the steps in the guide for [Linux](cli-linux.md) or [MacOS](cli-mac.md).
The core binaries consist of: Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums or download the binaries directly.
- **`nym-vpn-cli`**: Basic commandline client for running the vpn. This runs in the foreground. ## Automated Script for CLI Installation
- **`nym-vpnd`**: Daemon implementation of the vpn client that can run in the background and interacted with using `nym-vpnc`. We wrote a [script](https://gist.github.com/serinko/d65450653d6bbafacbcee71c9cb8fb31) which does download of the CLI, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically following the steps below:
- **`nym-vpnc`**: The commandline client used to interact with `nym-vpnd`. 1. Open a terminal window in a directory where you want the script and NymVPN CLI binary be downloaded and run
## Installation
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
1. Open Github [releases page]({{nym_vpn_releases}}) and download the CLI latest binary for your system
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
```sh ```sh
echo "<SHA_STRING>" | shasum -a 256 -c curl -o execute-nym-vpn-cli-binary.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/d39f98dbb36ccff761a7e940073388a6fe7b73fe/execute-nym-vpn-cli-binary.sh && chmod u+x execute-nym-vpn-cli-binary.sh && sudo -E ./execute-nym-vpn-cli-binary.sh
# choose a correct one according to your binary, this is just an example
# echo "0e4abb461e86b2c168577e0294112a3bacd3a24bf8565b49783bfebd9b530e23 nym-vpn-cli_<!-- cmdrun ../../../scripts/cmdrun/nym_vpn_cli_version.sh -->_ubuntu-22.04_amd64.tar.gz" | shasum -a 256 -c
``` ```
3. Extract files: 2. Follow the prompts in the program
```sh
tar -xvf <BINARY>.tar.gz
# for example
# tar -xvf nym-vpn-cli_<!-- cmdrun ../../../scripts/cmdrun/nym_vpn_cli_version.sh -->_ubuntu-22.04_x86_64.tar.gz
```
## Running 3. The script will automatically start the client. Make sure to **turn off any other VPNs** and follow the prompts:
If you are running Debian/Ubuntu/PopOS or any other distributio supporting debian packages and systemd, see the [relevant section below](#debian-package-for-debianubuntupopos). * It prints a JSON view of existing Gateways and prompt you to:
- *Make sure to use two different Gateways for entry and exit!*
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
- `do you want five hop or two hop?`: type `five` or `two`
- `enable WireGuard? (yes/no):` if you chose yes, find your private key and wireguard IP [here](https://nymvpn.com/en/alpha)
### Daemon To run `nym-vpn-cli` again, reconnect your wifi, move to the directory of your CLI binary `cd ~/nym-vpn-cli-dir` and follow the guide for [Linux](cli-linux.md#run-nymvpn) or [MacOS](cli-mac.md#run-nymvpn). If you find it too difficult, just run this script again - like in step \#3 above.
Start the daemon with In case of errors check out the [troubleshooting](troubleshooting.md) section.
```sh
sudo -E ./nym-vpnd
```
Then run
```sh
./nym-vpnc status
./nym-vpnc connect
./nym-vpnc disconnect
```
### CLI
An alternative to the daemon is to run the `nym-vpn-cli` commandline client that runs in the foreground.
```sh
./nym-vpn-cli run
```
## Credentials
NymVPN uses [zkNym bandwidth credentials](https://nymtech.net/docs/bandwidth-credentials.html). Those can be imported as a file or base58 encoded string.
```sh
sudo -E ./nym-vpn-cli import-credential --credential-path </PATH/TO/freepass.nym>
sudo -E ./nym-vpn-cli import-credential --credential-data "<STRING>"
```
## Debian package for Debian/Ubuntu/PopOS
For linux platforms using deb packages and systemd, there are also debian packages.
```sh
sudo apt install ./nym-vpnd_<!-- cmdrun ../../../scripts/cmdrun/nym_vpn_cli_version.sh -->-1_amd64.deb ./nym-vpnc_<!-- cmdrun ../../../scripts/cmdrun/nym_vpn_cli_version.sh -->-1_amd64.deb
# In case of error please substitute the correct version
```
Installing the `nym-vpnd` deb package starts a `nym-vpnd.service`. Check that the daemon is running with
```sh
systemctl status nym-vpnd.service
```
and check its logs with
```sh
sudo journalctl -u nym-vpnd.service -f
```
To stop the background service
```sh
systemctl stop nym-vpnd.service
```
It will start again on startup, so disable with
```sh
systemctl disable nym-vpnd.service
```
Interact with it with `nym-vpnc`
```sh
nym-vpnc status
nym-vpnc connect
nym-vpnc disconnect
```
## Commands & Options
```admonish note
Nym Exit Gateway functionality was implemented just recently and not all the Gateways are upgraded and ready to handle the VPN connections. If you want to make sure you are connecting to a Gateway with an embedded Network Requester, IP Packet Router and applied Nym exit policy, visit [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/) and search Gateways with all the functionalities enabled.
```
The basic syntax of `nym-vpn-cli` is:
```sh
# choose only one conditional --argument listed in {brackets}
sudo ./nym-vpn-cli { --exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY> }
```
To see all the possibilities run with `--help` flag:
```sh
./nym-vpn-cli --help
```
~~~admonish example collapsible=true title="Console output"
```sh
Usage: nym-vpn-cli [OPTIONS] <COMMAND>
Commands:
run Run the client
import-credential Import credential
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-env-file <CONFIG_ENV_FILE> Path pointing to an env file describing the network
--data-path <DATA_PATH> Path to the data directory of the mixnet client
-h, --help Print help
-V, --version Print version
```
~~~
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to be adjusted by users.
**Fundamental commands and arguments**
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`)
- `--exit-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`)
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`)
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/alpha)
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/alpha)
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
**Advanced options**
- `-c` is a path to an enviroment config, like [`sandbox.env`](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env)
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
## Testnet environment
If you want to run NymVPN CLI in Nym Sandbox environment, there are a few adjustments to be done:
1. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries:
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
```
2. Check available Gateways at [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
3. Run with a flag `-c`
```sh
sudo ./nym-vpn-cli -c <PATH_TO>/sandbox.env <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
```
+4 -4
View File
@@ -1,12 +1,12 @@
# NymVPN - Desktop (GUI) # NymVPN - Desktop (GUI)
```admonish info ```admonish info
Our alpha testing round is done with participants at live workshop events and the application in this stage may not work for everyone. Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page. **If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
``` ```
This is a desktop (GUI) version of NymVPN client. A demo of how the application will look like for majority of day-to-day users. This is the alpha version of NymVPN desktop application (GUI). A demo of how the client will look like for majority of day-to-day users.
Follow the simple [automated script](#automated-script-for-gui-installation) below to install and run NymVPN GUI. If the script didn't work for your distribution or you prefer to do a manual setup follow the steps in the guide for [Linux](gui-linux.md) or [MacOS](gui-mac.md) . Follow the simple [automated script](#automated-script-for-gui-installation) below to install and run NymVPN GUI. If the script didn't work for your distribution or you prefer to do a manual setup follow the steps in the guide for [Linux](gui-linux.md) or [MacOS](gui-mac.md) .
@@ -14,12 +14,12 @@ Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums
## Linux AppImage Automated Installation Method ## Linux AppImage Automated Installation Method
The latest releases contain `appimage.sh` script. This method makes the installation simple for Linux users who want to run NymVPN from AppImmage. Executing the command below will download the binary to `~/.local/bin` and verify the checksum: The latest releases contain `appimage.sh` script. This method makes the installation simple for Linux users who want to run NymVPN from AppImmage. Executing the command below will download the binary to `~/.local/bin` and verify the checksum.
```sh ```sh
curl -fsSL https://github.com/nymtech/nym-vpn-client/releases/download/nym-vpn-desktop-v<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->/appimage.sh | bash curl -fsSL https://github.com/nymtech/nym-vpn-client/releases/download/nym-vpn-desktop-v<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->/appimage.sh | bash
``` ```
Run with the command: Run with the command
```sh ```sh
sudo -E ~/.local/bin/nym-vpn.appimage sudo -E ~/.local/bin/nym-vpn.appimage
``` ```
+25 -7
View File
@@ -2,12 +2,21 @@
<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/897010658?h=1f55870fe6&amp;badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="NYMVPN alpha demo 37C3"></iframe></div><script src="https://player.vimeo.com/api/player.js"></script> <div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/897010658?h=1f55870fe6&amp;badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="NYMVPN alpha demo 37C3"></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>
**NymVPN alpha** is a client that uses [Nym Mixnet](https://nymtech.net) to anonymise all of a user's internet traffic through either a 5-hop mixnet (for a full network privacy) or the faster 2-hop decentralised VPN (with some extra features). **Nym proudly presents NymVPN alpha** - a client that uses [Nym Mixnet](https://nymtech.net) to anonymise all of a user's internet traffic through either a 5-hop mixnet (for a full network privacy) or the faster 2-hop decentralised VPN (with some extra features).
**You are invited to take part in the alpha testing** of this new application. Register for private testing round at [nymvpn.com](https://nymvpn.com/en), that will grant you access to the [download page](https://nymvpn.com/download). Visit [NymVPN Support & FAQ](https://nymvpn.com/en/support) or join the [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat) if you have any questions, comments or blockers. **You are invited to take part in the alpha testing** of this new application. The following pages provide a how-to guide, explaining steps to install and run NymVPN [CLI](cli.md) and [GUI](gui.md) on the Sandbox testnet environment.
Checkout the [release page](https://github.com/nymtech/nym-vpn-client/releases) for available binaries. **Here is how**
1. Go to the NymVPN [testers form]({{nym_vpn_form_url}})
2. Please consent to the GDPR so we can use the results
3. To test the GUI, [go here](gui.md)
4. To test the CLI, [go here](cli.md)
5. Fill and submit the [form!]({{nym_vpn_form_url}})
6. Join the [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat) if you have any questions, comments or blockers
***NymVPN alpha testing will last from 15th of January - 15th of February.***
*NOTE: NymVPN alpha is experimental software for testing purposes only.* *NOTE: NymVPN alpha is experimental software for testing purposes only.*
@@ -28,7 +37,16 @@ client ───► Gateway ──┘ mix │ mix ┌─►mix ───►
mix └─►mix──┘ mix mix └─►mix──┘ mix
``` ```
Users can switch to 2-hop only mode, which is a faster but less private option. In this mode traffic is only sent between the two Gateways, and is not passed between Mix Nodes. It uses Mixnet Sphinx packets with shorter, fixed routes, which improve latency, but doesn't offer the same level of protection as the 5 hop mode. Users can switch to 2-hop only mode, which is a faster but less private option. In this mode traffic is only sent between the two Gateways, and is not passed between Mix Nodes.
<!-- TO BE IMPLEMENTED:
Users can switch to 2-hop only mode, which is a faster but less private option. In this mode traffic is only sent between the two Gateways, and is not passed between Mix Nodes. The client than use two wireguard tunnels with the entry and exit gateway, the Exit Gateway one being tunnelled itself through the entry gateway tunnel. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device. The client can optionally do the first hop (local client to Entry Gateway) using Wireguard. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device.
-->
## NymVPN Resources & Guides
* [NymVPN webpage](https://nymvpn.com)
* [Alpha release page]({{nym_vpn_releases}})
* [NymVPN application (GUI) guide](gui.md)
* [NymVPN Command Line Interface (CLI) guide](cli.md)
* [Troubleshooting](troubleshooting.md)
* [NymVPN FAQ](faq.md)
* [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat)
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
release_url="https://api.github.com/repos/nymtech/nym-vpn-client/releases" release_url="https://api.github.com/repos/nymtech/nym-vpn-client/releases"
current_cli_version=$(curl -s $release_url | jq -r '.[].tag_name' | grep '^nym-vpn-cli-' | sort -Vr | head -n 1 | awk -F'-v' '{print $NF}') current_cli_version=$(curl -s $release_url | jq -r '.[].tag_name' | grep '^nym-vpn-cli-v' | sort -Vr | head -n 1 | awk -F'-v' '{print $NF}')
echo "${current_cli_version}" echo "${current_cli_version}"
@@ -2,14 +2,6 @@
Below are listed some points which may need to be addressed when testing NymVPN alpha. If you crashed into any errors which are not listed, please contact us at the testing workshop or in the NymVPN [Matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat). Below are listed some points which may need to be addressed when testing NymVPN alpha. If you crashed into any errors which are not listed, please contact us at the testing workshop or in the NymVPN [Matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat).
#### NymVPN attempts to connect to sandbox testnet
If you testing the latest versions and you correctly expect the client to run over the mainnet, but it listens to `https://sandbox-nym-api1.nymtech.net`, it's probably because of your previous configuration.
Check your `config.toml` either in the directory from which you run your client or in `~/.config/nym-vpn/` and remove `sandbox.env` from the config file and folder.
If the problem persists (probably due to some locally cache) download [`mainnet.env`](https://github.com/nymtech/nym/blob/master/envs/mainnet.env) and save it to the same directory.
#### Running GUI failed due to `TOML parse error` #### Running GUI failed due to `TOML parse error`
If you see this error when running NymVPN alpha desktop, it's because the older versions needed entry location in `config.toml` configuration file. From `v0.0.3` the entry location is selected directly by the user in the application. This error is due to an old `app-data.toml` config in your computer. If you see this error when running NymVPN alpha desktop, it's because the older versions needed entry location in `config.toml` configuration file. From `v0.0.3` the entry location is selected directly by the user in the application. This error is due to an old `app-data.toml` config in your computer.
+1 -1
View File
@@ -25,7 +25,7 @@ turn-off = true
[preprocessor.admonish] [preprocessor.admonish]
command = "mdbook-admonish" command = "mdbook-admonish"
assets_version = "3.0.2" # do not edit: managed by `mdbook-admonish install` assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
# https://gitlab.com/tglman/mdbook-variables/ # https://gitlab.com/tglman/mdbook-variables/
[preprocessor.variables.variables] [preprocessor.variables.variables]
@@ -1,4 +1,20 @@
@charset "UTF-8"; @charset "UTF-8";
:root {
--md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
--md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
--md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
--md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
--md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
--md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
--md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
--md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
--md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
--md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
--md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
--md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
--md-details-icon: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
}
:is(.admonition) { :is(.admonition) {
display: flow-root; display: flow-root;
margin: 1.5625em 0; margin: 1.5625em 0;
@@ -55,8 +71,6 @@ a.admonition-anchor-link::before {
padding-inline: 4.4rem 1.2rem; padding-inline: 4.4rem 1.2rem;
font-weight: 700; font-weight: 700;
background-color: rgba(68, 138, 255, 0.1); background-color: rgba(68, 138, 255, 0.1);
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
display: flex; display: flex;
} }
:is(.admonition-title, summary.admonition-title) p { :is(.admonition-title, summary.admonition-title) p {
@@ -72,8 +86,6 @@ html :is(.admonition-title, summary.admonition-title):last-child {
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
background-color: #448aff; background-color: #448aff;
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>'); mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
-webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>'); -webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
mask-repeat: no-repeat; mask-repeat: no-repeat;
@@ -107,25 +119,6 @@ details[open].admonition > summary.admonition-title::after {
transform: rotate(90deg); transform: rotate(90deg);
} }
:root {
--md-details-icon: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
}
:root {
--md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
--md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
--md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
--md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
--md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
--md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
--md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
--md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
--md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
--md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
--md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
--md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
}
:is(.admonition):is(.admonish-note) { :is(.admonition):is(.admonish-note) {
border-color: #448aff; border-color: #448aff;
} }
+11 -11
View File
@@ -13,12 +13,12 @@ declare -a plugins=("admonish" "linkcheck" "last-changed" "theme" "variables" "c
# install mdbook + plugins # install mdbook + plugins
install_mdbook_deps() { install_mdbook_deps() {
printf "\ninstalling mdbook..." printf "\ninstalling mdbook..."
# installing mdbook with only specific features for speed # installing mdbook with only specific features for speed
# cargo install mdbook --no-default-features --features search --vers "^$MINOR_VERSION" # cargo install mdbook --no-default-features --features search --vers "^$MINOR_VERSION"
cargo install mdbook --vers "^$MINOR_VERSION" cargo install mdbook --vers "^$MINOR_VERSION"
printf "\ninstalling plugins..." printf "\ninstalling plugins..."
for i in "${plugins[@]}" for i in "${plugins[@]}"
do do
cargo install mdbook-$i cargo install mdbook-$i
@@ -41,13 +41,13 @@ install_mdbook_deps() {
# uninstall mdbook + plugins # uninstall mdbook + plugins
uninstall_mdbook_deps() { uninstall_mdbook_deps() {
# mdbook # mdbook
printf "\nuninstalling existing mdbook installation...\n" printf "\nuninstalling existing mdbook installation...\n"
cargo uninstall mdbook cargo uninstall mdbook
# check it worked # check it worked
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
printf "\nsomething went wrong, exiting" printf "\nsomething went wrong, exiting"
exit 1 exit 1
else else
printf "\nmdbook deleted\n" printf "\nmdbook deleted\n"
fi fi
@@ -57,10 +57,10 @@ uninstall_mdbook_deps() {
do do
cargo uninstall mdbook-$i cargo uninstall mdbook-$i
# check it worked # check it worked
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
printf "\nsomething went wrong, exiting" printf "\nsomething went wrong, exiting"
exit 1 exit 1
else else
printf "\nmdbook-$i deleted\n" printf "\nmdbook-$i deleted\n"
fi fi
done done
@@ -71,10 +71,10 @@ main() {
printf "mdbook already installed (located at: $(which mdbook))" printf "mdbook already installed (located at: $(which mdbook))"
uninstall_mdbook_deps; uninstall_mdbook_deps;
install_mdbook_deps; install_mdbook_deps;
else else
printf "mdbook not installed" printf "mdbook not installed"
install_mdbook_deps; install_mdbook_deps;
fi fi
} }
main; main;
+1 -1
View File
@@ -24,7 +24,7 @@ turn-off = true
[preprocessor.admonish] [preprocessor.admonish]
command = "mdbook-admonish" command = "mdbook-admonish"
assets_version = "3.0.2" # do not edit: managed by `mdbook-admonish install` assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
# https://gitlab.com/tglman/mdbook-variables/ # https://gitlab.com/tglman/mdbook-variables/
[preprocessor.variables.variables] [preprocessor.variables.variables]
@@ -1,4 +1,20 @@
@charset "UTF-8"; @charset "UTF-8";
:root {
--md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
--md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
--md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
--md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
--md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
--md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
--md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
--md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
--md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
--md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
--md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
--md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
--md-details-icon: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
}
:is(.admonition) { :is(.admonition) {
display: flow-root; display: flow-root;
margin: 1.5625em 0; margin: 1.5625em 0;
@@ -55,8 +71,6 @@ a.admonition-anchor-link::before {
padding-inline: 4.4rem 1.2rem; padding-inline: 4.4rem 1.2rem;
font-weight: 700; font-weight: 700;
background-color: rgba(68, 138, 255, 0.1); background-color: rgba(68, 138, 255, 0.1);
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
display: flex; display: flex;
} }
:is(.admonition-title, summary.admonition-title) p { :is(.admonition-title, summary.admonition-title) p {
@@ -72,8 +86,6 @@ html :is(.admonition-title, summary.admonition-title):last-child {
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
background-color: #448aff; background-color: #448aff;
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>'); mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
-webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>'); -webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
mask-repeat: no-repeat; mask-repeat: no-repeat;
@@ -107,25 +119,6 @@ details[open].admonition > summary.admonition-title::after {
transform: rotate(90deg); transform: rotate(90deg);
} }
:root {
--md-details-icon: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
}
:root {
--md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
--md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
--md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
--md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
--md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
--md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
--md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
--md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
--md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
--md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
--md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
--md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
}
:is(.admonition):is(.admonish-note) { :is(.admonition):is(.admonish-note) {
border-color: #448aff; border-color: #448aff;
} }
+14 -33
View File
@@ -2,7 +2,6 @@
# Summary # Summary
- [Introduction](introduction.md) - [Introduction](introduction.md)
- [Changelog](changelog.md)
# Binaries # Binaries
@@ -13,17 +12,13 @@
# Operators Guides # Operators Guides
- [Preliminary Steps](nodes/preliminary-steps.md) - [Mixnet Nodes Setup](nodes/setup-guides.md)
- [Nym Wallet Preparation](nodes/wallet-preparation.md) - [Preliminary Steps](preliminary-steps.md)
- [VPS Setup](nodes/vps-setup.md) - [Mix Node](nodes/mix-node-setup.md)
- [Nym Node](nodes/nym-node.md) - [Gateway](nodes/gateway-setup.md)
- [Setup & Run](nodes/setup.md) - [Network Requester](nodes/network-requester-setup.md)
- [Configuration](nodes/configuration.md)
- [WSS & Reversed Proxy](nodes/proxy-configuration.md)
- [Bonding](nodes/bonding.md)
- [Nyx Validator Setup](nodes/validator-setup.md) - [Nyx Validator Setup](nodes/validator-setup.md)
- [Nym API Setup](nodes/nym-api.md) - [Nym API Setup](nodes/nym-api.md)
- [Validator & API Configuration](nodes/nyx-configuration.md)
- [Maintenance](nodes/maintenance.md) - [Maintenance](nodes/maintenance.md)
- [Manual Node Upgrade](nodes/manual-upgrade.md) - [Manual Node Upgrade](nodes/manual-upgrade.md)
- [Automatic Node Upgrade: Nymvisor Setup and Usage](nodes/nymvisor-upgrade.md) - [Automatic Node Upgrade: Nymvisor Setup and Usage](nodes/nymvisor-upgrade.md)
@@ -33,12 +28,12 @@
- [Prometheus & Grafana](testing/prometheus-grafana.md) - [Prometheus & Grafana](testing/prometheus-grafana.md)
- [ExploreNYM scripts](testing/explorenym-scripts.md) - [ExploreNYM scripts](testing/explorenym-scripts.md)
<!-- - [Run in a Docker](testing/docker-monitor.md) --> <!-- - [Run in a Docker](testing/docker-monitor.md) -->
- [Troubleshooting](nodes/troubleshooting.md)
# Troubleshooting <!--
- [Nym Nodes]()
- [VPS Setup](troubleshooting/vps-isp.md) - [Validators]
- [Nym Node](troubleshooting/nodes.md) - [Binary]
- [Validators](troubleshooting/validators.md) -->
# Token Economics # Token Economics
@@ -48,11 +43,10 @@
# FAQ # FAQ
- [General Operators FAQ](faq/general-faq.md) - [Mix Nodes](faq/mixnodes-faq.md)
- [Nym Nodes](faq/nym-nodes-faq.md) - [Project Smoosh](faq/smoosh-faq.md)
- [Nyx & Validators](faq/nyx-faq.md)
# Community & Legal Forum # Legal Forum
- [Exit Gateway](legal/exit-gateway.md) - [Exit Gateway](legal/exit-gateway.md)
- [Community Counsel](legal/community-counsel.md) - [Community Counsel](legal/community-counsel.md)
@@ -62,19 +56,6 @@
- [Landing Pages](legal/landing-pages.md) - [Landing Pages](legal/landing-pages.md)
- [How to Add Info](legal/add-content.md) - [How to Add Info](legal/add-content.md)
---
# Archive
- [Why archive?](archive/archive.md)
- [Mixnet Nodes Setup](archive/nodes/setup-guides.md)
- [Preliminary Steps](archive/nodes/initial-steps.md)
- [Mix Node](archive/nodes/mix-node-setup.md)
- [Gateway](archive/nodes/gateway-setup.md)
- [Network Requester](archive/nodes/network-requester-setup.md)
- [FAQ: Mix Nodes](archive/faq/mixnodes-faq.md)
- [FAQ: Project Smoosh](archive/faq/smoosh-faq.md)
--- ---
# Misc. # Misc.
- [Code of Conduct](coc.md) - [Code of Conduct](coc.md)
@@ -1,7 +0,0 @@
# Archived Pages
This section contains old but still relevant pages/guides, archived for backwards compatibility. The content of the pages is not updated. See the top of every page informing you about the last time of update.
Pages listed in archive section will eventually be terminated as they will become completely irrelevant with time.

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