Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0cc8cf0846 | |||
| 87b3b76854 | |||
| b3739c02c7 | |||
| 5b7b3c0bdc | |||
| f2c2f273d7 | |||
| 3913e3e56b | |||
| 5f3f97f3be | |||
| fb2b7d3480 | |||
| 7577ec9cb2 | |||
| 1e900c32df | |||
| cb4eaddc9f | |||
| 52151c5fb4 | |||
| 24fe628e15 | |||
| 369330f517 | |||
| ada30f5483 | |||
| c906770370 | |||
| 6f94124256 | |||
| e15c243202 | |||
| c5599bf07d | |||
| 70de88d53a | |||
| ef30a6706b | |||
| c784d95088 | |||
| d57a4bc242 | |||
| 750a59461c | |||
| f244cff810 | |||
| 63bbdfa523 | |||
| 1774606ec6 | |||
| e6c0b48819 | |||
| cabadcb5cc | |||
| 0cec9f636f | |||
| 56ddadc4c4 | |||
| 2ab5b63086 | |||
| ffcb7348ff | |||
| 05739b4d84 | |||
| 3a6f1bec79 | |||
| 538fd7c5ee | |||
| 12c931be36 | |||
| ca0525d949 | |||
| 053fee7fdc | |||
| 97981e536d | |||
| a7595ff176 | |||
| 14fbf8e064 | |||
| bb1fc9bb6a | |||
| 581edbf0b3 | |||
| 03c33b1ee5 | |||
| e13eeeb561 | |||
| e51881dbdf | |||
| 2a8ccace26 | |||
| 1fd02ede95 | |||
| c324804aa9 | |||
| d5b961be5b | |||
| 1e26d4c88e | |||
| f91fa95888 | |||
| b67d1e7a99 | |||
| 56a384ea09 | |||
| c7da8a7594 | |||
| cc9b444394 | |||
| 22c1d80712 | |||
| 0b38e20298 | |||
| 38f78c9983 | |||
| 6e3bb2ec18 | |||
| 412776e336 | |||
| 68d363af3c | |||
| 7415cf1934 | |||
| 8c6421f240 | |||
| c3f03a657f | |||
| 95646e5770 | |||
| 34ec1149d7 | |||
| 13f095a587 | |||
| 696a27ec76 | |||
| c0b8ddd48e | |||
| f35396481f | |||
| be8b9e5a83 | |||
| 7429487f30 | |||
| 23d11ce523 | |||
| 4f4cb83456 | |||
| 9fe1ee6436 | |||
| 9ae5ee38f6 | |||
| 0f8d1f6439 | |||
| b92527437a | |||
| d936c66e1f | |||
| e63de3f52b | |||
| bded08dce9 | |||
| 9fcffb1d94 | |||
| ed24afa207 | |||
| d966eab085 | |||
| e67c6613c0 | |||
| 2111251d35 | |||
| c60b52e9c4 | |||
| 8b14e2b1b6 | |||
| a59295f036 | |||
| d988fe02b5 | |||
| 9bdc3b260f | |||
| 2381e52d3b | |||
| aa2e0b662e | |||
| 6c7c9e46f4 | |||
| ee27dfe06c | |||
| 600b89b5c7 | |||
| 092268def9 | |||
| 80f7175abe | |||
| a90378f987 | |||
| a6278e9ae7 | |||
| 825c30a547 | |||
| fd54f8a32f | |||
| 9488b8ba6a | |||
| c1f167bbd4 | |||
| dbeeeb9796 | |||
| 81fbcdfdb2 | |||
| 13313d705f | |||
| e7c9c2b319 | |||
| 236a441036 | |||
| d8bef263b5 | |||
| e04c759c14 | |||
| 9d22387b18 | |||
| 6254656ab6 | |||
| afda62a5cf | |||
| a322becfec | |||
| 9b4e25221f | |||
| f46cc9d1bb | |||
| bf53a107af | |||
| 25ebdbb6eb | |||
| 47d045b1c7 | |||
| 0b0bb8175f |
@@ -20,6 +20,8 @@ jobs:
|
||||
platform: [ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ jobs:
|
||||
platform: [ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
# a push event from the origin repo, or a PR from external repo
|
||||
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'nymtech/nym' }}
|
||||
steps:
|
||||
|
||||
@@ -37,10 +37,17 @@ on:
|
||||
- 'tools/nym-nr-query/**'
|
||||
- 'tools/ts-rs-cli/**'
|
||||
- 'Cargo.toml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [custom-linux, custom-runner-mac-m1]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
# Enable sccache via environment variable
|
||||
# env:
|
||||
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
@@ -48,6 +55,7 @@ jobs:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||
continue-on-error: true
|
||||
if: matrix.os == 'custom-linux'
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
@@ -70,36 +78,40 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace
|
||||
# Enable wireguard by default on linux only
|
||||
args: --workspace --features wireguard
|
||||
|
||||
- name: Build all examples
|
||||
if: matrix.os == 'custom-linux'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
args: --workspace --examples --features wireguard
|
||||
|
||||
- name: Run all tests
|
||||
if: matrix.os == 'custom-linux'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace
|
||||
args: --workspace --features wireguard
|
||||
|
||||
- name: Run expensive tests
|
||||
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
|
||||
if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && matrix.os == 'custom-linux'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace -- --ignored
|
||||
args: --workspace --features wireguard -- --ignored
|
||||
|
||||
- name: Annotate with clippy checks
|
||||
if: matrix.os == 'custom-linux'
|
||||
uses: actions-rs/clippy-check@v1
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace
|
||||
args: --workspace --features wireguard
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
args: --workspace --all-targets --features wireguard -- -D warnings
|
||||
|
||||
@@ -15,6 +15,8 @@ jobs:
|
||||
check-schema:
|
||||
name: Generate and check schema
|
||||
runs-on: custom-runner-linux
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
@@ -24,9 +26,8 @@ jobs:
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
|
||||
- name: Generate the schema
|
||||
run: make contract-schema
|
||||
|
||||
- name: Check for diff
|
||||
run: git diff --exit-code -- contracts/*/schema
|
||||
run: git diff --exit-code -- contracts/**/schema
|
||||
|
||||
@@ -22,6 +22,10 @@ jobs:
|
||||
platform: [ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
# a push event from the origin repo, or a PR from external repo
|
||||
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'nymtech/nym' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ jobs:
|
||||
# since it's going to be compiled into wasm, there's absolutely
|
||||
# no point in running CI on different OS-es
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
needs: matrix_prep
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
@@ -25,6 +25,8 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted, custom-linux]
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
# env:
|
||||
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
steps:
|
||||
|
||||
@@ -17,6 +17,8 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
# env:
|
||||
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
steps:
|
||||
@@ -31,7 +33,7 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: 1.71.0
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
|
||||
@@ -10,13 +10,15 @@ on:
|
||||
jobs:
|
||||
wasm:
|
||||
runs-on: [custom-runner-linux]
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
|
||||
@@ -9,9 +9,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [custom-linux, windows10, custom-runner-mac-m1]
|
||||
rust: [stable, beta]
|
||||
os: [custom-linux, windows10, custom-runner-mac-m1]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
|
||||
@@ -14,8 +14,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [custom-linux, macos-latest, windows10]
|
||||
os: [custom-ubuntu-20.04, macos-latest, windows10]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
@@ -29,8 +31,7 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
# There is an issue with 1.72.0 where clippy crashes on nym-wallet-types. Pin to 1.71.0 for now
|
||||
toolchain: 1.71.0
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [custom-runner-linux]
|
||||
platform: [custom-ubuntu-20.04]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [custom-runner-linux]
|
||||
platform: [custom-ubuntu-20.04]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
if: ${{ (startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||
runs-on: [self-hosted, custom-runner-linux]
|
||||
runs-on: [self-hosted, custom-ubuntu-20.04]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [custom-runner-linux]
|
||||
platform: [custom-ubuntu-20.04]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -12,7 +12,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build APK
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-ubuntu-20.04
|
||||
env:
|
||||
ANDROID_HOME: ${{ github.workspace }}/android-sdk
|
||||
NDK_VERSION: 25.2.9519653
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
name: publish-sdk-npm
|
||||
name: Publish Typescript SDK
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: [custom-runner-linux]
|
||||
runs-on: [custom-ubuntu-20.04]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -28,8 +28,10 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Build and publish
|
||||
- name: Build WASM and Typescript SDK
|
||||
run: yarn sdk:build
|
||||
|
||||
- name: Publish to NPM
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
||||
working-directory: ./sdk/typescript/packages/sdk
|
||||
run: scripts/publish.sh
|
||||
run: ./sdk/typescript/scripts/publish.sh
|
||||
|
||||
@@ -3,6 +3,31 @@
|
||||
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
- add client registry to Gateway ([#3955])
|
||||
- add HTTP API to Gateway ([#3955])
|
||||
- add `/client/<pub-key>`, `clients` and `register` routes to the gateway ([#3955])
|
||||
|
||||
## [2023.1-milka] (2023-09-24)
|
||||
|
||||
- custom Debug impl for mix::Node and gateway::Node ([#3930])
|
||||
- added forceTls argument to 'MixFetchOptsSimple' ([#3907])
|
||||
- Enable loop cover traffic by default in NR ([#3904])
|
||||
- Fix all the cargo warnings ([#3899])
|
||||
- [Issue] nym-socks5-client crash on UDP request ([#3898])
|
||||
- Feature/gateway inbuilt nr ([#3877])
|
||||
- removed queued mixnet migration that was already run ([#3872])
|
||||
- [feat] Socks5 and Native client: run with hardcoded topology ([#3866])
|
||||
- Introduce a local network requester directly inside a gateway ([#3838])
|
||||
|
||||
[#3930]: https://github.com/nymtech/nym/pull/3930
|
||||
[#3907]: https://github.com/nymtech/nym/pull/3907
|
||||
[#3904]: https://github.com/nymtech/nym/pull/3904
|
||||
[#3899]: https://github.com/nymtech/nym/pull/3899
|
||||
[#3898]: https://github.com/nymtech/nym/issues/3898
|
||||
[#3877]: https://github.com/nymtech/nym/pull/3877
|
||||
[#3872]: https://github.com/nymtech/nym/pull/3872
|
||||
[#3866]: https://github.com/nymtech/nym/pull/3866
|
||||
[#3838]: https://github.com/nymtech/nym/issues/3838
|
||||
|
||||
## [v1.1.31-kitkat] (2023-09-12)
|
||||
|
||||
|
||||
Generated
+824
-955
File diff suppressed because it is too large
Load Diff
@@ -164,6 +164,8 @@ tap = "1.0.1"
|
||||
tendermint-rpc = "0.32" # same version as used by cosmrs
|
||||
thiserror = "1.0.38"
|
||||
tokio = "1.24.1"
|
||||
tokio-tungstenite = "0.20.1"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
ts-rs = "7.0.0"
|
||||
url = "2.4"
|
||||
zeroize = "1.6.0"
|
||||
|
||||
@@ -46,21 +46,9 @@ clippy:
|
||||
# -----------------------------------------------------------------------------
|
||||
define add_cargo_workspace
|
||||
|
||||
clippy-$(1):
|
||||
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
|
||||
|
||||
clippy-extra-$(1):
|
||||
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace --examples --tests -- -D warnings
|
||||
|
||||
check-$(1):
|
||||
cargo check --manifest-path $(2)/Cargo.toml --workspace $(3)
|
||||
|
||||
test-$(1):
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace
|
||||
|
||||
test-expensive-$(1):
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace -- --ignored
|
||||
|
||||
build-$(1):
|
||||
cargo build --manifest-path $(2)/Cargo.toml --workspace $(3)
|
||||
|
||||
@@ -70,15 +58,27 @@ build-extra-$(1):
|
||||
build-release-$(1):
|
||||
$(4) cargo $$($(1)_BUILD_RELEASE_TOOLCHAIN) build --manifest-path $(2)/Cargo.toml --workspace --release $(3)
|
||||
|
||||
test-$(1):
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace
|
||||
|
||||
test-expensive-$(1):
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace -- --ignored
|
||||
|
||||
clippy-$(1):
|
||||
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
|
||||
|
||||
clippy-extra-$(1):
|
||||
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace --examples --tests -- -D warnings
|
||||
|
||||
fmt-$(1):
|
||||
cargo fmt --manifest-path $(2)/Cargo.toml --all
|
||||
|
||||
clippy: clippy-$(1) clippy-extra-$(1)
|
||||
check: check-$(1)
|
||||
cargo-test: test-$(1)
|
||||
cargo-test-expensive: test-expensive-$(1)
|
||||
build: build-$(1) build-extra-$(1)
|
||||
build-release-all: build-release-$(1)
|
||||
cargo-test: test-$(1)
|
||||
cargo-test-expensive: test-expensive-$(1)
|
||||
clippy: clippy-$(1) clippy-extra-$(1)
|
||||
fmt: fmt-$(1)
|
||||
endef
|
||||
|
||||
@@ -93,10 +93,6 @@ $(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unkn
|
||||
$(eval $(call add_cargo_workspace,wallet,nym-wallet))
|
||||
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
|
||||
|
||||
# OVERRIDE: there is an issue where clippy crashes on nym-wallet-types with the latest
|
||||
# stable toolchain. So pin to 1.71.0 until that is resolved.
|
||||
wallet_CLIPPY_TOOLCHAIN := +1.71.0
|
||||
|
||||
# OVERRIDE: wasm-opt fails if the binary has been built with the latest rustc.
|
||||
# Pin to the last working version.
|
||||
contracts_BUILD_RELEASE_TOOLCHAIN := +1.69.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.29"
|
||||
version = "1.1.30"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
@@ -30,8 +30,8 @@ serde = { workspace = true, features = ["derive"] } # for config serialization/d
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tap = "1.0.1"
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||
tokio-tungstenite = "0.14" # websocket
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||
tokio-tungstenite = { workspace = true }
|
||||
|
||||
## internal
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.29"
|
||||
version = "1.1.30"
|
||||
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"
|
||||
edition = "2021"
|
||||
|
||||
@@ -64,7 +64,12 @@ pub(crate) struct Init {
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(long, alias = "api_validators", value_delimiter = ',')]
|
||||
#[clap(
|
||||
long,
|
||||
alias = "api_validators",
|
||||
value_delimiter = ',',
|
||||
group = "network"
|
||||
)]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ sha2 = "0.10.6"
|
||||
tap = "1.0.1"
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
tungstenite = { version = "0.13.0", default-features = false }
|
||||
tungstenite = { workspace = true, default-features = false }
|
||||
tokio = { workspace = true, features = ["macros"]}
|
||||
time = "0.3.17"
|
||||
zeroize = { workspace = true }
|
||||
@@ -54,7 +54,7 @@ workspace = true
|
||||
features = ["time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
version = "0.14"
|
||||
version = "0.20.1"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
version = "0.6.2"
|
||||
|
||||
@@ -47,11 +47,7 @@ impl<T> PersistedGatewayDetails<T> {
|
||||
pub fn validate(&self, shared_key: Option<&SharedKeys>) -> Result<(), ClientCoreError> {
|
||||
match self {
|
||||
PersistedGatewayDetails::Default(details) => {
|
||||
if !details.verify(
|
||||
shared_key
|
||||
.ok_or(ClientCoreError::UnavailableSharedKey)?
|
||||
.deref(),
|
||||
) {
|
||||
if !details.verify(shared_key.ok_or(ClientCoreError::UnavailableSharedKey)?) {
|
||||
Err(ClientCoreError::MismatchedGatewayDetails {
|
||||
gateway_id: details.details.gateway_id.clone(),
|
||||
})
|
||||
|
||||
@@ -199,7 +199,7 @@ fn group_mixnodes_by_country_code(
|
||||
if let Some(ref location) = m.location {
|
||||
let country_code = location.two_letter_iso_country_code.clone();
|
||||
let group_code = CountryGroup::new(country_code.as_str());
|
||||
let mixnodes = acc.entry(group_code).or_insert_with(Vec::new);
|
||||
let mixnodes = acc.entry(group_code).or_default();
|
||||
mixnodes.push(m.mix_id);
|
||||
}
|
||||
acc
|
||||
|
||||
@@ -31,7 +31,7 @@ serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
|
||||
[dependencies.tungstenite]
|
||||
version = "0.13"
|
||||
workspace = true
|
||||
default-features = false
|
||||
|
||||
# non-wasm-only dependencies
|
||||
@@ -44,7 +44,7 @@ version = "0.1.11"
|
||||
features = ["net", "sync", "time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
version = "0.14"
|
||||
workspace = true
|
||||
|
||||
# wasm-only dependencies
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||
|
||||
@@ -86,4 +86,5 @@ required-features = ["http-client"]
|
||||
default = ["http-client"]
|
||||
http-client = ["cosmrs/rpc", "openssl"]
|
||||
generate-ts = []
|
||||
contract-testing = ["nym-mixnet-contract-common/contract-testing"]
|
||||
|
||||
|
||||
+5
-4
@@ -683,13 +683,14 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
|
||||
#[cfg(feature = "contract-testing")]
|
||||
async fn testing_resolve_all_pending_events(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents {},
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents { limit: None },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
@@ -928,8 +929,8 @@ mod tests {
|
||||
.withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None)
|
||||
.ignore(),
|
||||
|
||||
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents {} => {
|
||||
#[cfg(feature = "contract-testing")]
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => {
|
||||
client.testing_resolve_all_pending_events(None).ignore()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -572,7 +572,7 @@ mod tests {
|
||||
let env = mock_env();
|
||||
|
||||
// epoch just begun
|
||||
let interval = Interval {
|
||||
let mut interval = Interval {
|
||||
id: 0,
|
||||
epochs_in_interval: 100,
|
||||
current_epoch_start: OffsetDateTime::from_unix_timestamp(
|
||||
@@ -586,19 +586,16 @@ mod tests {
|
||||
assert!(!interval.is_current_epoch_over(&env));
|
||||
|
||||
// current time == current epoch start
|
||||
let mut interval = interval;
|
||||
interval.current_epoch_start =
|
||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64).unwrap();
|
||||
assert!(!interval.is_current_epoch_over(&env));
|
||||
|
||||
// epoch HASN'T yet begun (weird edge case, but can happen if we decide to manually adjust things)
|
||||
let mut interval = interval;
|
||||
interval.current_epoch_start =
|
||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64 + 100).unwrap();
|
||||
assert!(!interval.is_current_epoch_over(&env));
|
||||
|
||||
// current_time = EXACTLY end of the epoch
|
||||
let mut interval = interval;
|
||||
interval.current_epoch_start =
|
||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64).unwrap()
|
||||
- interval.epoch_length;
|
||||
|
||||
@@ -80,7 +80,7 @@ pub fn number_of_required_fragments(
|
||||
let max_linked = linked_fragment_payload_max_len(plaintext_per_fragment);
|
||||
|
||||
match set::total_number_of_sets(message_len, plaintext_per_fragment) {
|
||||
n if n == 1 => {
|
||||
1 => {
|
||||
// is if it's a single fragment message
|
||||
if message_len < max_unlinked {
|
||||
return (1, max_unlinked - message_len);
|
||||
|
||||
@@ -89,7 +89,7 @@ pub enum PacketSize {
|
||||
impl PartialOrd for PacketSize {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
// order them by actual packet size
|
||||
self.size().partial_cmp(&other.size())
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -193,10 +193,7 @@ impl Controller {
|
||||
}
|
||||
} else if !self.recently_closed.contains(&hdr.connection_id) {
|
||||
debug!("Received a 'Send' before 'Connect' - going to buffer the data");
|
||||
let pending = self
|
||||
.pending_messages
|
||||
.entry(hdr.connection_id)
|
||||
.or_insert_with(Vec::new);
|
||||
let pending = self.pending_messages.entry(hdr.connection_id).or_default();
|
||||
pending.push(message);
|
||||
} else if !hdr.local_socket_closed {
|
||||
error!(
|
||||
|
||||
@@ -434,7 +434,7 @@ impl TaskClient {
|
||||
.await
|
||||
{
|
||||
self.log(Level::Error, "Task stopped without shutdown called");
|
||||
panic!("{timeout}")
|
||||
panic!("{:?}: {timeout}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,8 +175,8 @@ impl WasmStorage {
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
match self.key_count(store, key).await? {
|
||||
n if n == 0 => Ok(false),
|
||||
n if n == 1 => Ok(true),
|
||||
0 => Ok(false),
|
||||
1 => Ok(true),
|
||||
n => Err(StorageError::DuplicateKey { count: n }),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ gloo-net = { version = "0.3.1", features = ["websocket"], optional = true }
|
||||
|
||||
# we don't want entire tokio-tungstenite, tungstenite itself is just fine - we just want message and error enums
|
||||
[dependencies.tungstenite]
|
||||
version = "0.13"
|
||||
workspace = true
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
|
||||
@@ -19,9 +19,16 @@ base64 = "0.21.3"
|
||||
#boringtun = "0.6.0"
|
||||
boringtun = { git = "https://github.com/cloudflare/boringtun", rev = "e1d6360d6ab4529fc942a078e4c54df107abe2ba" }
|
||||
bytes = "1.5.0"
|
||||
dashmap = "5.5.3"
|
||||
etherparse = "0.13.0"
|
||||
futures = "0.3.28"
|
||||
ip_network = "0.4.1"
|
||||
ip_network_table = "0.2.0"
|
||||
log.workspace = true
|
||||
nym-task = { path = "../task" }
|
||||
tap.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net"]}
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
tokio-tun = "0.9.0"
|
||||
|
||||
@@ -5,8 +5,6 @@ use bytes::Bytes;
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Event {
|
||||
/// Dumb event with no data.
|
||||
Dumb,
|
||||
/// IP packet received from the WireGuard tunnel that should be passed through to the corresponding virtual device/internet.
|
||||
/// Original implementation also has protocol here since it understands it, but we'll have to infer it downstream
|
||||
WgPacket(Bytes),
|
||||
@@ -17,9 +15,6 @@ pub enum Event {
|
||||
impl Display for Event {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Event::Dumb => {
|
||||
write!(f, "Dumb{{}}")
|
||||
}
|
||||
Event::WgPacket(data) => {
|
||||
let size = data.len();
|
||||
write!(f, "WgPacket{{ size={size} }}")
|
||||
|
||||
+33
-125
@@ -1,140 +1,48 @@
|
||||
use std::{collections::HashMap, net::SocketAddr, sync::Arc};
|
||||
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use boringtun::x25519;
|
||||
use futures::StreamExt;
|
||||
use log::{error, info};
|
||||
use nym_task::TaskClient;
|
||||
use tap::TapFallible;
|
||||
use tokio::{net::UdpSocket, sync::mpsc, task::JoinHandle};
|
||||
use tun::WireGuardTunnel;
|
||||
|
||||
use crate::event::Event;
|
||||
|
||||
pub use error::WgError;
|
||||
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||
|
||||
mod error;
|
||||
mod event;
|
||||
mod tun;
|
||||
mod network_table;
|
||||
mod platform;
|
||||
mod setup;
|
||||
mod udp_listener;
|
||||
mod wg_tunnel;
|
||||
|
||||
//const WG_ADDRESS = "0.0.0.0:51820";
|
||||
const WG_ADDRESS: &str = "0.0.0.0:51822";
|
||||
// Currently the module related to setting up the virtual network device is platform specific.
|
||||
#[cfg(target_os = "linux")]
|
||||
use platform::linux::tun_device;
|
||||
|
||||
// The private key of the listener
|
||||
// Corresponding public key: "WM8s8bYegwMa0TJ+xIwhk+dImk2IpDUKslDBCZPizlE="
|
||||
const PRIVATE_KEY: &str = "AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=";
|
||||
#[derive(Clone)]
|
||||
struct TunTaskTx(tokio::sync::mpsc::UnboundedSender<Vec<u8>>);
|
||||
|
||||
// The public keys of the registered peers (clients)
|
||||
const PEERS: &[&str; 1] = &[
|
||||
// Corresponding private key: "ILeN6gEh6vJ3Ju8RJ3HVswz+sPgkcKtAYTqzQRhTtlo="
|
||||
"NCIhkgiqxFx1ckKl3Zuh595DzIFl8mxju1Vg995EZhI=", // "mxV/mw7WZTe+0Msa0kvJHMHERDA/cSskiZWQce+TdEs=",
|
||||
];
|
||||
|
||||
const MAX_PACKET: usize = 65535;
|
||||
|
||||
fn init_static_dev_keys() -> (x25519::StaticSecret, x25519::PublicKey) {
|
||||
// TODO: this is a temporary solution for development
|
||||
let static_private_bytes: [u8; 32] = general_purpose::STANDARD
|
||||
.decode(PRIVATE_KEY)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let static_private = x25519::StaticSecret::try_from(static_private_bytes).unwrap();
|
||||
let static_public = x25519::PublicKey::from(&static_private);
|
||||
info!(
|
||||
"wg public key: {}",
|
||||
general_purpose::STANDARD.encode(static_public)
|
||||
);
|
||||
|
||||
// TODO: A single static public key is used for all peers during development
|
||||
let peer_static_public_bytes: [u8; 32] = general_purpose::STANDARD
|
||||
.decode(PEERS[0])
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let peer_static_public = x25519::PublicKey::try_from(peer_static_public_bytes).unwrap();
|
||||
|
||||
(static_private, peer_static_public)
|
||||
impl TunTaskTx {
|
||||
fn send(&self, packet: Vec<u8>) -> Result<(), tokio::sync::mpsc::error::SendError<Vec<u8>>> {
|
||||
self.0.send(packet)
|
||||
}
|
||||
}
|
||||
|
||||
fn start_wg_tunnel(
|
||||
addr: SocketAddr,
|
||||
udp: Arc<UdpSocket>,
|
||||
static_private: x25519::StaticSecret,
|
||||
peer_static_public: x25519::PublicKey,
|
||||
) -> (JoinHandle<SocketAddr>, mpsc::UnboundedSender<Event>) {
|
||||
let (mut tunnel, peer_tx) = WireGuardTunnel::new(udp, addr, static_private, peer_static_public);
|
||||
let join_handle = tokio::spawn(async move {
|
||||
tunnel.spin_off().await;
|
||||
addr
|
||||
});
|
||||
(join_handle, peer_tx)
|
||||
}
|
||||
|
||||
pub async fn start_wg_listener(
|
||||
mut task_client: TaskClient,
|
||||
#[cfg(target_os = "linux")]
|
||||
pub async fn start_wireguard(
|
||||
task_client: nym_task::TaskClient,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
log::info!("Starting wireguard listener on {}", WG_ADDRESS);
|
||||
let udp_socket = Arc::new(UdpSocket::bind(WG_ADDRESS).await?);
|
||||
use std::sync::Arc;
|
||||
|
||||
// Setup some static keys for development
|
||||
let (static_private, peer_static_public) = init_static_dev_keys();
|
||||
// The set of active tunnels indexed by the peer's address
|
||||
let active_peers = Arc::new(udp_listener::ActivePeers::new());
|
||||
let peers_by_ip = Arc::new(std::sync::Mutex::new(network_table::NetworkTable::new()));
|
||||
|
||||
tokio::spawn(async move {
|
||||
// The set of active tunnels indexed by the peer's address
|
||||
let mut active_peers: HashMap<SocketAddr, mpsc::UnboundedSender<Event>> = HashMap::new();
|
||||
// Each tunnel is run in its own task, and the task handle is stored here so we can remove
|
||||
// it from `active_peers` when the tunnel is closed
|
||||
let mut active_peers_task_handles = futures::stream::FuturesUnordered::new();
|
||||
let mut buf = [0u8; MAX_PACKET];
|
||||
// Start the tun device that is used to relay traffic outbound
|
||||
let tun_task_tx = tun_device::start_tun_device(peers_by_ip.clone());
|
||||
|
||||
while !task_client.is_shutdown() {
|
||||
tokio::select! {
|
||||
_ = task_client.recv() => {
|
||||
log::trace!("WireGuard listener: received shutdown");
|
||||
break;
|
||||
}
|
||||
// Handle tunnel closing
|
||||
Some(addr) = active_peers_task_handles.next() => {
|
||||
match addr {
|
||||
Ok(addr) => {
|
||||
info!("WireGuard listener: closed {addr:?}");
|
||||
active_peers.remove(&addr);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("WireGuard listener: error receiving shutdown from peer: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle incoming packets
|
||||
Ok((len, addr)) = udp_socket.recv_from(&mut buf) => {
|
||||
log::info!("Received {} bytes from {}", len, addr);
|
||||
|
||||
if let Some(peer_tx) = active_peers.get_mut(&addr) {
|
||||
log::info!("WireGuard listener: received packet from known peer");
|
||||
peer_tx.send(Event::WgPacket(buf[..len].to_vec().into()))
|
||||
.tap_err(|err| log::error!("{err}"))
|
||||
.unwrap();
|
||||
} else {
|
||||
log::info!("WireGuard listener: received packet from unknown peer, starting tunnel");
|
||||
let (join_handle, peer_tx) = start_wg_tunnel(
|
||||
addr,
|
||||
udp_socket.clone(),
|
||||
static_private.clone(),
|
||||
peer_static_public
|
||||
);
|
||||
peer_tx.send(Event::WgPacket(buf[..len].to_vec().into()))
|
||||
.tap_err(|err| log::error!("{err}"))
|
||||
.unwrap();
|
||||
|
||||
active_peers.insert(addr, peer_tx);
|
||||
active_peers_task_handles.push(join_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log::info!("WireGuard listener: shutting down");
|
||||
});
|
||||
// Start the UDP listener that clients connect to
|
||||
udp_listener::start_udp_listener(tun_task_tx, active_peers, peers_by_ip, task_client).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub async fn start_wireguard(
|
||||
_task_client: nym_task::TaskClient,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
todo!("WireGuard is currently only supported on Linux")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use ip_network::IpNetwork;
|
||||
use ip_network_table::IpNetworkTable;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct NetworkTable<T> {
|
||||
ips: IpNetworkTable<T>,
|
||||
}
|
||||
|
||||
impl<T> NetworkTable<T> {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
ips: IpNetworkTable::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert<N: Into<IpNetwork>>(&mut self, network: N, data: T) -> Option<T> {
|
||||
self.ips.insert(network, data)
|
||||
}
|
||||
|
||||
pub fn longest_match<I: Into<IpAddr>>(&self, ip: I) -> Option<(IpNetwork, &T)> {
|
||||
self.ips.longest_match(ip)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub(crate) mod tun_device;
|
||||
@@ -0,0 +1,99 @@
|
||||
use std::{net::Ipv4Addr, sync::Arc};
|
||||
|
||||
use etherparse::{InternetSlice, SlicedPacket};
|
||||
use tap::TapFallible;
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
sync::mpsc::{self},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
event::Event,
|
||||
setup::{TUN_BASE_NAME, TUN_DEVICE_ADDRESS, TUN_DEVICE_NETMASK},
|
||||
udp_listener::PeersByIp,
|
||||
TunTaskTx,
|
||||
};
|
||||
|
||||
fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> tokio_tun::Tun {
|
||||
log::info!("Creating TUN device with: address={address}, netmask={netmask}");
|
||||
tokio_tun::Tun::builder()
|
||||
.name(name)
|
||||
.tap(false)
|
||||
.packet_info(false)
|
||||
.mtu(1350)
|
||||
.up()
|
||||
.address(address)
|
||||
.netmask(netmask)
|
||||
.try_build()
|
||||
.expect("Failed to setup tun device, do you have permission?")
|
||||
}
|
||||
|
||||
pub(crate) fn start_tun_device(peers_by_ip: Arc<std::sync::Mutex<PeersByIp>>) -> TunTaskTx {
|
||||
let tun = setup_tokio_tun_device(
|
||||
format!("{}%d", TUN_BASE_NAME).as_str(),
|
||||
TUN_DEVICE_ADDRESS.parse().unwrap(),
|
||||
TUN_DEVICE_NETMASK.parse().unwrap(),
|
||||
);
|
||||
log::info!("Created TUN device: {}", tun.name());
|
||||
|
||||
let (mut tun_device_rx, mut tun_device_tx) = tokio::io::split(tun);
|
||||
|
||||
// Channels to communicate with the other tasks
|
||||
let (tun_task_tx, mut tun_task_rx) = mpsc::unbounded_channel::<Vec<u8>>();
|
||||
let tun_task_tx = TunTaskTx(tun_task_tx);
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut buf = [0u8; 1024];
|
||||
loop {
|
||||
tokio::select! {
|
||||
// Reading from the TUN device
|
||||
len = tun_device_rx.read(&mut buf) => match len {
|
||||
Ok(len) => {
|
||||
let packet = &buf[..len];
|
||||
let dst_addr = boringtun::noise::Tunn::dst_address(packet).unwrap();
|
||||
|
||||
let headers = SlicedPacket::from_ip(packet).unwrap();
|
||||
let src_addr = match headers.ip.unwrap() {
|
||||
InternetSlice::Ipv4(ip, _) => ip.source_addr().to_string(),
|
||||
InternetSlice::Ipv6(ip, _) => ip.source_addr().to_string(),
|
||||
};
|
||||
log::info!("iface: read Packet({src_addr} -> {dst_addr}, {len} bytes)");
|
||||
|
||||
// Route packet to the correct peer.
|
||||
if let Some(peer_tx) = peers_by_ip.lock().unwrap().longest_match(dst_addr).map(|(_, tx)| tx) {
|
||||
log::info!("Forward packet to wg tunnel");
|
||||
peer_tx
|
||||
.send(Event::IpPacket(packet.to_vec().into()))
|
||||
.tap_err(|err| log::error!("{err}"))
|
||||
.unwrap();
|
||||
} else {
|
||||
log::info!("No peer found, packet dropped");
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log::info!("iface: read error: {err}");
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// Writing to the TUN device
|
||||
Some(data) = tun_task_rx.recv() => {
|
||||
let headers = SlicedPacket::from_ip(&data).unwrap();
|
||||
let (source_addr, destination_addr) = match headers.ip.unwrap() {
|
||||
InternetSlice::Ipv4(ip, _) => (ip.source_addr(), ip.destination_addr()),
|
||||
InternetSlice::Ipv6(_, _) => unimplemented!(),
|
||||
};
|
||||
|
||||
log::info!(
|
||||
"iface: write Packet({source_addr} -> {destination_addr}, {} bytes)",
|
||||
data.len()
|
||||
);
|
||||
// log::info!("iface: writing {} bytes", data.len());
|
||||
tun_device_tx.write_all(&data).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
log::info!("TUN device shutting down");
|
||||
});
|
||||
tun_task_tx
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#[cfg(target_os = "linux")]
|
||||
pub(crate) mod linux;
|
||||
@@ -0,0 +1,63 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use boringtun::x25519;
|
||||
use log::info;
|
||||
|
||||
// The wireguard UDP listener
|
||||
pub const WG_ADDRESS: &str = "0.0.0.0";
|
||||
pub const WG_PORT: u16 = 51822;
|
||||
|
||||
// The interface used to route traffic
|
||||
pub const TUN_BASE_NAME: &str = "nymtun";
|
||||
pub const TUN_DEVICE_ADDRESS: &str = "10.0.0.1";
|
||||
pub const TUN_DEVICE_NETMASK: &str = "255.255.255.0";
|
||||
|
||||
// The private key of the listener
|
||||
// Corresponding public key: "WM8s8bYegwMa0TJ+xIwhk+dImk2IpDUKslDBCZPizlE="
|
||||
const PRIVATE_KEY: &str = "AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=";
|
||||
|
||||
// The public keys of the registered peer (clients)
|
||||
// Corresponding private key: "ILeN6gEh6vJ3Ju8RJ3HVswz+sPgkcKtAYTqzQRhTtlo="
|
||||
const PEER: &str = "NCIhkgiqxFx1ckKl3Zuh595DzIFl8mxju1Vg995EZhI=";
|
||||
|
||||
// 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.0.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::StaticSecret {
|
||||
// TODO: this is a temporary solution for development
|
||||
let static_private_bytes: [u8; 32] = decode_base64_key(PRIVATE_KEY);
|
||||
let static_private = x25519::StaticSecret::try_from(static_private_bytes).unwrap();
|
||||
let static_public = x25519::PublicKey::from(&static_private);
|
||||
info!(
|
||||
"wg public key: {}",
|
||||
general_purpose::STANDARD.encode(static_public)
|
||||
);
|
||||
static_private
|
||||
}
|
||||
|
||||
pub fn peer_static_public_key() -> x25519::PublicKey {
|
||||
// A single static public key is used during development
|
||||
let peer_static_public_bytes: [u8; 32] = decode_base64_key(PEER);
|
||||
let peer_static_public = x25519::PublicKey::try_from(peer_static_public_bytes).unwrap();
|
||||
info!(
|
||||
"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 = 0u8;
|
||||
ip_network::IpNetwork::new_truncate(key, cidr).unwrap()
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use dashmap::DashMap;
|
||||
use futures::StreamExt;
|
||||
use log::error;
|
||||
use nym_task::TaskClient;
|
||||
use tap::TapFallible;
|
||||
use tokio::{
|
||||
net::UdpSocket,
|
||||
sync::mpsc::{self},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
event::Event,
|
||||
network_table::NetworkTable,
|
||||
setup::{self, WG_ADDRESS, WG_PORT},
|
||||
TunTaskTx,
|
||||
};
|
||||
|
||||
const MAX_PACKET: usize = 65535;
|
||||
|
||||
pub(crate) type ActivePeers = DashMap<SocketAddr, mpsc::UnboundedSender<Event>>;
|
||||
pub(crate) type PeersByIp = NetworkTable<mpsc::UnboundedSender<Event>>;
|
||||
|
||||
pub(crate) async fn start_udp_listener(
|
||||
tun_task_tx: TunTaskTx,
|
||||
active_peers: Arc<ActivePeers>,
|
||||
peers_by_ip: Arc<std::sync::Mutex<PeersByIp>>,
|
||||
mut task_client: TaskClient,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
let wg_address = SocketAddr::new(WG_ADDRESS.parse().unwrap(), WG_PORT);
|
||||
log::info!("Starting wireguard UDP listener on {wg_address}");
|
||||
let udp_socket = Arc::new(UdpSocket::bind(wg_address).await?);
|
||||
|
||||
// Setup some static keys for development
|
||||
let static_private = setup::server_static_private_key();
|
||||
let peer_static_public = setup::peer_static_public_key();
|
||||
let peer_allowed_ips = setup::peer_allowed_ips();
|
||||
|
||||
tokio::spawn(async move {
|
||||
// Each tunnel is run in its own task, and the task handle is stored here so we can remove
|
||||
// it from `active_peers` when the tunnel is closed
|
||||
let mut active_peers_task_handles = futures::stream::FuturesUnordered::new();
|
||||
let mut buf = [0u8; MAX_PACKET];
|
||||
|
||||
while !task_client.is_shutdown() {
|
||||
tokio::select! {
|
||||
_ = task_client.recv() => {
|
||||
log::trace!("WireGuard UDP listener: received shutdown");
|
||||
break;
|
||||
}
|
||||
// Handle tunnel closing
|
||||
Some(addr) = active_peers_task_handles.next() => {
|
||||
match addr {
|
||||
Ok(addr) => {
|
||||
log::info!("Removing peer: {addr:?}");
|
||||
active_peers.remove(&addr);
|
||||
// TODO: remove from peers_by_ip
|
||||
}
|
||||
Err(err) => {
|
||||
error!("WireGuard UDP listener: error receiving shutdown from peer: {err}");
|
||||
}
|
||||
}
|
||||
},
|
||||
// Handle incoming packets
|
||||
Ok((len, addr)) = udp_socket.recv_from(&mut buf) => {
|
||||
log::trace!("udp: received {} bytes from {}", len, addr);
|
||||
|
||||
if let Some(peer_tx) = active_peers.get_mut(&addr) {
|
||||
log::info!("udp: received {len} bytes from {addr} from known peer");
|
||||
peer_tx.send(Event::WgPacket(buf[..len].to_vec().into()))
|
||||
.tap_err(|err| log::error!("{err}"))
|
||||
.unwrap();
|
||||
} else {
|
||||
log::info!("udp: received {len} bytes from {addr} from unknown peer, starting tunnel");
|
||||
// TODO: this is a temporary solution for development since this
|
||||
// assumes we know the peer_static_public this corresponds to.
|
||||
// TODO: rework this before production! This is likely not secure!
|
||||
log::warn!("Assuming peer_static_public is known");
|
||||
log::warn!("SECURITY: Rework me to do proper handshake before creating the tunnel!");
|
||||
let (join_handle, peer_tx) = crate::wg_tunnel::start_wg_tunnel(
|
||||
addr,
|
||||
udp_socket.clone(),
|
||||
static_private.clone(),
|
||||
peer_static_public,
|
||||
peer_allowed_ips,
|
||||
tun_task_tx.clone(),
|
||||
);
|
||||
|
||||
peers_by_ip.lock().unwrap().insert(peer_allowed_ips, peer_tx.clone());
|
||||
|
||||
peer_tx.send(Event::WgPacket(buf[..len].to_vec().into()))
|
||||
.tap_err(|err| log::error!("{err}"))
|
||||
.unwrap();
|
||||
|
||||
// WIP(JON): active peers should probably be keyed by peer_static_public
|
||||
// instead. Does this current setup lead to any issues?
|
||||
log::info!("Adding peer: {addr}");
|
||||
active_peers.insert(addr, peer_tx);
|
||||
active_peers_task_handles.push(join_handle);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
log::info!("WireGuard listener: shutting down");
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -14,19 +14,22 @@ use tokio::{
|
||||
time::timeout,
|
||||
};
|
||||
|
||||
use crate::{event::Event, WgError};
|
||||
use crate::{error::WgError, event::Event, network_table::NetworkTable, TunTaskTx};
|
||||
|
||||
const MAX_PACKET: usize = 65535;
|
||||
|
||||
pub struct WireGuardTunnel {
|
||||
// Incoming data from the UDP socket received in the main event loop
|
||||
udp_rx: mpsc::UnboundedReceiver<Event>,
|
||||
peer_rx: mpsc::UnboundedReceiver<Event>,
|
||||
|
||||
// UDP socket used for sending data
|
||||
udp: Arc<UdpSocket>,
|
||||
|
||||
// Peer endpoint
|
||||
addr: SocketAddr,
|
||||
endpoint: Arc<tokio::sync::RwLock<SocketAddr>>,
|
||||
|
||||
// AllowedIPs for this peer
|
||||
allowed_ips: NetworkTable<()>,
|
||||
|
||||
// `boringtun` tunnel, used for crypto & WG protocol
|
||||
wg_tunnel: Arc<tokio::sync::Mutex<Tunn>>,
|
||||
@@ -34,6 +37,9 @@ pub struct WireGuardTunnel {
|
||||
// Signal close
|
||||
close_tx: broadcast::Sender<()>,
|
||||
close_rx: broadcast::Receiver<()>,
|
||||
|
||||
// Send data to the task that handles sending data through the tun device
|
||||
tun_task_tx: TunTaskTx,
|
||||
}
|
||||
|
||||
impl Drop for WireGuardTunnel {
|
||||
@@ -44,16 +50,18 @@ impl Drop for WireGuardTunnel {
|
||||
}
|
||||
|
||||
impl WireGuardTunnel {
|
||||
fn close(&self) {
|
||||
let _ = self.close_tx.send(());
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
udp: Arc<UdpSocket>,
|
||||
addr: SocketAddr,
|
||||
endpoint: SocketAddr,
|
||||
static_private: x25519::StaticSecret,
|
||||
peer_static_public: x25519::PublicKey,
|
||||
peer_allowed_ips: ip_network::IpNetwork,
|
||||
tunnel_tx: TunTaskTx,
|
||||
) -> (Self, mpsc::UnboundedSender<Event>) {
|
||||
let local_addr = udp.local_addr().unwrap();
|
||||
let peer_addr = udp.peer_addr();
|
||||
log::info!("New wg tunnel: endpoint: {endpoint}, local_addr: {local_addr}, peer_addr: {peer_addr:?}");
|
||||
|
||||
let preshared_key = None;
|
||||
let persistent_keepalive = None;
|
||||
let index = 0;
|
||||
@@ -72,21 +80,30 @@ impl WireGuardTunnel {
|
||||
));
|
||||
|
||||
// Channels with incoming data that is received by the main event loop
|
||||
let (udp_tx, udp_rx) = mpsc::unbounded_channel();
|
||||
let (peer_tx, peer_rx) = mpsc::unbounded_channel();
|
||||
|
||||
// Signal close tunnel
|
||||
let (close_tx, close_rx) = broadcast::channel(1);
|
||||
|
||||
let mut allowed_ips = NetworkTable::new();
|
||||
allowed_ips.insert(peer_allowed_ips, ());
|
||||
|
||||
let tunnel = WireGuardTunnel {
|
||||
udp_rx,
|
||||
peer_rx,
|
||||
udp,
|
||||
addr,
|
||||
endpoint: Arc::new(tokio::sync::RwLock::new(endpoint)),
|
||||
allowed_ips,
|
||||
wg_tunnel,
|
||||
close_tx,
|
||||
close_rx,
|
||||
tun_task_tx: tunnel_tx,
|
||||
};
|
||||
|
||||
(tunnel, udp_tx)
|
||||
(tunnel, peer_tx)
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
let _ = self.close_tx.send(());
|
||||
}
|
||||
|
||||
pub async fn spin_off(&mut self) {
|
||||
@@ -96,9 +113,9 @@ impl WireGuardTunnel {
|
||||
info!("WireGuard tunnel: received msg to close");
|
||||
break;
|
||||
},
|
||||
packet = self.udp_rx.recv() => match packet {
|
||||
packet = self.peer_rx.recv() => match packet {
|
||||
Some(packet) => {
|
||||
info!("WireGuard tunnel received: {packet}");
|
||||
info!("event loop: {packet}");
|
||||
match packet {
|
||||
Event::WgPacket(data) => {
|
||||
let _ = self.consume_wg(&data)
|
||||
@@ -106,7 +123,6 @@ impl WireGuardTunnel {
|
||||
.tap_err(|err| error!("WireGuard tunnel: consume_wg error: {err}"));
|
||||
},
|
||||
Event::IpPacket(data) => self.consume_eth(&data).await,
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
None => {
|
||||
@@ -121,7 +137,7 @@ impl WireGuardTunnel {
|
||||
},
|
||||
}
|
||||
}
|
||||
info!("WireGuard tunnel ({}): closed", self.addr);
|
||||
info!("WireGuard tunnel ({}): closed", self.endpoint.read().await);
|
||||
}
|
||||
|
||||
async fn wg_tunnel_lock(&self) -> Result<tokio::sync::MutexGuard<'_, Tunn>, WgError> {
|
||||
@@ -130,21 +146,31 @@ impl WireGuardTunnel {
|
||||
.map_err(|_| WgError::UnableToGetTunnel)
|
||||
}
|
||||
|
||||
async fn consume_wg(&self, data: &[u8]) -> Result<(), WgError> {
|
||||
#[allow(unused)]
|
||||
async fn set_endpoint(&self, addr: SocketAddr) {
|
||||
if *self.endpoint.read().await != addr {
|
||||
log::info!("wg tunnel update endpoint: {addr}");
|
||||
*self.endpoint.write().await = addr;
|
||||
}
|
||||
}
|
||||
|
||||
async fn consume_wg(&mut self, data: &[u8]) -> Result<(), WgError> {
|
||||
let mut send_buf = [0u8; MAX_PACKET];
|
||||
let mut peer = self.wg_tunnel_lock().await?;
|
||||
match peer.decapsulate(None, data, &mut send_buf) {
|
||||
let mut tunnel = self.wg_tunnel_lock().await?;
|
||||
match tunnel.decapsulate(None, data, &mut send_buf) {
|
||||
TunnResult::WriteToNetwork(packet) => {
|
||||
debug!("WireGuard: writing to network");
|
||||
if let Err(err) = self.udp.send_to(packet, self.addr).await {
|
||||
let endpoint = self.endpoint.read().await;
|
||||
log::info!("udp: send {} bytes to {}", packet.len(), *endpoint);
|
||||
if let Err(err) = self.udp.send_to(packet, *endpoint).await {
|
||||
error!("Failed to send decapsulation-instructed packet to WireGuard endpoint: {err:?}");
|
||||
};
|
||||
// Flush pending queue
|
||||
loop {
|
||||
let mut send_buf = [0u8; MAX_PACKET];
|
||||
match peer.decapsulate(None, &[], &mut send_buf) {
|
||||
match tunnel.decapsulate(None, &[], &mut send_buf) {
|
||||
TunnResult::WriteToNetwork(packet) => {
|
||||
if let Err(err) = self.udp.send_to(packet, self.addr).await {
|
||||
log::info!("udp: send {} bytes to {}", packet.len(), *endpoint);
|
||||
if let Err(err) = self.udp.send_to(packet, *endpoint).await {
|
||||
error!("Failed to send decapsulation-instructed packet to WireGuard endpoint: {err:?}");
|
||||
break;
|
||||
};
|
||||
@@ -155,13 +181,23 @@ impl WireGuardTunnel {
|
||||
}
|
||||
}
|
||||
}
|
||||
TunnResult::WriteToTunnelV4(packet, _) | TunnResult::WriteToTunnelV6(packet, _) => {
|
||||
debug!("WireGuard: writing to tunnel");
|
||||
info!(
|
||||
"WireGuard endpoint sent IP packet of {} bytes (not yet implemented)",
|
||||
packet.len()
|
||||
);
|
||||
// TODO
|
||||
TunnResult::WriteToTunnelV4(packet, addr) => {
|
||||
// TODO: once the flow is redone, we should add updating the endpoint dynamically
|
||||
// self.set_endpoint(addr);
|
||||
if self.allowed_ips.longest_match(addr).is_some() {
|
||||
self.tun_task_tx.send(packet.to_vec()).unwrap();
|
||||
} else {
|
||||
warn!("Packet from {addr} not in allowed_ips");
|
||||
}
|
||||
}
|
||||
TunnResult::WriteToTunnelV6(packet, addr) => {
|
||||
// TODO: once the flow is redone, we should add updating the endpoint dynamically
|
||||
// self.set_endpoint(addr);
|
||||
if self.allowed_ips.longest_match(addr).is_some() {
|
||||
self.tun_task_tx.send(packet.to_vec()).unwrap();
|
||||
} else {
|
||||
warn!("Packet (v6) from {addr} not in allowed_ips");
|
||||
}
|
||||
}
|
||||
TunnResult::Done => {
|
||||
debug!("WireGuard: decapsulate done");
|
||||
@@ -173,9 +209,36 @@ impl WireGuardTunnel {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn consume_eth(&self, _data: &Bytes) {
|
||||
info!("WireGuard tunnel: consume_eth");
|
||||
todo!();
|
||||
async fn consume_eth(&self, data: &Bytes) {
|
||||
info!("consume_eth: raw packet size: {}", data.len());
|
||||
let encapsulated_packet = self.encapsulate_packet(data).await;
|
||||
info!(
|
||||
"consume_eth: after encapsulate: {}",
|
||||
encapsulated_packet.len()
|
||||
);
|
||||
|
||||
let endpoint = self.endpoint.read().await;
|
||||
info!("consume_eth: send to {}: {}", *endpoint, data.len());
|
||||
self.udp
|
||||
.send_to(&encapsulated_packet, *endpoint)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn encapsulate_packet(&self, payload: &[u8]) -> Vec<u8> {
|
||||
// TODO: use fixed dst and src buffers that we can reuse
|
||||
let len = 148.max(payload.len() + 32);
|
||||
let mut dst = vec![0; len];
|
||||
|
||||
let mut wg_tunnel = self.wg_tunnel_lock().await.unwrap();
|
||||
|
||||
match wg_tunnel.encapsulate(payload, &mut dst) {
|
||||
TunnResult::WriteToNetwork(packet) => packet.to_vec(),
|
||||
unexpected => {
|
||||
error!("{:?}", unexpected);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_wg_timers(&mut self) -> Result<(), WgError> {
|
||||
@@ -190,16 +253,15 @@ impl WireGuardTunnel {
|
||||
async fn handle_routine_tun_result<'a: 'async_recursion>(&self, result: TunnResult<'a>) {
|
||||
match result {
|
||||
TunnResult::WriteToNetwork(packet) => {
|
||||
info!(
|
||||
"Sending routine packet of {} bytes to WireGuard endpoint",
|
||||
packet.len()
|
||||
);
|
||||
if let Err(err) = self.udp.send_to(packet, self.addr).await {
|
||||
error!("Failed to send routine packet to WireGuard endpoint: {err:?}",);
|
||||
let endpoint = self.endpoint.read().await;
|
||||
log::info!("routine: write to network: {}: {}", endpoint, packet.len());
|
||||
if let Err(err) = self.udp.send_to(packet, *endpoint).await {
|
||||
error!("routine: failed to send packet: {err:?}");
|
||||
};
|
||||
}
|
||||
TunnResult::Err(WireGuardError::ConnectionExpired) => {
|
||||
warn!("Wireguard handshake has expired!");
|
||||
// WIP(JON): consider just closing the tunnel here
|
||||
let mut buf = vec![0u8; MAX_PACKET];
|
||||
let Ok(mut peer) = self.wg_tunnel_lock().await else {
|
||||
warn!("Failed to lock WireGuard peer, closing tunnel");
|
||||
@@ -219,3 +281,29 @@ impl WireGuardTunnel {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start_wg_tunnel(
|
||||
endpoint: SocketAddr,
|
||||
udp: Arc<UdpSocket>,
|
||||
static_private: x25519::StaticSecret,
|
||||
peer_static_public: x25519::PublicKey,
|
||||
peer_allowed_ips: ip_network::IpNetwork,
|
||||
tunnel_tx: TunTaskTx,
|
||||
) -> (
|
||||
tokio::task::JoinHandle<SocketAddr>,
|
||||
mpsc::UnboundedSender<Event>,
|
||||
) {
|
||||
let (mut tunnel, peer_tx) = WireGuardTunnel::new(
|
||||
udp,
|
||||
endpoint,
|
||||
static_private,
|
||||
peer_static_public,
|
||||
peer_allowed_ips,
|
||||
tunnel_tx,
|
||||
);
|
||||
let join_handle = tokio::spawn(async move {
|
||||
tunnel.spin_off().await;
|
||||
endpoint
|
||||
});
|
||||
(join_handle, peer_tx)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -234,67 +234,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"staking"
|
||||
],
|
||||
"properties": {
|
||||
"staking": {
|
||||
"$ref": "#/definitions/StakingMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"distribution"
|
||||
],
|
||||
"properties": {
|
||||
"distribution": {
|
||||
"$ref": "#/definitions/DistributionMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"stargate"
|
||||
],
|
||||
"properties": {
|
||||
"stargate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type_url",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/Binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ibc"
|
||||
],
|
||||
"properties": {
|
||||
"ibc": {
|
||||
"$ref": "#/definitions/IbcMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -306,67 +245,6 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"gov"
|
||||
],
|
||||
"properties": {
|
||||
"gov": {
|
||||
"$ref": "#/definitions/GovMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"DistributionMsg": {
|
||||
"description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"set_withdraw_address"
|
||||
],
|
||||
"properties": {
|
||||
"set_withdraw_address": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"address"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
"description": "The `withdraw_address`",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"withdraw_delegator_reward"
|
||||
],
|
||||
"properties": {
|
||||
"withdraw_delegator_reward": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"validator": {
|
||||
"description": "The `validator_address`",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -421,196 +299,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"GovMsg": {
|
||||
"description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, StdError> { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, StdError> { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"vote"
|
||||
],
|
||||
"properties": {
|
||||
"vote": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"proposal_id",
|
||||
"vote"
|
||||
],
|
||||
"properties": {
|
||||
"proposal_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"vote": {
|
||||
"description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See <https://github.com/CosmWasm/cosmwasm/issues/1571>.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/VoteOption"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"IbcMsg": {
|
||||
"description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"transfer"
|
||||
],
|
||||
"properties": {
|
||||
"transfer": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"channel_id",
|
||||
"timeout",
|
||||
"to_address"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"channel_id": {
|
||||
"description": "exisiting channel to send the tokens over",
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "when packet times out, measured on remote chain",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeout"
|
||||
}
|
||||
]
|
||||
},
|
||||
"to_address": {
|
||||
"description": "address on the remote chain to receive these tokens",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"send_packet"
|
||||
],
|
||||
"properties": {
|
||||
"send_packet": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"channel_id",
|
||||
"data",
|
||||
"timeout"
|
||||
],
|
||||
"properties": {
|
||||
"channel_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/definitions/Binary"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "when packet times out, measured on remote chain",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeout"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"close_channel"
|
||||
],
|
||||
"properties": {
|
||||
"close_channel": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"channel_id"
|
||||
],
|
||||
"properties": {
|
||||
"channel_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"IbcTimeout": {
|
||||
"description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeoutBlock"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"timestamp": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"IbcTimeoutBlock": {
|
||||
"description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"height",
|
||||
"revision"
|
||||
],
|
||||
"properties": {
|
||||
"height": {
|
||||
"description": "block height after which the packet times out. the height within the given revision",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"revision": {
|
||||
"description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"MemberChangedHookMsg": {
|
||||
"description": "MemberChangedHookMsg should be de/serialized under `MemberChangedHook()` variant in a ExecuteMsg. This contains a list of all diffs on the given transaction.",
|
||||
"type": "object",
|
||||
@@ -656,90 +344,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"StakingMsg": {
|
||||
"description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"delegate"
|
||||
],
|
||||
"properties": {
|
||||
"delegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"undelegate"
|
||||
],
|
||||
"properties": {
|
||||
"undelegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"redelegate"
|
||||
],
|
||||
"properties": {
|
||||
"redelegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"dst_validator",
|
||||
"src_validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"dst_validator": {
|
||||
"type": "string"
|
||||
},
|
||||
"src_validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"Timestamp": {
|
||||
"description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```",
|
||||
"allOf": [
|
||||
@@ -788,15 +392,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"VoteOption": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"yes",
|
||||
"no",
|
||||
"abstain",
|
||||
"no_with_veto"
|
||||
]
|
||||
},
|
||||
"WasmMsg": {
|
||||
"description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto",
|
||||
"oneOf": [
|
||||
|
||||
@@ -121,67 +121,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"staking"
|
||||
],
|
||||
"properties": {
|
||||
"staking": {
|
||||
"$ref": "#/definitions/StakingMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"distribution"
|
||||
],
|
||||
"properties": {
|
||||
"distribution": {
|
||||
"$ref": "#/definitions/DistributionMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"stargate"
|
||||
],
|
||||
"properties": {
|
||||
"stargate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type_url",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/Binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ibc"
|
||||
],
|
||||
"properties": {
|
||||
"ibc": {
|
||||
"$ref": "#/definitions/IbcMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -193,18 +132,6 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"gov"
|
||||
],
|
||||
"properties": {
|
||||
"gov": {
|
||||
"$ref": "#/definitions/GovMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -272,55 +199,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DistributionMsg": {
|
||||
"description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"set_withdraw_address"
|
||||
],
|
||||
"properties": {
|
||||
"set_withdraw_address": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"address"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
"description": "The `withdraw_address`",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"withdraw_delegator_reward"
|
||||
],
|
||||
"properties": {
|
||||
"withdraw_delegator_reward": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"validator": {
|
||||
"description": "The `validator_address`",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"Empty": {
|
||||
"description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)",
|
||||
"type": "object"
|
||||
@@ -372,196 +250,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"GovMsg": {
|
||||
"description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, StdError> { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, StdError> { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"vote"
|
||||
],
|
||||
"properties": {
|
||||
"vote": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"proposal_id",
|
||||
"vote"
|
||||
],
|
||||
"properties": {
|
||||
"proposal_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"vote": {
|
||||
"description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See <https://github.com/CosmWasm/cosmwasm/issues/1571>.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/VoteOption"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"IbcMsg": {
|
||||
"description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"transfer"
|
||||
],
|
||||
"properties": {
|
||||
"transfer": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"channel_id",
|
||||
"timeout",
|
||||
"to_address"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"channel_id": {
|
||||
"description": "exisiting channel to send the tokens over",
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "when packet times out, measured on remote chain",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeout"
|
||||
}
|
||||
]
|
||||
},
|
||||
"to_address": {
|
||||
"description": "address on the remote chain to receive these tokens",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"send_packet"
|
||||
],
|
||||
"properties": {
|
||||
"send_packet": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"channel_id",
|
||||
"data",
|
||||
"timeout"
|
||||
],
|
||||
"properties": {
|
||||
"channel_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/definitions/Binary"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "when packet times out, measured on remote chain",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeout"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"close_channel"
|
||||
],
|
||||
"properties": {
|
||||
"close_channel": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"channel_id"
|
||||
],
|
||||
"properties": {
|
||||
"channel_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"IbcTimeout": {
|
||||
"description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeoutBlock"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"timestamp": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"IbcTimeoutBlock": {
|
||||
"description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"height",
|
||||
"revision"
|
||||
],
|
||||
"properties": {
|
||||
"height": {
|
||||
"description": "block height after which the packet times out. the height within the given revision",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"revision": {
|
||||
"description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProposalResponse_for_Empty": {
|
||||
"description": "Note, if you are storing custom messages in the proposal, the querier needs to know what possible custom message types those are in order to parse the response",
|
||||
"type": "object",
|
||||
@@ -623,90 +311,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"StakingMsg": {
|
||||
"description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"delegate"
|
||||
],
|
||||
"properties": {
|
||||
"delegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"undelegate"
|
||||
],
|
||||
"properties": {
|
||||
"undelegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"redelegate"
|
||||
],
|
||||
"properties": {
|
||||
"redelegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"dst_validator",
|
||||
"src_validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"dst_validator": {
|
||||
"type": "string"
|
||||
},
|
||||
"src_validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"Status": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -857,15 +461,6 @@
|
||||
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
|
||||
"type": "string"
|
||||
},
|
||||
"VoteOption": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"yes",
|
||||
"no",
|
||||
"abstain",
|
||||
"no_with_veto"
|
||||
]
|
||||
},
|
||||
"WasmMsg": {
|
||||
"description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto",
|
||||
"oneOf": [
|
||||
|
||||
@@ -167,67 +167,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"staking"
|
||||
],
|
||||
"properties": {
|
||||
"staking": {
|
||||
"$ref": "#/definitions/StakingMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"distribution"
|
||||
],
|
||||
"properties": {
|
||||
"distribution": {
|
||||
"$ref": "#/definitions/DistributionMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"stargate"
|
||||
],
|
||||
"properties": {
|
||||
"stargate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type_url",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/Binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ibc"
|
||||
],
|
||||
"properties": {
|
||||
"ibc": {
|
||||
"$ref": "#/definitions/IbcMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -239,18 +178,6 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"gov"
|
||||
],
|
||||
"properties": {
|
||||
"gov": {
|
||||
"$ref": "#/definitions/GovMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -318,55 +245,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DistributionMsg": {
|
||||
"description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"set_withdraw_address"
|
||||
],
|
||||
"properties": {
|
||||
"set_withdraw_address": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"address"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
"description": "The `withdraw_address`",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"withdraw_delegator_reward"
|
||||
],
|
||||
"properties": {
|
||||
"withdraw_delegator_reward": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"validator": {
|
||||
"description": "The `validator_address`",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"Empty": {
|
||||
"description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)",
|
||||
"type": "object"
|
||||
@@ -418,280 +296,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"GovMsg": {
|
||||
"description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, StdError> { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, StdError> { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"vote"
|
||||
],
|
||||
"properties": {
|
||||
"vote": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"proposal_id",
|
||||
"vote"
|
||||
],
|
||||
"properties": {
|
||||
"proposal_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"vote": {
|
||||
"description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See <https://github.com/CosmWasm/cosmwasm/issues/1571>.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/VoteOption"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"IbcMsg": {
|
||||
"description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"transfer"
|
||||
],
|
||||
"properties": {
|
||||
"transfer": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"channel_id",
|
||||
"timeout",
|
||||
"to_address"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"channel_id": {
|
||||
"description": "exisiting channel to send the tokens over",
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "when packet times out, measured on remote chain",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeout"
|
||||
}
|
||||
]
|
||||
},
|
||||
"to_address": {
|
||||
"description": "address on the remote chain to receive these tokens",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"send_packet"
|
||||
],
|
||||
"properties": {
|
||||
"send_packet": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"channel_id",
|
||||
"data",
|
||||
"timeout"
|
||||
],
|
||||
"properties": {
|
||||
"channel_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/definitions/Binary"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "when packet times out, measured on remote chain",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeout"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"close_channel"
|
||||
],
|
||||
"properties": {
|
||||
"close_channel": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"channel_id"
|
||||
],
|
||||
"properties": {
|
||||
"channel_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"IbcTimeout": {
|
||||
"description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeoutBlock"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"timestamp": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"IbcTimeoutBlock": {
|
||||
"description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"height",
|
||||
"revision"
|
||||
],
|
||||
"properties": {
|
||||
"height": {
|
||||
"description": "block height after which the packet times out. the height within the given revision",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"revision": {
|
||||
"description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"StakingMsg": {
|
||||
"description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"delegate"
|
||||
],
|
||||
"properties": {
|
||||
"delegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"undelegate"
|
||||
],
|
||||
"properties": {
|
||||
"undelegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"redelegate"
|
||||
],
|
||||
"properties": {
|
||||
"redelegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"dst_validator",
|
||||
"src_validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"dst_validator": {
|
||||
"type": "string"
|
||||
},
|
||||
"src_validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"Status": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -842,15 +446,6 @@
|
||||
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
|
||||
"type": "string"
|
||||
},
|
||||
"VoteOption": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"yes",
|
||||
"no",
|
||||
"abstain",
|
||||
"no_with_veto"
|
||||
]
|
||||
},
|
||||
"WasmMsg": {
|
||||
"description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto",
|
||||
"oneOf": [
|
||||
|
||||
@@ -121,67 +121,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"staking"
|
||||
],
|
||||
"properties": {
|
||||
"staking": {
|
||||
"$ref": "#/definitions/StakingMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"distribution"
|
||||
],
|
||||
"properties": {
|
||||
"distribution": {
|
||||
"$ref": "#/definitions/DistributionMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"stargate"
|
||||
],
|
||||
"properties": {
|
||||
"stargate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type_url",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/Binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ibc"
|
||||
],
|
||||
"properties": {
|
||||
"ibc": {
|
||||
"$ref": "#/definitions/IbcMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -193,18 +132,6 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"gov"
|
||||
],
|
||||
"properties": {
|
||||
"gov": {
|
||||
"$ref": "#/definitions/GovMsg"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -272,55 +199,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DistributionMsg": {
|
||||
"description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"set_withdraw_address"
|
||||
],
|
||||
"properties": {
|
||||
"set_withdraw_address": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"address"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
"description": "The `withdraw_address`",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"withdraw_delegator_reward"
|
||||
],
|
||||
"properties": {
|
||||
"withdraw_delegator_reward": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"validator": {
|
||||
"description": "The `validator_address`",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"Empty": {
|
||||
"description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)",
|
||||
"type": "object"
|
||||
@@ -372,196 +250,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"GovMsg": {
|
||||
"description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, StdError> { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, StdError> { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"vote"
|
||||
],
|
||||
"properties": {
|
||||
"vote": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"proposal_id",
|
||||
"vote"
|
||||
],
|
||||
"properties": {
|
||||
"proposal_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"vote": {
|
||||
"description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See <https://github.com/CosmWasm/cosmwasm/issues/1571>.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/VoteOption"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"IbcMsg": {
|
||||
"description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"transfer"
|
||||
],
|
||||
"properties": {
|
||||
"transfer": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"channel_id",
|
||||
"timeout",
|
||||
"to_address"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"channel_id": {
|
||||
"description": "exisiting channel to send the tokens over",
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "when packet times out, measured on remote chain",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeout"
|
||||
}
|
||||
]
|
||||
},
|
||||
"to_address": {
|
||||
"description": "address on the remote chain to receive these tokens",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"send_packet"
|
||||
],
|
||||
"properties": {
|
||||
"send_packet": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"channel_id",
|
||||
"data",
|
||||
"timeout"
|
||||
],
|
||||
"properties": {
|
||||
"channel_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/definitions/Binary"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "when packet times out, measured on remote chain",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeout"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"close_channel"
|
||||
],
|
||||
"properties": {
|
||||
"close_channel": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"channel_id"
|
||||
],
|
||||
"properties": {
|
||||
"channel_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"IbcTimeout": {
|
||||
"description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IbcTimeoutBlock"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"timestamp": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"IbcTimeoutBlock": {
|
||||
"description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"height",
|
||||
"revision"
|
||||
],
|
||||
"properties": {
|
||||
"height": {
|
||||
"description": "block height after which the packet times out. the height within the given revision",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"revision": {
|
||||
"description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProposalResponse_for_Empty": {
|
||||
"description": "Note, if you are storing custom messages in the proposal, the querier needs to know what possible custom message types those are in order to parse the response",
|
||||
"type": "object",
|
||||
@@ -623,90 +311,6 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"StakingMsg": {
|
||||
"description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"delegate"
|
||||
],
|
||||
"properties": {
|
||||
"delegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"undelegate"
|
||||
],
|
||||
"properties": {
|
||||
"undelegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"redelegate"
|
||||
],
|
||||
"properties": {
|
||||
"redelegate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"dst_validator",
|
||||
"src_validator"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"dst_validator": {
|
||||
"type": "string"
|
||||
},
|
||||
"src_validator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"Status": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -857,15 +461,6 @@
|
||||
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
|
||||
"type": "string"
|
||||
},
|
||||
"VoteOption": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"yes",
|
||||
"no",
|
||||
"abstain",
|
||||
"no_with_veto"
|
||||
]
|
||||
},
|
||||
"WasmMsg": {
|
||||
"description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto",
|
||||
"oneOf": [
|
||||
|
||||
@@ -43,7 +43,7 @@ turn-off = true
|
||||
|
||||
[preprocessor.admonish]
|
||||
command = "mdbook-admonish"
|
||||
assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
|
||||
# variables preprocessor: import variables into files
|
||||
# https://gitlab.com/tglman/mdbook-variables/
|
||||
@@ -76,7 +76,7 @@ curly-quotes = true
|
||||
# mathjax-support = false # useful if we want to pull equations in ?
|
||||
copy-fonts = true
|
||||
no-section-label = false
|
||||
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "custom.css"]
|
||||
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "custom.css", "./mdbook-admonish.css"]
|
||||
additional-js = ["theme/pagetoc.js"]
|
||||
git-repository-url = "https://github.com/nymtech/nym"
|
||||
git-repository-icon = "fa-github"
|
||||
|
||||
@@ -1,31 +1,18 @@
|
||||
@charset "UTF-8";
|
||||
:root {
|
||||
--md-admonition-icon--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--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--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--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--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--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--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--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--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--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--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--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>");
|
||||
--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) {
|
||||
@@ -75,8 +62,9 @@ a.admonition-anchor-link::before {
|
||||
content: "§";
|
||||
}
|
||||
|
||||
:is(.admonition-title, summary) {
|
||||
:is(.admonition-title, summary.admonition-title) {
|
||||
position: relative;
|
||||
min-height: 4rem;
|
||||
margin-block: 0;
|
||||
margin-inline: -1.6rem -1.2rem;
|
||||
padding-block: 0.8rem;
|
||||
@@ -85,13 +73,13 @@ a.admonition-anchor-link::before {
|
||||
background-color: rgba(68, 138, 255, 0.1);
|
||||
display: flex;
|
||||
}
|
||||
:is(.admonition-title, summary) p {
|
||||
:is(.admonition-title, summary.admonition-title) p {
|
||||
margin: 0;
|
||||
}
|
||||
html :is(.admonition-title, summary):last-child {
|
||||
html :is(.admonition-title, summary.admonition-title):last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
:is(.admonition-title, summary)::before {
|
||||
:is(.admonition-title, summary.admonition-title)::before {
|
||||
position: absolute;
|
||||
top: 0.625em;
|
||||
inset-inline-start: 1.6rem;
|
||||
@@ -106,7 +94,7 @@ html :is(.admonition-title, summary):last-child {
|
||||
-webkit-mask-size: contain;
|
||||
content: "";
|
||||
}
|
||||
:is(.admonition-title, summary):hover a.admonition-anchor-link {
|
||||
:is(.admonition-title, summary.admonition-title):hover a.admonition-anchor-link {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
@@ -131,204 +119,204 @@ details[open].admonition > summary.admonition-title::after {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
:is(.admonition):is(.note) {
|
||||
:is(.admonition):is(.admonish-note) {
|
||||
border-color: #448aff;
|
||||
}
|
||||
|
||||
:is(.note) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(68, 138, 255, 0.1);
|
||||
}
|
||||
:is(.note) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #448aff;
|
||||
mask-image: var(--md-admonition-icon--note);
|
||||
-webkit-mask-image: var(--md-admonition-icon--note);
|
||||
mask-image: var(--md-admonition-icon--admonish-note);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-note);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.abstract, .summary, .tldr) {
|
||||
:is(.admonition):is(.admonish-abstract, .admonish-summary, .admonish-tldr) {
|
||||
border-color: #00b0ff;
|
||||
}
|
||||
|
||||
:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 176, 255, 0.1);
|
||||
}
|
||||
:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00b0ff;
|
||||
mask-image: var(--md-admonition-icon--abstract);
|
||||
-webkit-mask-image: var(--md-admonition-icon--abstract);
|
||||
mask-image: var(--md-admonition-icon--admonish-abstract);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-abstract);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.info, .todo) {
|
||||
:is(.admonition):is(.admonish-info, .admonish-todo) {
|
||||
border-color: #00b8d4;
|
||||
}
|
||||
|
||||
:is(.info, .todo) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 184, 212, 0.1);
|
||||
}
|
||||
:is(.info, .todo) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00b8d4;
|
||||
mask-image: var(--md-admonition-icon--info);
|
||||
-webkit-mask-image: var(--md-admonition-icon--info);
|
||||
mask-image: var(--md-admonition-icon--admonish-info);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-info);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.tip, .hint, .important) {
|
||||
:is(.admonition):is(.admonish-tip, .admonish-hint, .admonish-important) {
|
||||
border-color: #00bfa5;
|
||||
}
|
||||
|
||||
:is(.tip, .hint, .important) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 191, 165, 0.1);
|
||||
}
|
||||
:is(.tip, .hint, .important) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00bfa5;
|
||||
mask-image: var(--md-admonition-icon--tip);
|
||||
-webkit-mask-image: var(--md-admonition-icon--tip);
|
||||
mask-image: var(--md-admonition-icon--admonish-tip);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-tip);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.success, .check, .done) {
|
||||
:is(.admonition):is(.admonish-success, .admonish-check, .admonish-done) {
|
||||
border-color: #00c853;
|
||||
}
|
||||
|
||||
:is(.success, .check, .done) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 200, 83, 0.1);
|
||||
}
|
||||
:is(.success, .check, .done) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00c853;
|
||||
mask-image: var(--md-admonition-icon--success);
|
||||
-webkit-mask-image: var(--md-admonition-icon--success);
|
||||
mask-image: var(--md-admonition-icon--admonish-success);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-success);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.question, .help, .faq) {
|
||||
:is(.admonition):is(.admonish-question, .admonish-help, .admonish-faq) {
|
||||
border-color: #64dd17;
|
||||
}
|
||||
|
||||
:is(.question, .help, .faq) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(100, 221, 23, 0.1);
|
||||
}
|
||||
:is(.question, .help, .faq) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #64dd17;
|
||||
mask-image: var(--md-admonition-icon--question);
|
||||
-webkit-mask-image: var(--md-admonition-icon--question);
|
||||
mask-image: var(--md-admonition-icon--admonish-question);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-question);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.warning, .caution, .attention) {
|
||||
:is(.admonition):is(.admonish-warning, .admonish-caution, .admonish-attention) {
|
||||
border-color: #ff9100;
|
||||
}
|
||||
|
||||
:is(.warning, .caution, .attention) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 145, 0, 0.1);
|
||||
}
|
||||
:is(.warning, .caution, .attention) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff9100;
|
||||
mask-image: var(--md-admonition-icon--warning);
|
||||
-webkit-mask-image: var(--md-admonition-icon--warning);
|
||||
mask-image: var(--md-admonition-icon--admonish-warning);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-warning);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.failure, .fail, .missing) {
|
||||
:is(.admonition):is(.admonish-failure, .admonish-fail, .admonish-missing) {
|
||||
border-color: #ff5252;
|
||||
}
|
||||
|
||||
:is(.failure, .fail, .missing) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 82, 82, 0.1);
|
||||
}
|
||||
:is(.failure, .fail, .missing) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff5252;
|
||||
mask-image: var(--md-admonition-icon--failure);
|
||||
-webkit-mask-image: var(--md-admonition-icon--failure);
|
||||
mask-image: var(--md-admonition-icon--admonish-failure);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-failure);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.danger, .error) {
|
||||
:is(.admonition):is(.admonish-danger, .admonish-error) {
|
||||
border-color: #ff1744;
|
||||
}
|
||||
|
||||
:is(.danger, .error) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 23, 68, 0.1);
|
||||
}
|
||||
:is(.danger, .error) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff1744;
|
||||
mask-image: var(--md-admonition-icon--danger);
|
||||
-webkit-mask-image: var(--md-admonition-icon--danger);
|
||||
mask-image: var(--md-admonition-icon--admonish-danger);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-danger);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.bug) {
|
||||
:is(.admonition):is(.admonish-bug) {
|
||||
border-color: #f50057;
|
||||
}
|
||||
|
||||
:is(.bug) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(245, 0, 87, 0.1);
|
||||
}
|
||||
:is(.bug) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #f50057;
|
||||
mask-image: var(--md-admonition-icon--bug);
|
||||
-webkit-mask-image: var(--md-admonition-icon--bug);
|
||||
mask-image: var(--md-admonition-icon--admonish-bug);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-bug);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.example) {
|
||||
:is(.admonition):is(.admonish-example) {
|
||||
border-color: #7c4dff;
|
||||
}
|
||||
|
||||
:is(.example) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-example) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(124, 77, 255, 0.1);
|
||||
}
|
||||
:is(.example) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-example) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #7c4dff;
|
||||
mask-image: var(--md-admonition-icon--example);
|
||||
-webkit-mask-image: var(--md-admonition-icon--example);
|
||||
mask-image: var(--md-admonition-icon--admonish-example);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-example);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.quote, .cite) {
|
||||
:is(.admonition):is(.admonish-quote, .admonish-cite) {
|
||||
border-color: #9e9e9e;
|
||||
}
|
||||
|
||||
:is(.quote, .cite) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(158, 158, 158, 0.1);
|
||||
}
|
||||
:is(.quote, .cite) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #9e9e9e;
|
||||
mask-image: var(--md-admonition-icon--quote);
|
||||
-webkit-mask-image: var(--md-admonition-icon--quote);
|
||||
mask-image: var(--md-admonition-icon--admonish-quote);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-quote);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
@@ -339,7 +327,8 @@ details[open].admonition > summary.admonition-title::after {
|
||||
background-color: var(--sidebar-bg);
|
||||
}
|
||||
|
||||
.ayu :is(.admonition), .coal :is(.admonition) {
|
||||
.ayu :is(.admonition),
|
||||
.coal :is(.admonition) {
|
||||
background-color: var(--theme-hover);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
|
||||
|
||||
# Events
|
||||
|
||||
- [Web3Privacy Now](./events/web3-privacy.md)
|
||||
- [HCPP23-serinko](./events/hcpp23-serinko.md)
|
||||
- [HCPP23-max](./events/hcpp23-max.md)
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# Web3 Privacy Now - Nym for Ethereum validator privacy
|
||||
|
||||
Serinko's presentation on [Web3Privacy Now: Community 1st](https://lu.ma/web3privacynow_rome) introduces ***why network privacy matters*** for ETH 2.0 validators' security and decentralisation.
|
||||
|
||||
This page serves as an accessible list of references mentioned during the talk.
|
||||
|
||||
## References
|
||||
|
||||
### Mixnet architecture
|
||||
|
||||
* [Mixnet motivations](https://nymtech.net/developers/infrastructure/nym.html)
|
||||
* [Mixnet architecture overview](https://nymtech.net/docs/architecture/network-overview.html)
|
||||
* [Mixnet traffic flow](https://nymtech.net/docs/architecture/traffic-flow.html)
|
||||
* [Tor + VPN comparison](https://nymtech.net/developers/infrastructure/nym-vs-others.html)
|
||||
* [Addressing system](https://nymtech.net/docs/clients/addressing-system.html)
|
||||
|
||||
### Nym \<\> ETH 2.0
|
||||
|
||||
* [Chainsafe Rust libp2p Nym intergration repo](https://github.com/ChainSafe/rust-libp2p-nym)
|
||||
* [rust-libp2p-nym Transport Specification](https://hackmd.io/@nZ-twauPRISEa6G9zg3XRw/HkE8sHuns)
|
||||
* [Lighthouse PoC](https://github.com/ChainSafe/lighthouse/blob/nym/USE_NYM.md)
|
||||
* [Nym \<\> Aztec partnership](https://blog.nymtech.net/nym-partners-with-aztec-to-provide-integral-infrastructure-privacy-in-ethereum-chains-694963c55192)
|
||||
|
||||
### Rust Examples
|
||||
|
||||
* [Dev tutorial: chain service](https://nymtech.net/developers/tutorials/cosmos-service/intro.html)
|
||||
|
||||
### Clients
|
||||
|
||||
* [Clients overview](https://nymtech.net/docs/clients/overview.html)
|
||||
|
||||
### Nym Docs
|
||||
|
||||
* [Nym Developer Portal](https://nymtech.net/developers)
|
||||
* [Nym Operators Guide](https://nymtech.net/operators)
|
||||
* [Nym technical Documentation](https://nymtech.net/docs)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ turn-off = true
|
||||
|
||||
[preprocessor.admonish]
|
||||
command = "mdbook-admonish"
|
||||
assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
|
||||
# variables preprocessor: import variables into files
|
||||
# https://gitlab.com/tglman/mdbook-variables/
|
||||
@@ -83,7 +83,7 @@ curly-quotes = true
|
||||
# mathjax-support = false # useful if we want to pull equations in
|
||||
copy-fonts = true
|
||||
no-section-label = false
|
||||
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "./custom.css"]
|
||||
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "./custom.css", "./mdbook-admonish.css"]
|
||||
additional-js = ["theme/pagetoc.js"]
|
||||
git-repository-url = "https://github.com/nymtech/nym"
|
||||
git-repository-icon = "fa-github"
|
||||
|
||||
@@ -1,31 +1,18 @@
|
||||
@charset "UTF-8";
|
||||
:root {
|
||||
--md-admonition-icon--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--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--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--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--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--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--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--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--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--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--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--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>");
|
||||
--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) {
|
||||
@@ -75,8 +62,9 @@ a.admonition-anchor-link::before {
|
||||
content: "§";
|
||||
}
|
||||
|
||||
:is(.admonition-title, summary) {
|
||||
:is(.admonition-title, summary.admonition-title) {
|
||||
position: relative;
|
||||
min-height: 4rem;
|
||||
margin-block: 0;
|
||||
margin-inline: -1.6rem -1.2rem;
|
||||
padding-block: 0.8rem;
|
||||
@@ -85,13 +73,13 @@ a.admonition-anchor-link::before {
|
||||
background-color: rgba(68, 138, 255, 0.1);
|
||||
display: flex;
|
||||
}
|
||||
:is(.admonition-title, summary) p {
|
||||
:is(.admonition-title, summary.admonition-title) p {
|
||||
margin: 0;
|
||||
}
|
||||
html :is(.admonition-title, summary):last-child {
|
||||
html :is(.admonition-title, summary.admonition-title):last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
:is(.admonition-title, summary)::before {
|
||||
:is(.admonition-title, summary.admonition-title)::before {
|
||||
position: absolute;
|
||||
top: 0.625em;
|
||||
inset-inline-start: 1.6rem;
|
||||
@@ -106,7 +94,7 @@ html :is(.admonition-title, summary):last-child {
|
||||
-webkit-mask-size: contain;
|
||||
content: "";
|
||||
}
|
||||
:is(.admonition-title, summary):hover a.admonition-anchor-link {
|
||||
:is(.admonition-title, summary.admonition-title):hover a.admonition-anchor-link {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
@@ -131,204 +119,204 @@ details[open].admonition > summary.admonition-title::after {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
:is(.admonition):is(.note) {
|
||||
:is(.admonition):is(.admonish-note) {
|
||||
border-color: #448aff;
|
||||
}
|
||||
|
||||
:is(.note) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(68, 138, 255, 0.1);
|
||||
}
|
||||
:is(.note) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #448aff;
|
||||
mask-image: var(--md-admonition-icon--note);
|
||||
-webkit-mask-image: var(--md-admonition-icon--note);
|
||||
mask-image: var(--md-admonition-icon--admonish-note);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-note);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.abstract, .summary, .tldr) {
|
||||
:is(.admonition):is(.admonish-abstract, .admonish-summary, .admonish-tldr) {
|
||||
border-color: #00b0ff;
|
||||
}
|
||||
|
||||
:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 176, 255, 0.1);
|
||||
}
|
||||
:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00b0ff;
|
||||
mask-image: var(--md-admonition-icon--abstract);
|
||||
-webkit-mask-image: var(--md-admonition-icon--abstract);
|
||||
mask-image: var(--md-admonition-icon--admonish-abstract);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-abstract);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.info, .todo) {
|
||||
:is(.admonition):is(.admonish-info, .admonish-todo) {
|
||||
border-color: #00b8d4;
|
||||
}
|
||||
|
||||
:is(.info, .todo) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 184, 212, 0.1);
|
||||
}
|
||||
:is(.info, .todo) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00b8d4;
|
||||
mask-image: var(--md-admonition-icon--info);
|
||||
-webkit-mask-image: var(--md-admonition-icon--info);
|
||||
mask-image: var(--md-admonition-icon--admonish-info);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-info);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.tip, .hint, .important) {
|
||||
:is(.admonition):is(.admonish-tip, .admonish-hint, .admonish-important) {
|
||||
border-color: #00bfa5;
|
||||
}
|
||||
|
||||
:is(.tip, .hint, .important) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 191, 165, 0.1);
|
||||
}
|
||||
:is(.tip, .hint, .important) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00bfa5;
|
||||
mask-image: var(--md-admonition-icon--tip);
|
||||
-webkit-mask-image: var(--md-admonition-icon--tip);
|
||||
mask-image: var(--md-admonition-icon--admonish-tip);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-tip);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.success, .check, .done) {
|
||||
:is(.admonition):is(.admonish-success, .admonish-check, .admonish-done) {
|
||||
border-color: #00c853;
|
||||
}
|
||||
|
||||
:is(.success, .check, .done) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 200, 83, 0.1);
|
||||
}
|
||||
:is(.success, .check, .done) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00c853;
|
||||
mask-image: var(--md-admonition-icon--success);
|
||||
-webkit-mask-image: var(--md-admonition-icon--success);
|
||||
mask-image: var(--md-admonition-icon--admonish-success);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-success);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.question, .help, .faq) {
|
||||
:is(.admonition):is(.admonish-question, .admonish-help, .admonish-faq) {
|
||||
border-color: #64dd17;
|
||||
}
|
||||
|
||||
:is(.question, .help, .faq) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(100, 221, 23, 0.1);
|
||||
}
|
||||
:is(.question, .help, .faq) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #64dd17;
|
||||
mask-image: var(--md-admonition-icon--question);
|
||||
-webkit-mask-image: var(--md-admonition-icon--question);
|
||||
mask-image: var(--md-admonition-icon--admonish-question);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-question);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.warning, .caution, .attention) {
|
||||
:is(.admonition):is(.admonish-warning, .admonish-caution, .admonish-attention) {
|
||||
border-color: #ff9100;
|
||||
}
|
||||
|
||||
:is(.warning, .caution, .attention) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 145, 0, 0.1);
|
||||
}
|
||||
:is(.warning, .caution, .attention) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff9100;
|
||||
mask-image: var(--md-admonition-icon--warning);
|
||||
-webkit-mask-image: var(--md-admonition-icon--warning);
|
||||
mask-image: var(--md-admonition-icon--admonish-warning);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-warning);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.failure, .fail, .missing) {
|
||||
:is(.admonition):is(.admonish-failure, .admonish-fail, .admonish-missing) {
|
||||
border-color: #ff5252;
|
||||
}
|
||||
|
||||
:is(.failure, .fail, .missing) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 82, 82, 0.1);
|
||||
}
|
||||
:is(.failure, .fail, .missing) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff5252;
|
||||
mask-image: var(--md-admonition-icon--failure);
|
||||
-webkit-mask-image: var(--md-admonition-icon--failure);
|
||||
mask-image: var(--md-admonition-icon--admonish-failure);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-failure);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.danger, .error) {
|
||||
:is(.admonition):is(.admonish-danger, .admonish-error) {
|
||||
border-color: #ff1744;
|
||||
}
|
||||
|
||||
:is(.danger, .error) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 23, 68, 0.1);
|
||||
}
|
||||
:is(.danger, .error) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff1744;
|
||||
mask-image: var(--md-admonition-icon--danger);
|
||||
-webkit-mask-image: var(--md-admonition-icon--danger);
|
||||
mask-image: var(--md-admonition-icon--admonish-danger);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-danger);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.bug) {
|
||||
:is(.admonition):is(.admonish-bug) {
|
||||
border-color: #f50057;
|
||||
}
|
||||
|
||||
:is(.bug) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(245, 0, 87, 0.1);
|
||||
}
|
||||
:is(.bug) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #f50057;
|
||||
mask-image: var(--md-admonition-icon--bug);
|
||||
-webkit-mask-image: var(--md-admonition-icon--bug);
|
||||
mask-image: var(--md-admonition-icon--admonish-bug);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-bug);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.example) {
|
||||
:is(.admonition):is(.admonish-example) {
|
||||
border-color: #7c4dff;
|
||||
}
|
||||
|
||||
:is(.example) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-example) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(124, 77, 255, 0.1);
|
||||
}
|
||||
:is(.example) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-example) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #7c4dff;
|
||||
mask-image: var(--md-admonition-icon--example);
|
||||
-webkit-mask-image: var(--md-admonition-icon--example);
|
||||
mask-image: var(--md-admonition-icon--admonish-example);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-example);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.quote, .cite) {
|
||||
:is(.admonition):is(.admonish-quote, .admonish-cite) {
|
||||
border-color: #9e9e9e;
|
||||
}
|
||||
|
||||
:is(.quote, .cite) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(158, 158, 158, 0.1);
|
||||
}
|
||||
:is(.quote, .cite) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #9e9e9e;
|
||||
mask-image: var(--md-admonition-icon--quote);
|
||||
-webkit-mask-image: var(--md-admonition-icon--quote);
|
||||
mask-image: var(--md-admonition-icon--admonish-quote);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-quote);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
@@ -339,7 +327,8 @@ details[open].admonition > summary.admonition-title::after {
|
||||
background-color: var(--sidebar-bg);
|
||||
}
|
||||
|
||||
.ayu :is(.admonition), .coal :is(.admonition) {
|
||||
.ayu :is(.admonition),
|
||||
.coal :is(.admonition) {
|
||||
background-color: var(--theme-hover);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ turn-off = true
|
||||
|
||||
[preprocessor.admonish]
|
||||
command = "mdbook-admonish"
|
||||
assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
|
||||
# variables preprocessor: import variables into files
|
||||
# https://gitlab.com/tglman/mdbook-variables/
|
||||
@@ -83,7 +83,7 @@ curly-quotes = true
|
||||
# mathjax-support = false # useful if we want to pull equations in
|
||||
copy-fonts = true
|
||||
no-section-label = false
|
||||
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css","./custom.css"]
|
||||
additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css","./custom.css", "./mdbook-admonish.css"]
|
||||
additional-js = ["theme/pagetoc.js"]
|
||||
git-repository-url = "https://github.com/nymtech/nym"
|
||||
git-repository-icon = "fa-github"
|
||||
|
||||
@@ -1,31 +1,18 @@
|
||||
@charset "UTF-8";
|
||||
:root {
|
||||
--md-admonition-icon--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--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--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--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--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--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--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--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--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--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--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--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>");
|
||||
--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) {
|
||||
@@ -75,8 +62,9 @@ a.admonition-anchor-link::before {
|
||||
content: "§";
|
||||
}
|
||||
|
||||
:is(.admonition-title, summary) {
|
||||
:is(.admonition-title, summary.admonition-title) {
|
||||
position: relative;
|
||||
min-height: 4rem;
|
||||
margin-block: 0;
|
||||
margin-inline: -1.6rem -1.2rem;
|
||||
padding-block: 0.8rem;
|
||||
@@ -85,13 +73,13 @@ a.admonition-anchor-link::before {
|
||||
background-color: rgba(68, 138, 255, 0.1);
|
||||
display: flex;
|
||||
}
|
||||
:is(.admonition-title, summary) p {
|
||||
:is(.admonition-title, summary.admonition-title) p {
|
||||
margin: 0;
|
||||
}
|
||||
html :is(.admonition-title, summary):last-child {
|
||||
html :is(.admonition-title, summary.admonition-title):last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
:is(.admonition-title, summary)::before {
|
||||
:is(.admonition-title, summary.admonition-title)::before {
|
||||
position: absolute;
|
||||
top: 0.625em;
|
||||
inset-inline-start: 1.6rem;
|
||||
@@ -106,7 +94,7 @@ html :is(.admonition-title, summary):last-child {
|
||||
-webkit-mask-size: contain;
|
||||
content: "";
|
||||
}
|
||||
:is(.admonition-title, summary):hover a.admonition-anchor-link {
|
||||
:is(.admonition-title, summary.admonition-title):hover a.admonition-anchor-link {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
@@ -131,204 +119,204 @@ details[open].admonition > summary.admonition-title::after {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
:is(.admonition):is(.note) {
|
||||
:is(.admonition):is(.admonish-note) {
|
||||
border-color: #448aff;
|
||||
}
|
||||
|
||||
:is(.note) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(68, 138, 255, 0.1);
|
||||
}
|
||||
:is(.note) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #448aff;
|
||||
mask-image: var(--md-admonition-icon--note);
|
||||
-webkit-mask-image: var(--md-admonition-icon--note);
|
||||
mask-image: var(--md-admonition-icon--admonish-note);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-note);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.abstract, .summary, .tldr) {
|
||||
:is(.admonition):is(.admonish-abstract, .admonish-summary, .admonish-tldr) {
|
||||
border-color: #00b0ff;
|
||||
}
|
||||
|
||||
:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 176, 255, 0.1);
|
||||
}
|
||||
:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00b0ff;
|
||||
mask-image: var(--md-admonition-icon--abstract);
|
||||
-webkit-mask-image: var(--md-admonition-icon--abstract);
|
||||
mask-image: var(--md-admonition-icon--admonish-abstract);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-abstract);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.info, .todo) {
|
||||
:is(.admonition):is(.admonish-info, .admonish-todo) {
|
||||
border-color: #00b8d4;
|
||||
}
|
||||
|
||||
:is(.info, .todo) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 184, 212, 0.1);
|
||||
}
|
||||
:is(.info, .todo) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00b8d4;
|
||||
mask-image: var(--md-admonition-icon--info);
|
||||
-webkit-mask-image: var(--md-admonition-icon--info);
|
||||
mask-image: var(--md-admonition-icon--admonish-info);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-info);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.tip, .hint, .important) {
|
||||
:is(.admonition):is(.admonish-tip, .admonish-hint, .admonish-important) {
|
||||
border-color: #00bfa5;
|
||||
}
|
||||
|
||||
:is(.tip, .hint, .important) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 191, 165, 0.1);
|
||||
}
|
||||
:is(.tip, .hint, .important) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00bfa5;
|
||||
mask-image: var(--md-admonition-icon--tip);
|
||||
-webkit-mask-image: var(--md-admonition-icon--tip);
|
||||
mask-image: var(--md-admonition-icon--admonish-tip);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-tip);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.success, .check, .done) {
|
||||
:is(.admonition):is(.admonish-success, .admonish-check, .admonish-done) {
|
||||
border-color: #00c853;
|
||||
}
|
||||
|
||||
:is(.success, .check, .done) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(0, 200, 83, 0.1);
|
||||
}
|
||||
:is(.success, .check, .done) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #00c853;
|
||||
mask-image: var(--md-admonition-icon--success);
|
||||
-webkit-mask-image: var(--md-admonition-icon--success);
|
||||
mask-image: var(--md-admonition-icon--admonish-success);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-success);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.question, .help, .faq) {
|
||||
:is(.admonition):is(.admonish-question, .admonish-help, .admonish-faq) {
|
||||
border-color: #64dd17;
|
||||
}
|
||||
|
||||
:is(.question, .help, .faq) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(100, 221, 23, 0.1);
|
||||
}
|
||||
:is(.question, .help, .faq) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #64dd17;
|
||||
mask-image: var(--md-admonition-icon--question);
|
||||
-webkit-mask-image: var(--md-admonition-icon--question);
|
||||
mask-image: var(--md-admonition-icon--admonish-question);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-question);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.warning, .caution, .attention) {
|
||||
:is(.admonition):is(.admonish-warning, .admonish-caution, .admonish-attention) {
|
||||
border-color: #ff9100;
|
||||
}
|
||||
|
||||
:is(.warning, .caution, .attention) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 145, 0, 0.1);
|
||||
}
|
||||
:is(.warning, .caution, .attention) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff9100;
|
||||
mask-image: var(--md-admonition-icon--warning);
|
||||
-webkit-mask-image: var(--md-admonition-icon--warning);
|
||||
mask-image: var(--md-admonition-icon--admonish-warning);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-warning);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.failure, .fail, .missing) {
|
||||
:is(.admonition):is(.admonish-failure, .admonish-fail, .admonish-missing) {
|
||||
border-color: #ff5252;
|
||||
}
|
||||
|
||||
:is(.failure, .fail, .missing) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 82, 82, 0.1);
|
||||
}
|
||||
:is(.failure, .fail, .missing) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff5252;
|
||||
mask-image: var(--md-admonition-icon--failure);
|
||||
-webkit-mask-image: var(--md-admonition-icon--failure);
|
||||
mask-image: var(--md-admonition-icon--admonish-failure);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-failure);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.danger, .error) {
|
||||
:is(.admonition):is(.admonish-danger, .admonish-error) {
|
||||
border-color: #ff1744;
|
||||
}
|
||||
|
||||
:is(.danger, .error) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(255, 23, 68, 0.1);
|
||||
}
|
||||
:is(.danger, .error) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #ff1744;
|
||||
mask-image: var(--md-admonition-icon--danger);
|
||||
-webkit-mask-image: var(--md-admonition-icon--danger);
|
||||
mask-image: var(--md-admonition-icon--admonish-danger);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-danger);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.bug) {
|
||||
:is(.admonition):is(.admonish-bug) {
|
||||
border-color: #f50057;
|
||||
}
|
||||
|
||||
:is(.bug) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(245, 0, 87, 0.1);
|
||||
}
|
||||
:is(.bug) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #f50057;
|
||||
mask-image: var(--md-admonition-icon--bug);
|
||||
-webkit-mask-image: var(--md-admonition-icon--bug);
|
||||
mask-image: var(--md-admonition-icon--admonish-bug);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-bug);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.example) {
|
||||
:is(.admonition):is(.admonish-example) {
|
||||
border-color: #7c4dff;
|
||||
}
|
||||
|
||||
:is(.example) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-example) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(124, 77, 255, 0.1);
|
||||
}
|
||||
:is(.example) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-example) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #7c4dff;
|
||||
mask-image: var(--md-admonition-icon--example);
|
||||
-webkit-mask-image: var(--md-admonition-icon--example);
|
||||
mask-image: var(--md-admonition-icon--admonish-example);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-example);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
:is(.admonition):is(.quote, .cite) {
|
||||
:is(.admonition):is(.admonish-quote, .admonish-cite) {
|
||||
border-color: #9e9e9e;
|
||||
}
|
||||
|
||||
:is(.quote, .cite) > :is(.admonition-title, summary) {
|
||||
:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title) {
|
||||
background-color: rgba(158, 158, 158, 0.1);
|
||||
}
|
||||
:is(.quote, .cite) > :is(.admonition-title, summary)::before {
|
||||
:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title)::before {
|
||||
background-color: #9e9e9e;
|
||||
mask-image: var(--md-admonition-icon--quote);
|
||||
-webkit-mask-image: var(--md-admonition-icon--quote);
|
||||
mask-image: var(--md-admonition-icon--admonish-quote);
|
||||
-webkit-mask-image: var(--md-admonition-icon--admonish-quote);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
@@ -339,7 +327,8 @@ details[open].admonition > summary.admonition-title::after {
|
||||
background-color: var(--sidebar-bg);
|
||||
}
|
||||
|
||||
.ayu :is(.admonition), .coal :is(.admonition) {
|
||||
.ayu :is(.admonition),
|
||||
.coal :is(.admonition) {
|
||||
background-color: var(--theme-hover);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
> We aim on purpose to make minimal changes to reward scheme and software. We're just 'smooshing' together stuff we already debugged and know works.
|
||||
> -- Harry Halpin, Nym CEO
|
||||
<p>
|
||||
<p></p>
|
||||
|
||||
This page refer to the changes which are planned to take place over Q3 and Q4 2023. As this is a transition period in the beginning (Q3 2023) the [Mix Nodes FAQ page](./mixnodes-faq.md) holds more answers to the current setup as project Smoosh refers to the eventual setup. As project Smoosh gets progressively implemented the answers on this page will become to be more relevant to the current state and eventually this FAQ page will be merged with the still relevant parts of the main Mix Nodes FAQ page.
|
||||
</p>
|
||||
|
||||
If any questions are not answered or it's not clear for you in which stage project Smoosh is right now, please reach out in Node Operators [Matrix room](https://matrix.to/#/#operators:nymtech.chat).
|
||||
|
||||
## Overview
|
||||
@@ -40,7 +41,7 @@ Yes, to run a mix node only is an option. However it will be less rewarded as no
|
||||
|
||||
### What are the incentives for the node operator?
|
||||
|
||||
In the original setup there were no incentives to run a `network-requester`. After the transition all the users will buy multiple tickets of zkNyms credentials and use those as [anonymous e-cash](https://arxiv.org/abs/2303.08221) to pay for their data traffic (`[Nym API](https://github.com/nymtech/nym/tree/master/nym-api)` will do the do cryptographical checks to prevent double-spending). All collected fees get distributed to all active nodes proportionally to their work by the end of each epoch.
|
||||
In the original setup there were no incentives to run a `network-requester`. After the transition all the users will buy multiple tickets of zkNyms credentials and use those as [anonymous e-cash](https://arxiv.org/abs/2303.08221) to pay for their data traffic ([`Nym API`](https://github.com/nymtech/nym/tree/master/nym-api) will do the do cryptographical checks to prevent double-spending). All collected fees get distributed to all active nodes proportionally to their work by the end of each epoch.
|
||||
|
||||
### How does this change the token economics?
|
||||
|
||||
|
||||
+7
-3
@@ -37,14 +37,16 @@ nym-ephemera-common = { path = "../common/cosmwasm-smart-contracts/ephemera" }
|
||||
pretty_env_logger = "0.4"
|
||||
refinery = { version = "0.8.7", features = ["rusqlite"], optional = true }
|
||||
reqwest = { version = "0.11.6", features = ["json"] }
|
||||
rocksdb = { version = "0.21.0", optional = true }
|
||||
# Rocksdb kills compilation times and we're not currently using it. The reason
|
||||
# we comment it out is that rust-analyzer runs with --all-features
|
||||
#rocksdb = { version = "0.21.0", optional = true }
|
||||
rusqlite = { version = "0.27.0", features = ["bundled"], optional = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_derive = "1.0.149"
|
||||
serde_json = "1.0.91"
|
||||
thiserror = "1.0.37"
|
||||
tokio = { version = "1", features = ["macros", "net","rt-multi-thread"] }
|
||||
tokio-tungstenite = "0.18.0"
|
||||
tokio-tungstenite = { workspace = true }
|
||||
tokio-util = { version = "0.7.4", features = ["full"] }
|
||||
toml = "0.7.0"
|
||||
unsigned-varint = "0.7.1"
|
||||
@@ -61,5 +63,7 @@ rand = "0.8.5"
|
||||
|
||||
[features]
|
||||
default = ["sqlite_storage"]
|
||||
rocksdb_storage = ["rocksdb"]
|
||||
# Rocksdb kills compilation times and we're not currently using it. The reason
|
||||
# we comment it out is that rust-analyzer runs with --all-features
|
||||
#rocksdb_storage = ["rocksdb"]
|
||||
sqlite_storage = ["rusqlite", "refinery"]
|
||||
|
||||
@@ -20,7 +20,7 @@ pub(crate) async fn submit_message(
|
||||
api: web::Data<CommandExecutor>,
|
||||
) -> HttpResponse {
|
||||
match api.send_ephemera_message(message.into_inner()).await {
|
||||
Ok(_) => HttpResponse::Ok().json("Message submitted"),
|
||||
Ok(()) => HttpResponse::Ok().json("Message submitted"),
|
||||
Err(err) => {
|
||||
if let ApiError::DuplicateMessage = err {
|
||||
debug!("Message already submitted {err:?}");
|
||||
@@ -53,7 +53,7 @@ pub(crate) async fn store_in_dht(
|
||||
let value = request.value();
|
||||
|
||||
match api.store_in_dht(key, value).await {
|
||||
Ok(_) => HttpResponse::Ok().json("Store request submitted"),
|
||||
Ok(()) => HttpResponse::Ok().json("Store request submitted"),
|
||||
Err(err) => {
|
||||
error!("Error storing in dht: {}", err);
|
||||
HttpResponse::InternalServerError().json("Server failed to process request")
|
||||
|
||||
@@ -278,7 +278,7 @@ impl BlockManager {
|
||||
}
|
||||
|
||||
match self.message_pool.remove_messages(&block.messages) {
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
self.block_chain_state
|
||||
.mark_last_produced_block_as_committed();
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ impl ApiCmdProcessor {
|
||||
.send_ephemera_event(EphemeraEvent::StoreInDht { key, value })
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) => {
|
||||
error!("Error sending StoreInDht to network: {:?}", err);
|
||||
Err(ApiError::Internal("Failed to store in DHT".to_string()))
|
||||
@@ -153,7 +153,7 @@ impl ApiCmdProcessor {
|
||||
.send_ephemera_event(EphemeraEvent::QueryDht { key: key.clone() })
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
//Save the reply channel in a map and send the reply when we get the response from the network
|
||||
ephemera
|
||||
.api_cmd_processor
|
||||
@@ -278,7 +278,7 @@ impl ApiCmdProcessor {
|
||||
// Send to BlockManager to verify it and put into memory pool
|
||||
let ephemera_msg: message::EphemeraMessage = (*api_msg).into();
|
||||
match ephemera.block_manager.on_new_message(ephemera_msg.clone()) {
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
//Gossip to network for other nodes to receive
|
||||
match ephemera
|
||||
.to_network
|
||||
@@ -287,7 +287,7 @@ impl ApiCmdProcessor {
|
||||
))
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) => {
|
||||
error!("Error sending EphemeraMessage to network: {:?}", err);
|
||||
Err(ApiError::Internal("Failed to submit message".to_string()))
|
||||
|
||||
@@ -267,7 +267,7 @@ impl<A: Application> EphemeraStarterWithApplication<A> {
|
||||
}
|
||||
ws_stopped = websocket.run() => {
|
||||
match ws_stopped {
|
||||
Ok(_) => info!("Websocket stopped unexpectedly"),
|
||||
Ok(()) => info!("Websocket stopped unexpectedly"),
|
||||
Err(e) => error!("Websocket stopped with error: {}", e),
|
||||
}
|
||||
}
|
||||
@@ -293,7 +293,7 @@ impl<A: Application> EphemeraStarterWithApplication<A> {
|
||||
}
|
||||
http_stopped = http => {
|
||||
match http_stopped {
|
||||
Ok(_) => info!("Http server stopped unexpectedly"),
|
||||
Ok(()) => info!("Http server stopped unexpectedly"),
|
||||
Err(e) => error!("Http server stopped with error: {}", e),
|
||||
}
|
||||
}
|
||||
@@ -330,7 +330,7 @@ impl<A: Application> EphemeraStarterWithApplication<A> {
|
||||
}
|
||||
nw_stopped = network.start() => {
|
||||
match nw_stopped {
|
||||
Ok(_) => info!("Network stopped unexpectedly"),
|
||||
Ok(()) => info!("Network stopped unexpectedly"),
|
||||
Err(e) => error!("Network stopped with error: {e}",),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ impl<A: Application> Ephemera<A> {
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = shutdown.recv() => {
|
||||
() = shutdown.recv() => {
|
||||
trace!("UpdateHandler: Received shutdown");
|
||||
self.shutdown_manager.stop().await;
|
||||
break;
|
||||
|
||||
@@ -57,7 +57,7 @@ impl ShutdownManager {
|
||||
.map(|(i, h)| (i + 1, h))
|
||||
{
|
||||
match handle.await.unwrap() {
|
||||
Ok(_) => info!("Task {i} finished successfully"),
|
||||
Ok(()) => info!("Task {i} finished successfully"),
|
||||
Err(e) => info!("Task {i} finished with error: {e}",),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ impl ConnectionHandler for Handler {
|
||||
match event {
|
||||
ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound {
|
||||
protocol: stream,
|
||||
info: _,
|
||||
info: (),
|
||||
}) => {
|
||||
if self.inbound_substream_attempts > MAX_SUBSTREAM_ATTEMPTS {
|
||||
log::warn!("Too many inbound substream attempts, refusing stream");
|
||||
@@ -320,7 +320,7 @@ impl ConnectionHandler for Handler {
|
||||
}
|
||||
ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound {
|
||||
protocol,
|
||||
info: _,
|
||||
info: (),
|
||||
}) => {
|
||||
if self.outbound_substream_attempts > MAX_SUBSTREAM_ATTEMPTS {
|
||||
log::warn!("Too many outbound substream attempts, refusing stream");
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.29"
|
||||
version = "1.1.30"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
dotenvy = "0.15.6"
|
||||
humantime-serde = "1.0"
|
||||
|
||||
+29
-6
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.29"
|
||||
version = "1.1.30"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
@@ -15,6 +15,13 @@ rust-version = "1.56"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
axum = "0.6.20"
|
||||
sha2 = "0.10.8"
|
||||
hmac = "0.12.1"
|
||||
axum-macros = "0.3.8" # Useful for debugging axum Handler trait errors
|
||||
fastrand = "2"
|
||||
x25519-dalek = { version = "2.0.0", features = ["static_secrets"] }
|
||||
base64 = "0.21.4"
|
||||
anyhow = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
atty = "0.2"
|
||||
@@ -34,12 +41,23 @@ pretty_env_logger = "0.4"
|
||||
rand = "0.7"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "sqlite", "macros", "migrate", ] }
|
||||
sqlx = { version = "0.5", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
] }
|
||||
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
|
||||
thiserror = "1"
|
||||
tokio = { workspace = true, features = [ "rt-multi-thread", "net", "signal", "fs", "time" ] }
|
||||
tokio = { workspace = true, features = [
|
||||
"rt-multi-thread",
|
||||
"net",
|
||||
"signal",
|
||||
"fs",
|
||||
"time",
|
||||
] }
|
||||
tokio-stream = { version = "0.1.11", features = ["fs"] }
|
||||
tokio-tungstenite = "0.14"
|
||||
tokio-tungstenite = { version = "0.20.1" }
|
||||
tokio-util = { version = "0.7.4", features = ["codec"] }
|
||||
url = { version = "2.2", features = ["serde"] }
|
||||
zeroize = { workspace = true }
|
||||
@@ -62,7 +80,12 @@ nym-statistics-common = { path = "../common/statistics" }
|
||||
nym-task = { path = "../common/task" }
|
||||
nym-types = { path = "../common/types" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-wireguard = { path = "../common/wireguard" }
|
||||
nym-wireguard = { path = "../common/wireguard", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tower = "0.4.13"
|
||||
rand = "0.8.5"
|
||||
hyper = "0.14.27"
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
||||
@@ -74,4 +97,4 @@ sqlx = { version = "0.5", features = [
|
||||
] }
|
||||
|
||||
[features]
|
||||
wireguard = []
|
||||
wireguard = ["nym-wireguard"]
|
||||
|
||||
@@ -28,6 +28,6 @@ nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
|
||||
[dependencies.tungstenite]
|
||||
version = "0.13.0"
|
||||
workspace = true
|
||||
default-features = false
|
||||
|
||||
|
||||
@@ -106,6 +106,16 @@ pub(crate) enum GatewayError {
|
||||
#[from]
|
||||
source: NyxdError,
|
||||
},
|
||||
#[error("Error verifying hmac digest")]
|
||||
HmacDigestError {
|
||||
#[from]
|
||||
source: hmac::digest::MacError,
|
||||
},
|
||||
#[error("Invalid hmac length")]
|
||||
HmacInvalidLength {
|
||||
#[from]
|
||||
source: hmac::digest::InvalidLength,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ClientCoreError> for GatewayError {
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
net::SocketAddr,
|
||||
ops::Deref,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use hmac::{Hmac, Mac};
|
||||
use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use x25519_dalek::StaticSecret;
|
||||
|
||||
use crate::error::GatewayError;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) enum ClientMessage {
|
||||
Init(InitMessage),
|
||||
Final(Client),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) struct InitMessage {
|
||||
pub_key: ClientPublicKey,
|
||||
}
|
||||
|
||||
impl InitMessage {
|
||||
pub fn pub_key(&self) -> &ClientPublicKey {
|
||||
&self.pub_key
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn new(pub_key: ClientPublicKey) -> Self {
|
||||
InitMessage { pub_key }
|
||||
}
|
||||
}
|
||||
|
||||
// Client that wants to register sends its PublicKey and SocketAddr bytes mac digest encrypted with a DH shared secret.
|
||||
// Gateway can then verify pub_key payload using the sme process
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) struct Client {
|
||||
// base64 encoded public key, using x25519-dalek for impl
|
||||
pub(crate) pub_key: ClientPublicKey,
|
||||
pub(crate) socket: SocketAddr,
|
||||
pub(crate) mac: ClientMac,
|
||||
}
|
||||
|
||||
pub type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
impl Client {
|
||||
// Reusable secret should be gateways Wireguard PK
|
||||
// Client should perform this step when generating its payload, using its own WG PK
|
||||
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), GatewayError> {
|
||||
#[allow(clippy::expect_used)]
|
||||
let static_secret =
|
||||
StaticSecret::try_from(gateway_key.to_bytes()).expect("This is infalliable");
|
||||
let dh = static_secret.diffie_hellman(&self.pub_key);
|
||||
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())?;
|
||||
mac.update(self.pub_key.as_bytes());
|
||||
mac.update(self.socket.ip().to_string().as_bytes());
|
||||
mac.update(self.socket.port().to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
Ok(mac.verify_slice(&self.mac)?)
|
||||
}
|
||||
|
||||
pub fn pub_key(&self) -> &ClientPublicKey {
|
||||
&self.pub_key
|
||||
}
|
||||
|
||||
pub fn socket(&self) -> SocketAddr {
|
||||
self.socket
|
||||
}
|
||||
}
|
||||
|
||||
// This should go into nym-wireguard crate
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ClientPublicKey(x25519_dalek::PublicKey);
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ClientMac(Vec<u8>);
|
||||
|
||||
impl ClientMac {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(mac: Vec<u8>) -> Self {
|
||||
ClientMac(mac)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClientMac {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientPublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", general_purpose::STANDARD.encode(self.0.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for ClientPublicKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.as_bytes().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ClientMac {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mac_bytes: Vec<u8> = general_purpose::STANDARD
|
||||
.decode(s)
|
||||
.map_err(|_| "Could not base64 decode public key mac representation".to_string())?;
|
||||
Ok(ClientMac(mac_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClientMac {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded_key = general_purpose::STANDARD.encode(self.0.clone());
|
||||
serializer.serialize_str(&encoded_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientMac {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let encoded_key = String::deserialize(deserializer)?;
|
||||
ClientMac::from_str(&encoded_key).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientPublicKey {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(key: x25519_dalek::PublicKey) -> Self {
|
||||
ClientPublicKey(key)
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClientPublicKey {
|
||||
type Target = x25519_dalek::PublicKey;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ClientPublicKey {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let key_bytes: [u8; 32] = general_purpose::STANDARD
|
||||
.decode(s)
|
||||
.map_err(|_| "Could not base64 decode public key representation".to_string())?
|
||||
.try_into()
|
||||
.map_err(|_| "Invalid key length".to_string())?;
|
||||
Ok(ClientPublicKey(x25519_dalek::PublicKey::from(key_bytes)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClientPublicKey {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded_key = general_purpose::STANDARD.encode(self.0.as_bytes());
|
||||
serializer.serialize_str(&encoded_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientPublicKey {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let encoded_key = String::deserialize(deserializer)?;
|
||||
Ok(ClientPublicKey::from_str(&encoded_key).map_err(serde::de::Error::custom))?
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ClientRegistry = HashMap<SocketAddr, Client>;
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
pub(crate) mod active_clients;
|
||||
mod bandwidth;
|
||||
pub(crate) mod client_registration;
|
||||
pub(crate) mod embedded_network_requester;
|
||||
pub(crate) mod websocket;
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ impl CoconutVerifier {
|
||||
let req = nym_api_requests::coconut::VerifyCredentialBody::new(
|
||||
credential.clone(),
|
||||
proposal_id,
|
||||
self.nyxd_client.address().clone(),
|
||||
self.nyxd_client.address(),
|
||||
);
|
||||
for client in api_clients {
|
||||
self.nyxd_client
|
||||
|
||||
@@ -19,11 +19,11 @@ mod authenticated;
|
||||
pub(crate) mod coconut;
|
||||
mod fresh;
|
||||
|
||||
//// TODO: note for my future self to consider the following idea:
|
||||
//// split the socket connection into sink and stream
|
||||
//// stream will be for reading explicit requests
|
||||
//// and sink for pumping responses AND mix traffic
|
||||
//// but as byproduct this might (or might not) break the clean "SocketStream" enum here
|
||||
// TODO: note for my future self to consider the following idea:
|
||||
// split the socket connection into sink and stream
|
||||
// stream will be for reading explicit requests
|
||||
// and sink for pumping responses AND mix traffic
|
||||
// but as byproduct this might (or might not) break the clean "SocketStream" enum here
|
||||
|
||||
pub(crate) enum SocketStream<S> {
|
||||
RawTcp(S),
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub(crate) mod v1;
|
||||
@@ -0,0 +1,105 @@
|
||||
use axum::extract::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use axum::{extract::State, Json};
|
||||
use std::str::FromStr;
|
||||
|
||||
// use axum_macros::debug_handler;
|
||||
|
||||
use crate::node::client_handling::client_registration::{
|
||||
Client, ClientMessage, ClientPublicKey, InitMessage,
|
||||
};
|
||||
use crate::node::http::ApiState;
|
||||
|
||||
async fn process_final_message(client: Client, state: Arc<ApiState>) -> StatusCode {
|
||||
let preshared_nonce = {
|
||||
let in_progress_ro = state.registration_in_progress.read().await;
|
||||
if let Some(nonce) = in_progress_ro.get(client.pub_key()) {
|
||||
*nonce
|
||||
} else {
|
||||
return StatusCode::BAD_REQUEST;
|
||||
}
|
||||
};
|
||||
|
||||
if client
|
||||
.verify(state.sphinx_key_pair.private_key(), preshared_nonce)
|
||||
.is_ok()
|
||||
{
|
||||
{
|
||||
let mut in_progress_rw = state.registration_in_progress.write().await;
|
||||
in_progress_rw.remove(client.pub_key());
|
||||
}
|
||||
{
|
||||
let mut registry_rw = state.client_registry.write().await;
|
||||
registry_rw.insert(client.socket(), client);
|
||||
}
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
|
||||
async fn process_init_message(init_message: InitMessage, state: Arc<ApiState>) -> u64 {
|
||||
let nonce: u64 = fastrand::u64(..);
|
||||
let mut registry_rw = state.registration_in_progress.write().await;
|
||||
registry_rw.insert(init_message.pub_key().clone(), nonce);
|
||||
nonce
|
||||
}
|
||||
|
||||
// #[debug_handler]
|
||||
pub(crate) async fn register_client(
|
||||
State(state): State<Arc<ApiState>>,
|
||||
Json(payload): Json<ClientMessage>,
|
||||
) -> (StatusCode, Json<Option<u64>>) {
|
||||
match payload {
|
||||
ClientMessage::Init(i) => (
|
||||
StatusCode::OK,
|
||||
Json(Some(process_init_message(i, Arc::clone(&state)).await)),
|
||||
),
|
||||
ClientMessage::Final(client) => (
|
||||
process_final_message(client, Arc::clone(&state)).await,
|
||||
Json(None),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_all_clients(
|
||||
State(state): State<Arc<ApiState>>,
|
||||
) -> (StatusCode, Json<Vec<ClientPublicKey>>) {
|
||||
let registry_ro = state.client_registry.read().await;
|
||||
(
|
||||
StatusCode::OK,
|
||||
Json(
|
||||
registry_ro
|
||||
.values()
|
||||
.map(|c| c.pub_key().clone())
|
||||
.collect::<Vec<ClientPublicKey>>(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_client(
|
||||
Path(pub_key): Path<String>,
|
||||
State(state): State<Arc<ApiState>>,
|
||||
) -> (StatusCode, Json<Vec<Client>>) {
|
||||
let pub_key = match ClientPublicKey::from_str(&pub_key) {
|
||||
Ok(pub_key) => pub_key,
|
||||
Err(_) => return (StatusCode::BAD_REQUEST, Json(vec![])),
|
||||
};
|
||||
let registry_ro = state.client_registry.read().await;
|
||||
let clients = registry_ro
|
||||
.iter()
|
||||
.filter_map(|(_, c)| {
|
||||
if c.pub_key() == &pub_key {
|
||||
Some(c.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Client>>();
|
||||
if clients.is_empty() {
|
||||
return (StatusCode::NOT_FOUND, Json(clients));
|
||||
}
|
||||
(StatusCode::OK, Json(clients))
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub(crate) mod client_registry;
|
||||
@@ -0,0 +1,206 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use axum::{
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
use log::info;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
mod api;
|
||||
use api::v1::client_registry::*;
|
||||
|
||||
use super::client_handling::client_registration::{ClientPublicKey, ClientRegistry};
|
||||
|
||||
const ROUTE_PREFIX: &str = "/api/v1/gateway/client-interfaces/wireguard";
|
||||
|
||||
pub struct ApiState {
|
||||
client_registry: Arc<RwLock<ClientRegistry>>,
|
||||
sphinx_key_pair: Arc<encryption::KeyPair>,
|
||||
registration_in_progress: Arc<RwLock<HashMap<ClientPublicKey, u64>>>,
|
||||
}
|
||||
|
||||
fn router_with_state(state: Arc<ApiState>) -> Router {
|
||||
Router::new()
|
||||
.route(&format!("{}/clients", ROUTE_PREFIX), get(get_all_clients))
|
||||
.route(&format!("{}/client", ROUTE_PREFIX), post(register_client))
|
||||
.route(
|
||||
&format!("{}/client/:pub_key", ROUTE_PREFIX),
|
||||
get(get_client),
|
||||
)
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
pub(crate) async fn start_http_api(
|
||||
client_registry: Arc<RwLock<ClientRegistry>>,
|
||||
sphinx_key_pair: Arc<encryption::KeyPair>,
|
||||
) {
|
||||
// Port should be 80 post smoosh
|
||||
let port = 8000;
|
||||
|
||||
info!("Started HTTP API on port {}", port);
|
||||
|
||||
let client_registry = Arc::clone(&client_registry);
|
||||
|
||||
let state = Arc::new(ApiState {
|
||||
client_registry,
|
||||
sphinx_key_pair,
|
||||
registration_in_progress: Arc::new(RwLock::new(HashMap::new())),
|
||||
});
|
||||
|
||||
let routes = router_with_state(state);
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
axum::Server::bind(&format!("0.0.0.0:{}", port).parse().unwrap())
|
||||
.serve(routes.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::net::SocketAddr;
|
||||
use std::str::FromStr;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use axum::body::Body;
|
||||
use axum::http::Request;
|
||||
use axum::http::StatusCode;
|
||||
use hmac::Mac;
|
||||
use tower::Service;
|
||||
use tower::ServiceExt;
|
||||
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use tokio::sync::RwLock;
|
||||
use x25519_dalek::{PublicKey, StaticSecret};
|
||||
|
||||
use crate::node::client_handling::client_registration::{
|
||||
Client, ClientMac, ClientMessage, InitMessage,
|
||||
};
|
||||
use crate::node::client_handling::client_registration::{ClientPublicKey, HmacSha256};
|
||||
use crate::node::http::{router_with_state, ApiState, ROUTE_PREFIX};
|
||||
|
||||
#[tokio::test]
|
||||
async fn registration() {
|
||||
// 1. Provision random keys for gateway and client
|
||||
// 2. Generate DH shared secret
|
||||
// 3. Client submits its public key to the gateway to start the handshake process, gateway responds with nonce
|
||||
// 4. Client generates mac digest using DH shared secret, its own public key, socket address and port, and nonce
|
||||
// 5. Client sends its public key, socket address and port, nonce and mac digest to the gateway
|
||||
// 6. Gateway verifies mac digest and nonce, and stores client's public key and socket address and port
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let gateway_key_pair = encryption::KeyPair::new(&mut rng);
|
||||
let client_key_pair = encryption::KeyPair::new(&mut rng);
|
||||
|
||||
let gateway_static_public =
|
||||
PublicKey::try_from(gateway_key_pair.public_key().to_bytes()).unwrap();
|
||||
|
||||
let client_static_private =
|
||||
StaticSecret::try_from(client_key_pair.private_key().to_bytes()).unwrap();
|
||||
let client_static_public =
|
||||
PublicKey::try_from(client_key_pair.public_key().to_bytes()).unwrap();
|
||||
|
||||
let client_dh = client_static_private.diffie_hellman(&gateway_static_public);
|
||||
|
||||
let registration_in_progress = Arc::new(RwLock::new(HashMap::new()));
|
||||
let client_registry = Arc::new(RwLock::new(HashMap::new()));
|
||||
|
||||
let state = Arc::new(ApiState {
|
||||
client_registry: Arc::clone(&client_registry),
|
||||
sphinx_key_pair: Arc::new(gateway_key_pair),
|
||||
registration_in_progress: Arc::clone(®istration_in_progress),
|
||||
});
|
||||
|
||||
// `Router` implements `tower::Service<Request<Body>>` so we can
|
||||
// call it like any tower service, no need to run an HTTP server.
|
||||
let mut app = router_with_state(state);
|
||||
|
||||
let init_message =
|
||||
ClientMessage::Init(InitMessage::new(ClientPublicKey::new(client_static_public)));
|
||||
|
||||
let init_request = Request::builder()
|
||||
.method("POST")
|
||||
.uri(format!("{}/client", ROUTE_PREFIX))
|
||||
.header("Content-type", "application/json")
|
||||
.body(Body::from(serde_json::to_vec(&init_message).unwrap()))
|
||||
.unwrap();
|
||||
|
||||
let response = ServiceExt::<Request<Body>>::ready(&mut app)
|
||||
.await
|
||||
.unwrap()
|
||||
.call(init_request)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert!(!registration_in_progress.read().await.is_empty());
|
||||
|
||||
let nonce: Option<u64> =
|
||||
serde_json::from_slice(&hyper::body::to_bytes(response.into_body()).await.unwrap())
|
||||
.unwrap();
|
||||
assert!(nonce.is_some());
|
||||
|
||||
let mut mac = HmacSha256::new_from_slice(client_dh.as_bytes()).unwrap();
|
||||
mac.update(client_static_public.as_bytes());
|
||||
mac.update("127.0.0.1".as_bytes());
|
||||
mac.update("8080".as_bytes());
|
||||
mac.update(&nonce.unwrap().to_le_bytes());
|
||||
let mac = mac.finalize().into_bytes();
|
||||
|
||||
let finalized_message = ClientMessage::Final(Client {
|
||||
pub_key: ClientPublicKey::new(client_static_public),
|
||||
socket: SocketAddr::from_str("127.0.0.1:8080").unwrap(),
|
||||
mac: ClientMac::new(mac.as_slice().to_vec()),
|
||||
});
|
||||
|
||||
let final_request = Request::builder()
|
||||
.method("POST")
|
||||
.uri(format!("{}/client", ROUTE_PREFIX))
|
||||
.header("Content-type", "application/json")
|
||||
.body(Body::from(serde_json::to_vec(&finalized_message).unwrap()))
|
||||
.unwrap();
|
||||
|
||||
let response = ServiceExt::<Request<Body>>::ready(&mut app)
|
||||
.await
|
||||
.unwrap()
|
||||
.call(final_request)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert!(!client_registry.read().await.is_empty());
|
||||
|
||||
let clients_request = Request::builder()
|
||||
.method("GET")
|
||||
.uri(format!("{}/clients", ROUTE_PREFIX))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let response = ServiceExt::<Request<Body>>::ready(&mut app)
|
||||
.await
|
||||
.unwrap()
|
||||
.call(clients_request)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let clients: Vec<ClientPublicKey> =
|
||||
serde_json::from_slice(&hyper::body::to_bytes(response.into_body()).await.unwrap())
|
||||
.unwrap();
|
||||
|
||||
assert!(!clients.is_empty());
|
||||
|
||||
let ro_clients = client_registry.read().await.clone();
|
||||
assert_eq!(
|
||||
ro_clients
|
||||
.values()
|
||||
.map(|c| c.pub_key().clone())
|
||||
.collect::<Vec<ClientPublicKey>>(),
|
||||
clients
|
||||
)
|
||||
}
|
||||
}
|
||||
+16
-1
@@ -1,6 +1,7 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use self::client_handling::client_registration::ClientRegistry;
|
||||
use self::storage::PersistentStorage;
|
||||
use crate::commands::helpers::{override_network_requester_config, OverrideNetworkRequesterConfig};
|
||||
use crate::config::Config;
|
||||
@@ -12,6 +13,7 @@ use crate::node::client_handling::embedded_network_requester::{
|
||||
use crate::node::client_handling::websocket;
|
||||
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
|
||||
use crate::node::helpers::{initialise_main_storage, load_network_requester_config};
|
||||
use crate::node::http::start_http_api;
|
||||
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
|
||||
use crate::node::statistics::collector::GatewayStatisticsCollector;
|
||||
use crate::node::storage::Storage;
|
||||
@@ -27,13 +29,16 @@ use nym_task::{TaskClient, TaskManager};
|
||||
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub(crate) mod client_handling;
|
||||
pub(crate) mod helpers;
|
||||
pub(crate) mod http;
|
||||
pub(crate) mod mixnet_handling;
|
||||
pub(crate) mod statistics;
|
||||
pub(crate) mod storage;
|
||||
@@ -85,6 +90,8 @@ pub(crate) struct Gateway<St = PersistentStorage> {
|
||||
/// x25519 keypair used for Diffie-Hellman. Currently only used for sphinx key derivation.
|
||||
sphinx_keypair: Arc<encryption::KeyPair>,
|
||||
storage: St,
|
||||
|
||||
client_registry: Arc<RwLock<ClientRegistry>>,
|
||||
}
|
||||
|
||||
impl<St> Gateway<St> {
|
||||
@@ -100,6 +107,7 @@ impl<St> Gateway<St> {
|
||||
sphinx_keypair: Arc::new(helpers::load_sphinx_keys(&config)?),
|
||||
config,
|
||||
network_requester_opts,
|
||||
client_registry: Arc::new(RwLock::new(HashMap::new())),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -117,6 +125,7 @@ impl<St> Gateway<St> {
|
||||
identity_keypair: Arc::new(identity_keypair),
|
||||
sphinx_keypair: Arc::new(sphinx_keypair),
|
||||
storage,
|
||||
client_registry: Arc::new(RwLock::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,11 +379,17 @@ impl<St> Gateway<St> {
|
||||
// Once this is a bit more mature, make this a commandline flag instead of a compile time
|
||||
// flag
|
||||
#[cfg(feature = "wireguard")]
|
||||
if let Err(err) = nym_wireguard::start_wg_listener(shutdown.subscribe()).await {
|
||||
if let Err(err) = nym_wireguard::start_wireguard(shutdown.subscribe()).await {
|
||||
// that's a nasty workaround, but anyhow errors are generally nicer, especially on exit
|
||||
bail!("{err}")
|
||||
}
|
||||
|
||||
// This should likely be wireguard feature gated, but its easier to test if it hangs in here
|
||||
tokio::spawn(start_http_api(
|
||||
Arc::clone(&self.client_registry),
|
||||
Arc::clone(&self.sphinx_keypair),
|
||||
));
|
||||
|
||||
info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!");
|
||||
|
||||
if let Err(err) = Self::wait_for_interrupt(shutdown).await {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.30"
|
||||
version = "1.1.31"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-api"
|
||||
version = "1.1.30"
|
||||
version = "1.1.31"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
@@ -48,7 +48,7 @@ impl Epoch {
|
||||
let start_time = NaiveDateTime::from_timestamp_opt(info.start_time, 0)
|
||||
.ok_or("Invalid start time")
|
||||
.unwrap();
|
||||
let start_time: DateTime<Utc> = DateTime::from_utc(start_time, Utc);
|
||||
let start_time: DateTime<Utc> = DateTime::from_naive_utc_and_offset(start_time, Utc);
|
||||
let interval = tokio::time::interval(std::time::Duration::from_secs(info.duration));
|
||||
Self {
|
||||
start_time,
|
||||
|
||||
Generated
-814
@@ -1,814 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"ghash",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95c2fcf79ad1932ac6269a738109997a83c227c09b75842ae564dc8ede6a861c"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bip39"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f"
|
||||
dependencies = [
|
||||
"bitcoin_hashes",
|
||||
"serde",
|
||||
"unicode-normalization",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin_hashes"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4"
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "extension-storage"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
"serde-wasm-bindgen",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-utils",
|
||||
"wee_alloc",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
|
||||
dependencies = [
|
||||
"opaque-debug",
|
||||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexed_db_futures"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfbcff6ae46750b15cc594bfd277b188cbddcfdc1817848f97f03f26f8625b9e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"uuid",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memory_units"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
|
||||
|
||||
[[package]]
|
||||
name = "nym-store-cipher"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2",
|
||||
"generic-array",
|
||||
"getrandom",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.163"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.163"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-utils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"indexed_db_futures",
|
||||
"js-sys",
|
||||
"nym-store-cipher",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wee_alloc"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"memory_units",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "extension-storage"
|
||||
version = "1.2.0-rc.9"
|
||||
version = "1.2.0-rc.10"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/nymtech/nym"
|
||||
|
||||
Generated
+17
-35
@@ -1524,6 +1524,12 @@ dependencies = [
|
||||
"parking_lot_core 0.9.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
|
||||
[[package]]
|
||||
name = "debugid"
|
||||
version = "0.8.0"
|
||||
@@ -3211,15 +3217,6 @@ dependencies = [
|
||||
"generic-array 0.14.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "input_buffer"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
@@ -5807,7 +5804,7 @@ dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"sct 0.7.0",
|
||||
"webpki 0.22.0",
|
||||
"webpki 0.22.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6305,19 +6302,6 @@ dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.5"
|
||||
@@ -7431,7 +7415,7 @@ checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
||||
dependencies = [
|
||||
"rustls 0.20.8",
|
||||
"tokio",
|
||||
"webpki 0.22.0",
|
||||
"webpki 0.22.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7460,13 +7444,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.14.0"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e96bb520beab540ab664bd5a9cfeaa1fcd846fa68c830b42e2c8963071251d2"
|
||||
checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"pin-project",
|
||||
"tokio",
|
||||
"tungstenite",
|
||||
]
|
||||
@@ -7637,19 +7620,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.13.0"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093"
|
||||
checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http",
|
||||
"httparse",
|
||||
"input_buffer",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"sha-1",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
@@ -8090,9 +8072,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
version = "0.22.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
@@ -8113,7 +8095,7 @@ version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
|
||||
dependencies = [
|
||||
"webpki 0.22.0",
|
||||
"webpki 0.22.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"@nymproject/mui-theme": "^1.0.0",
|
||||
"@nymproject/react": "^1.0.0",
|
||||
"@nymproject/types": "^1.0.0",
|
||||
"@nymproject/node-tester": ">=1.2.0-rc.5",
|
||||
"@nymproject/node-tester": ">=1.2.0-rc.8",
|
||||
"@storybook/react": "^6.5.15",
|
||||
"@tauri-apps/api": "^1.2.0",
|
||||
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
|
||||
|
||||
@@ -42,17 +42,18 @@ fn main() {
|
||||
mixnet::account::connect_with_mnemonic,
|
||||
mixnet::account::create_new_mnemonic,
|
||||
mixnet::account::create_password,
|
||||
mixnet::account::update_password,
|
||||
mixnet::account::does_password_file_exist,
|
||||
mixnet::account::get_balance,
|
||||
mixnet::account::list_accounts,
|
||||
mixnet::account::logout,
|
||||
mixnet::account::remove_account_for_password,
|
||||
mixnet::account::remove_password,
|
||||
mixnet::account::rename_account_for_password,
|
||||
mixnet::account::show_mnemonic_for_account_in_password,
|
||||
mixnet::account::sign_in_with_password,
|
||||
mixnet::account::sign_in_with_password_and_account_id,
|
||||
mixnet::account::switch_network,
|
||||
mixnet::account::update_password,
|
||||
mixnet::account::validate_mnemonic,
|
||||
mixnet::admin::get_contract_settings,
|
||||
mixnet::admin::update_contract_settings,
|
||||
|
||||
@@ -484,7 +484,8 @@ pub async fn add_account_for_password(
|
||||
})
|
||||
}
|
||||
|
||||
// The first `AccoundId` when converting is the `LoginId` for the entry that was loaded.
|
||||
// Set the tauri state with all the accounts in the wallet.
|
||||
// NOTE: the first `AccoundId` when converting is the `LoginId` for the entry that was loaded.
|
||||
async fn set_state_with_all_accounts(
|
||||
stored_login: wallet_storage::StoredLogin,
|
||||
first_id_when_converting: wallet_storage::AccountId,
|
||||
@@ -547,6 +548,28 @@ pub async fn remove_account_for_password(
|
||||
set_state_with_all_accounts(stored_login, first_account_id_when_converting, state).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn rename_account_for_password(
|
||||
password: UserPassword,
|
||||
account_id: &str,
|
||||
new_account_id: &str,
|
||||
state: tauri::State<'_, WalletState>,
|
||||
) -> Result<(), BackendError> {
|
||||
log::info!("Renaming account: {account_id} to {new_account_id}");
|
||||
// Currently we only support a single, default, id in the wallet
|
||||
let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string());
|
||||
let account_id = wallet_storage::AccountId::new(account_id.to_string());
|
||||
let new_account_id = wallet_storage::AccountId::new(new_account_id.to_string());
|
||||
wallet_storage::rename_account_in_login(&login_id, &account_id, &new_account_id, &password)?;
|
||||
|
||||
// Load from storage to reset the internal tuari state
|
||||
let stored_login = wallet_storage::load_existing_login(&login_id, &password)?;
|
||||
// NOTE: Since we removed from a multi-account login, this id shouldn't be needed, but setting
|
||||
// the state is supposed to be a general function
|
||||
let first_account_id_when_converting = login_id.into();
|
||||
set_state_with_all_accounts(stored_login, first_account_id_when_converting, state).await
|
||||
}
|
||||
|
||||
fn derive_address(
|
||||
mnemonic: bip39::Mnemonic,
|
||||
prefix: &str,
|
||||
|
||||
@@ -226,6 +226,10 @@ impl MultipleAccounts {
|
||||
self.accounts.iter().find(|account| &account.id == id)
|
||||
}
|
||||
|
||||
pub(crate) fn get_account_mut(&mut self, id: &AccountId) -> Option<&mut WalletAccount> {
|
||||
self.accounts.iter_mut().find(|account| &account.id == id)
|
||||
}
|
||||
|
||||
pub(crate) fn get_account_with_mnemonic(
|
||||
&self,
|
||||
mnemonic: &bip39::Mnemonic,
|
||||
@@ -273,6 +277,21 @@ impl MultipleAccounts {
|
||||
self.accounts.retain(|accounts| &accounts.id != id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn rename(
|
||||
&mut self,
|
||||
id: &AccountId,
|
||||
new_id: &AccountId,
|
||||
) -> Result<(), BackendError> {
|
||||
if self.get_account(new_id).is_some() {
|
||||
return Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin);
|
||||
}
|
||||
let account = self
|
||||
.get_account_mut(id)
|
||||
.ok_or(BackendError::WalletNoSuchAccountIdInWalletLogin)?;
|
||||
account.rename_id(new_id.clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<WalletAccount>> for MultipleAccounts {
|
||||
@@ -300,6 +319,10 @@ impl WalletAccount {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub(crate) fn rename_id(&mut self, new_id: AccountId) {
|
||||
self.id = new_id;
|
||||
}
|
||||
|
||||
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
|
||||
match self.account {
|
||||
AccountData::Mnemonic(ref account) => account.mnemonic(),
|
||||
|
||||
@@ -425,6 +425,49 @@ fn remove_account_from_login_at_file(
|
||||
}
|
||||
}
|
||||
|
||||
/// Rename an account inside the encrypted login.
|
||||
/// - If the encrypted login is just a single account, abort to be on the safe side.
|
||||
/// - If the name already exists, abort.
|
||||
pub(crate) fn rename_account_in_login(
|
||||
id: &LoginId,
|
||||
account_id: &AccountId,
|
||||
new_account_id: &AccountId,
|
||||
password: &UserPassword,
|
||||
) -> Result<(), BackendError> {
|
||||
let store_dir = get_storage_directory()?;
|
||||
let filepath = store_dir.join(WALLET_INFO_FILENAME);
|
||||
rename_account_in_login_at_file(&filepath, id, account_id, new_account_id, password)
|
||||
}
|
||||
|
||||
fn rename_account_in_login_at_file(
|
||||
filepath: &Path,
|
||||
id: &LoginId,
|
||||
account_id: &AccountId,
|
||||
new_account_id: &AccountId,
|
||||
password: &UserPassword,
|
||||
) -> Result<(), BackendError> {
|
||||
log::info!("Renaming associated account in login account: {id}");
|
||||
let mut stored_wallet = load_existing_wallet_at_file(filepath)?;
|
||||
|
||||
let mut decrypted_login = stored_wallet.decrypt_login(id, password)?;
|
||||
|
||||
// Rename the account
|
||||
match decrypted_login {
|
||||
StoredLogin::Mnemonic(_) => {
|
||||
log::warn!("Encountered mnemonic login instead of list of accounts, aborting");
|
||||
return Err(BackendError::WalletUnexpectedMnemonicAccount);
|
||||
}
|
||||
StoredLogin::Multiple(ref mut accounts) => {
|
||||
accounts.rename(account_id, new_account_id)?;
|
||||
}
|
||||
};
|
||||
|
||||
// Encrypt the new updated login and write to file
|
||||
let encrypted_accounts = EncryptedLogin::encrypt(id.clone(), &decrypted_login, password)?;
|
||||
stored_wallet.replace_encrypted_login(encrypted_accounts)?;
|
||||
write_to_file(filepath, &stored_wallet)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::wallet_storage::account_data::{MnemonicAccount, WalletAccount};
|
||||
@@ -1513,11 +1556,61 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password,),
|
||||
append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password),
|
||||
Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin),
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_the_same_account_name_twice_fails() {
|
||||
let store_dir = tempdir().unwrap();
|
||||
let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME);
|
||||
let mnemonic1 = Mnemonic::generate(24).unwrap();
|
||||
let mnemonic2 = Mnemonic::generate(24).unwrap();
|
||||
let mnemonic3 = Mnemonic::generate(24).unwrap();
|
||||
let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap();
|
||||
let password = UserPassword::new("password".to_string());
|
||||
// The top-level login id. NOTE: the first account id is always set to default.
|
||||
let login_id = LoginId::new("my_login_id".to_string());
|
||||
|
||||
// Store the first account under login_id. The first account id is always set to default
|
||||
// name.
|
||||
store_login_with_multiple_accounts_at_file(
|
||||
&wallet_file,
|
||||
mnemonic1.clone(),
|
||||
hd_path.clone(),
|
||||
login_id.clone(),
|
||||
&password,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Append another account (account2) to the same login (login_id)
|
||||
let account2 = AccountId::new("account_2".to_string());
|
||||
|
||||
append_account_to_login_at_file(
|
||||
&wallet_file,
|
||||
mnemonic2.clone(),
|
||||
hd_path.clone(),
|
||||
login_id.clone(),
|
||||
account2.clone(),
|
||||
&password,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Appending the third account, with same account id will fail
|
||||
assert!(matches!(
|
||||
append_account_to_login_at_file(
|
||||
&wallet_file,
|
||||
mnemonic3.clone(),
|
||||
hd_path,
|
||||
login_id,
|
||||
account2,
|
||||
&password,
|
||||
),
|
||||
Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin),
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_the_same_account_twice_for_a_login_fails() {
|
||||
let store_dir = tempdir().unwrap();
|
||||
@@ -1841,6 +1934,204 @@ mod tests {
|
||||
assert_eq!(account.hd_path(), &hd_path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_first_account_in_login() {
|
||||
let store_dir = tempdir().unwrap();
|
||||
let wallet = store_dir.path().join(WALLET_INFO_FILENAME);
|
||||
let account1 = Mnemonic::generate(24).unwrap();
|
||||
let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap();
|
||||
let password = UserPassword::new("password".to_string());
|
||||
let login_id = LoginId::new("first".to_string());
|
||||
|
||||
store_login_with_multiple_accounts_at_file(
|
||||
&wallet,
|
||||
account1.clone(),
|
||||
hd_path.clone(),
|
||||
login_id.clone(),
|
||||
&password,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap();
|
||||
let loaded_accounts = loaded_login.as_multiple_accounts().unwrap();
|
||||
let expected = vec![WalletAccount::new(
|
||||
DEFAULT_FIRST_ACCOUNT_NAME.into(),
|
||||
MnemonicAccount::new(account1.clone(), hd_path.clone()),
|
||||
)]
|
||||
.into();
|
||||
assert_eq!(loaded_accounts, &expected);
|
||||
|
||||
let renamed_account = AccountId::new("new_first".to_string());
|
||||
|
||||
rename_account_in_login_at_file(
|
||||
&wallet,
|
||||
&login_id,
|
||||
&DEFAULT_FIRST_ACCOUNT_NAME.into(),
|
||||
&renamed_account.clone(),
|
||||
&password,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap();
|
||||
let loaded_accounts = loaded_login.as_multiple_accounts().unwrap();
|
||||
let expected = vec![WalletAccount::new(
|
||||
renamed_account.clone(),
|
||||
MnemonicAccount::new(account1.clone(), hd_path.clone()),
|
||||
)]
|
||||
.into();
|
||||
assert_eq!(loaded_accounts, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_one_account_in_login_with_two_accounts() {
|
||||
let store_dir = tempdir().unwrap();
|
||||
let wallet = store_dir.path().join(WALLET_INFO_FILENAME);
|
||||
let account1 = Mnemonic::generate(24).unwrap();
|
||||
let account2 = Mnemonic::generate(24).unwrap();
|
||||
let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap();
|
||||
let password = UserPassword::new("password".to_string());
|
||||
let login_id = LoginId::new("first".to_string());
|
||||
let account_id2 = AccountId::new("second".to_string());
|
||||
let renamed_account_id2 = AccountId::new("new_second".to_string());
|
||||
|
||||
store_login_with_multiple_accounts_at_file(
|
||||
&wallet,
|
||||
account1.clone(),
|
||||
hd_path.clone(),
|
||||
login_id.clone(),
|
||||
&password,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
append_account_to_login_at_file(
|
||||
&wallet,
|
||||
account2.clone(),
|
||||
hd_path.clone(),
|
||||
login_id.clone(),
|
||||
account_id2.clone(),
|
||||
&password,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Load and confirm
|
||||
let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap();
|
||||
let loaded_accounts = loaded_login.as_multiple_accounts().unwrap();
|
||||
let expected = vec![
|
||||
WalletAccount::new(
|
||||
DEFAULT_FIRST_ACCOUNT_NAME.into(),
|
||||
MnemonicAccount::new(account1.clone(), hd_path.clone()),
|
||||
),
|
||||
WalletAccount::new(
|
||||
account_id2.clone(),
|
||||
MnemonicAccount::new(account2.clone(), hd_path.clone()),
|
||||
),
|
||||
]
|
||||
.into();
|
||||
assert_eq!(loaded_accounts, &expected);
|
||||
|
||||
// Rename the second account to a new name
|
||||
rename_account_in_login_at_file(
|
||||
&wallet,
|
||||
&login_id,
|
||||
&account_id2,
|
||||
&renamed_account_id2,
|
||||
&password,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Load and confirm
|
||||
let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap();
|
||||
let loaded_accounts = loaded_login.as_multiple_accounts().unwrap();
|
||||
let expected = vec![
|
||||
WalletAccount::new(
|
||||
DEFAULT_FIRST_ACCOUNT_NAME.into(),
|
||||
MnemonicAccount::new(account1, hd_path.clone()),
|
||||
),
|
||||
WalletAccount::new(
|
||||
renamed_account_id2.clone(),
|
||||
MnemonicAccount::new(account2, hd_path.clone()),
|
||||
),
|
||||
]
|
||||
.into();
|
||||
assert_eq!(loaded_accounts, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_account_into_existing_account_id_fails() {
|
||||
let store_dir = tempdir().unwrap();
|
||||
let wallet = store_dir.path().join(WALLET_INFO_FILENAME);
|
||||
let account1 = Mnemonic::generate(24).unwrap();
|
||||
let account2 = Mnemonic::generate(24).unwrap();
|
||||
let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap();
|
||||
let password = UserPassword::new("password".to_string());
|
||||
let login_id = LoginId::new("first".to_string());
|
||||
let account_id2 = AccountId::new("second".to_string());
|
||||
let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into();
|
||||
|
||||
store_login_with_multiple_accounts_at_file(
|
||||
&wallet,
|
||||
account1.clone(),
|
||||
hd_path.clone(),
|
||||
login_id.clone(),
|
||||
&password,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
append_account_to_login_at_file(
|
||||
&wallet,
|
||||
account2.clone(),
|
||||
hd_path.clone(),
|
||||
login_id.clone(),
|
||||
account_id2.clone(),
|
||||
&password,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Load and confirm
|
||||
let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap();
|
||||
let loaded_accounts = loaded_login.as_multiple_accounts().unwrap();
|
||||
let expected = vec![
|
||||
WalletAccount::new(
|
||||
DEFAULT_FIRST_ACCOUNT_NAME.into(),
|
||||
MnemonicAccount::new(account1.clone(), hd_path.clone()),
|
||||
),
|
||||
WalletAccount::new(
|
||||
account_id2.clone(),
|
||||
MnemonicAccount::new(account2.clone(), hd_path.clone()),
|
||||
),
|
||||
]
|
||||
.into();
|
||||
assert_eq!(loaded_accounts, &expected);
|
||||
|
||||
// Rename the second account to the name of the first one fails
|
||||
assert!(matches!(
|
||||
rename_account_in_login_at_file(
|
||||
&wallet,
|
||||
&login_id,
|
||||
&account_id2,
|
||||
&renamed_account_id2,
|
||||
&password,
|
||||
),
|
||||
Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin)
|
||||
));
|
||||
|
||||
// Load and confirm nothing was changed
|
||||
let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap();
|
||||
let loaded_accounts = loaded_login.as_multiple_accounts().unwrap();
|
||||
let expected = vec![
|
||||
WalletAccount::new(
|
||||
DEFAULT_FIRST_ACCOUNT_NAME.into(),
|
||||
MnemonicAccount::new(account1, hd_path.clone()),
|
||||
),
|
||||
WalletAccount::new(
|
||||
account_id2.clone(),
|
||||
MnemonicAccount::new(account2, hd_path.clone()),
|
||||
),
|
||||
]
|
||||
.into();
|
||||
assert_eq!(loaded_accounts, &expected);
|
||||
}
|
||||
|
||||
// Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored
|
||||
// wallets created with older versions.
|
||||
#[test]
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Box, ListItem, ListItemAvatar, ListItemButton, ListItemText, Tooltip, Typography } from '@mui/material';
|
||||
import EditIcon from '@mui/icons-material/Create';
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { useClipboard } from 'use-clipboard-copy';
|
||||
import { AccountsContext } from 'src/context';
|
||||
import { AccountAvatar } from './AccountAvatar';
|
||||
@@ -13,13 +23,24 @@ export const AccountItem = ({
|
||||
address: string;
|
||||
onSelectAccount: () => void;
|
||||
}) => {
|
||||
const { selectedAccount, setDialogToDisplay, setAccountMnemonic } = useContext(AccountsContext);
|
||||
const { selectedAccount, setDialogToDisplay, setAccountMnemonic, handleAccountToEdit } = useContext(AccountsContext);
|
||||
const { copy, copied } = useClipboard({ copiedTimeout: 1000 });
|
||||
return (
|
||||
<ListItem
|
||||
disablePadding
|
||||
disableGutters
|
||||
sx={selectedAccount?.id === name ? { bgcolor: 'rgba(33, 208, 115, 0.1)' } : {}}
|
||||
secondaryAction={
|
||||
<IconButton
|
||||
sx={{ mr: 2, color: 'nym.text.dark' }}
|
||||
onClick={() => {
|
||||
handleAccountToEdit(name);
|
||||
setDialogToDisplay('Edit');
|
||||
}}
|
||||
>
|
||||
<EditIcon fontSize="small" />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemButton disableRipple onClick={onSelectAccount}>
|
||||
<ListItemAvatar sx={{ minWidth: 0, mr: 2 }}>
|
||||
@@ -59,17 +80,6 @@ export const AccountItem = ({
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
{/* edit and remove accounts todo */}
|
||||
{/* <ListItemIcon>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleAccountToEdit(name);
|
||||
}}
|
||||
>
|
||||
<Edit />
|
||||
</IconButton>
|
||||
</ListItemIcon> */}
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
);
|
||||
|
||||
@@ -33,7 +33,9 @@ export const AccountsModal = () => {
|
||||
if (accountToSwitchTo)
|
||||
return (
|
||||
<ConfirmPasswordModal
|
||||
modalTitle="Switch account"
|
||||
accountName={accountToSwitchTo}
|
||||
buttonTitle="Switch account"
|
||||
onClose={() => {
|
||||
handleClose();
|
||||
setDialogToDisplay('Accounts');
|
||||
|
||||
@@ -6,10 +6,14 @@ import { AccountsContext } from 'src/context';
|
||||
|
||||
export const ConfirmPasswordModal = ({
|
||||
accountName,
|
||||
modalTitle,
|
||||
onClose,
|
||||
onConfirm,
|
||||
buttonTitle,
|
||||
}: {
|
||||
accountName?: string;
|
||||
modalTitle: string;
|
||||
buttonTitle: string;
|
||||
onClose: () => void;
|
||||
onConfirm: (password: string) => Promise<void>;
|
||||
}) => {
|
||||
@@ -27,7 +31,7 @@ export const ConfirmPasswordModal = ({
|
||||
>
|
||||
<Paper>
|
||||
<DialogTitle>
|
||||
<Typography variant="h6">Switch account</Typography>
|
||||
<Typography variant="h6">{modalTitle}</Typography>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
Confirm password
|
||||
</Typography>
|
||||
@@ -36,7 +40,7 @@ export const ConfirmPasswordModal = ({
|
||||
onConfirm={onConfirm}
|
||||
error={error}
|
||||
isLoading={isLoading}
|
||||
buttonTitle="Switch account"
|
||||
buttonTitle={buttonTitle}
|
||||
onCancel={onClose}
|
||||
/>
|
||||
</Paper>
|
||||
|
||||
@@ -14,22 +14,59 @@ import {
|
||||
import { Close } from '@mui/icons-material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { AccountsContext } from 'src/context';
|
||||
import { StyledBackButton } from 'src/components/StyledBackButton';
|
||||
import { ConfirmPasswordModal } from './ConfirmPasswordModal';
|
||||
|
||||
export const EditAccountModal = () => {
|
||||
const [accountName, setAccountName] = useState('');
|
||||
const { accountToEdit, dialogToDisplay, setDialogToDisplay, handleEditAccount, handleAccountToEdit, setError } =
|
||||
useContext(AccountsContext);
|
||||
|
||||
const { accountToEdit, dialogToDisplay, setDialogToDisplay, handleEditAccount } = useContext(AccountsContext);
|
||||
const [accountName, setAccountName] = useState('');
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
setAccountName(accountToEdit ? accountToEdit?.id : '');
|
||||
if (accountToEdit) {
|
||||
setAccountName(accountToEdit.id);
|
||||
}
|
||||
}, [accountToEdit]);
|
||||
|
||||
const handleClose = () => {
|
||||
handleAccountToEdit(undefined);
|
||||
setDialogToDisplay('Accounts');
|
||||
};
|
||||
|
||||
const onConfirmPassword = async (password: string) => {
|
||||
if (accountToEdit) {
|
||||
try {
|
||||
await handleEditAccount({ account: accountToEdit, newAccountName: accountName, password });
|
||||
setShowConfirmPassword(false);
|
||||
} catch (e) {
|
||||
setError(`Error editing account: ${e}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (showConfirmPassword) {
|
||||
return (
|
||||
<ConfirmPasswordModal
|
||||
modalTitle="Rename account"
|
||||
accountName={accountToEdit?.id}
|
||||
buttonTitle="Confirm"
|
||||
onClose={() => {
|
||||
setShowConfirmPassword(false);
|
||||
setError(undefined);
|
||||
}}
|
||||
onConfirm={onConfirmPassword}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={dialogToDisplay === 'Edit'}
|
||||
onClose={() => setDialogToDisplay('Accounts')}
|
||||
onClose={handleClose}
|
||||
fullWidth
|
||||
PaperProps={{
|
||||
style: { border: `1px solid ${theme.palette.nym.nymWallet.modal.border}` },
|
||||
@@ -38,17 +75,15 @@ export const EditAccountModal = () => {
|
||||
<Paper>
|
||||
<DialogTitle>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">Edit account name</Typography>
|
||||
<IconButton onClick={() => setDialogToDisplay('Accounts')}>
|
||||
<Typography variant="h6">Rename account</Typography>
|
||||
<IconButton onClick={handleClose}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
New wallet address
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ p: 0 }}>
|
||||
<Box sx={{ px: 3, mt: 1 }}>
|
||||
<Typography sx={{ mb: 2 }}>Type the new name for your account</Typography>
|
||||
<TextField
|
||||
label="Account name"
|
||||
fullWidth
|
||||
@@ -59,21 +94,19 @@ export const EditAccountModal = () => {
|
||||
/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ p: 3 }}>
|
||||
<DialogActions sx={{ p: 3, gap: 2 }}>
|
||||
<StyledBackButton onBack={handleClose} />
|
||||
<Button
|
||||
fullWidth
|
||||
disableElevation
|
||||
variant="contained"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
if (accountToEdit) {
|
||||
handleEditAccount({ ...accountToEdit, id: accountName });
|
||||
setDialogToDisplay('Accounts');
|
||||
}
|
||||
setShowConfirmPassword(true);
|
||||
}}
|
||||
disabled={!accountName?.length}
|
||||
>
|
||||
Edit
|
||||
Rename
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Paper>
|
||||
|
||||
@@ -48,7 +48,7 @@ export const MnemonicModal = () => {
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">Display mnemonic</Typography>
|
||||
</Box>
|
||||
<Typography variant="body1" sx={{ color: (theme) => theme.palette.text.disabled }}>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
{`Display mnemonic for: ${accountMnemonic?.accountName}`}
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { createContext, Dispatch, SetStateAction, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { AccountEntry } from '@nymproject/types';
|
||||
import { addAccount as addAccountRequest, showMnemonicForAccount } from 'src/requests';
|
||||
import { addAccount as addAccountRequest, renameAccount, showMnemonicForAccount } from 'src/requests';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { AppContext } from './main';
|
||||
|
||||
@@ -17,8 +17,16 @@ type TAccounts = {
|
||||
handleAddAccount: (data: { accountName: string; mnemonic: string; password: string }) => void;
|
||||
setDialogToDisplay: (dialog?: TAccountsDialog) => void;
|
||||
handleSelectAccount: (data: { accountName: string; password: string }) => Promise<boolean>;
|
||||
handleAccountToEdit: (accountId: string) => void;
|
||||
handleEditAccount: (account: AccountEntry) => void;
|
||||
handleAccountToEdit: (accountId: string | undefined) => void;
|
||||
handleEditAccount: ({
|
||||
account,
|
||||
newAccountName,
|
||||
password,
|
||||
}: {
|
||||
account: AccountEntry;
|
||||
newAccountName: string;
|
||||
password: string;
|
||||
}) => Promise<void>;
|
||||
handleImportAccount: (account: AccountEntry) => void;
|
||||
handleGetAccountMnemonic: (data: { password: string; accountName: string }) => void;
|
||||
};
|
||||
@@ -67,12 +75,35 @@ export const AccountsProvider: FCWithChildren = ({ children }) => {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
const handleEditAccount = (account: AccountEntry) =>
|
||||
setAccounts((accs) => accs?.map((acc) => (acc.address === account.address ? account : acc)));
|
||||
const handleEditAccount = async ({
|
||||
account,
|
||||
newAccountName,
|
||||
password,
|
||||
}: {
|
||||
account: AccountEntry;
|
||||
newAccountName: string;
|
||||
password: string;
|
||||
}) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await renameAccount({ accountName: account.id, newAccountName, password });
|
||||
setAccounts((accs) =>
|
||||
accs?.map((acc) => (acc.address === account.address ? { ...acc, id: newAccountName } : acc)),
|
||||
);
|
||||
if (selectedAccount?.id === account.id) {
|
||||
setSelectedAccount({ ...selectedAccount, id: newAccountName });
|
||||
}
|
||||
setDialogToDisplay('Accounts');
|
||||
} catch (e) {
|
||||
throw new Error(`Error editing account: ${e}`);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleImportAccount = (account: AccountEntry) => setAccounts((accs) => [...(accs ? [...accs] : []), account]);
|
||||
|
||||
const handleAccountToEdit = (accountName: string) =>
|
||||
const handleAccountToEdit = (accountName: string | undefined) =>
|
||||
setAccountToEdit(accounts?.find((acc) => acc.id === accountName));
|
||||
|
||||
const handleSelectAccount = async ({ accountName, password }: { accountName: string; password: string }) => {
|
||||
|
||||
@@ -28,12 +28,31 @@ export const MockAccountsProvider: FCWithChildren = ({ children }) => {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
const handleEditAccount = (account: AccountEntry) =>
|
||||
setAccounts((accs) => accs?.map((acc) => (acc.address === account.address ? account : acc)));
|
||||
const handleEditAccount = async ({
|
||||
password,
|
||||
account,
|
||||
newAccountName,
|
||||
}: {
|
||||
password: string;
|
||||
account: AccountEntry;
|
||||
newAccountName: string;
|
||||
}) => {
|
||||
if (password) {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
setAccounts((accs) => accs.map((acc) => (acc.id === account.id ? { ...acc, id: newAccountName } : acc)));
|
||||
setDialogToDisplay('Accounts');
|
||||
} catch (e) {
|
||||
setError(`Error adding account: ${e}`);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleImportAccount = (account: AccountEntry) => setAccounts((accs) => [...(accs ? [...accs] : []), account]);
|
||||
|
||||
const handleAccountToEdit = (accountName: string) =>
|
||||
const handleAccountToEdit = (accountName: string | undefined) =>
|
||||
setAccountToEdit(accounts?.find((acc) => acc.id === accountName));
|
||||
|
||||
const handleSelectAccount = async ({ accountName }: { accountName: string; password: string }) => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user