Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 887ae8c789 | |||
| 12759a8460 | |||
| 00b737d362 | |||
| bd7b5db68f | |||
| 80184c3a51 | |||
| a13e9d8cc3 | |||
| 003a835ee6 | |||
| a60f37ae88 | |||
| 8d400ed4e0 | |||
| e4ba6c815e | |||
| 1ee18d9efe | |||
| 1fc7e07028 | |||
| 8758bea17c | |||
| 9d8a686760 | |||
| 435d9d3115 | |||
| 275cd9ff92 | |||
| 8435cf91e5 | |||
| 3460ce70a3 | |||
| 0ddaf3b50b | |||
| a103acaf70 | |||
| 08aa0af562 | |||
| 493390d92b | |||
| 8113948f48 | |||
| c3e3ef1737 | |||
| 45f4eeeff2 | |||
| f76d677f64 | |||
| 050a58affa | |||
| 065b3891f2 | |||
| 59da117e8f | |||
| 3d13274219 | |||
| 561566f3c4 | |||
| 6879c211ee | |||
| c7f2ef7074 | |||
| fb43e917cb | |||
| e79450a082 | |||
| 2e365026b7 | |||
| cd706aa67e | |||
| 304b192f52 | |||
| 8c979e3bac | |||
| 279b00d833 | |||
| 49fd0dc9e1 | |||
| 9a9d91cb4f | |||
| 5c08a89be3 | |||
| fd88776998 | |||
| 1339e20420 | |||
| 51b511b27e | |||
| 27ac34522c | |||
| 5e0d1bb14e | |||
| c16746a47b | |||
| a21052b72e | |||
| 92e9da7be5 | |||
| 143b336978 | |||
| d4293c9bae | |||
| e2d1806e49 | |||
| 469f85fc49 | |||
| 1202a2f5f4 | |||
| 6030bf6c95 | |||
| 09a771f58f | |||
| 676a909aee | |||
| f86e088663 |
@@ -5,7 +5,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
runs-on: arc-ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Dependencies (Linux)
|
||||
@@ -99,24 +99,3 @@ jobs:
|
||||
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
|
||||
working-directory: dist/docs
|
||||
continue-on-error: false
|
||||
|
||||
- name: Matrix - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Matrix - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: cd-docs
|
||||
NYM_PROJECT_NAME: "Docs CD"
|
||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CD_WWW_BASE }}"
|
||||
NYM_CI_WWW_LOCATION: "${{ env.GITHUB_REF_SLUG }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_DOCS }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name: ci-build-ts
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- "ts-packages/**"
|
||||
@@ -9,7 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
runs-on: arc-ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install rsync
|
||||
@@ -45,23 +46,3 @@ jobs:
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ts-${{ env.GITHUB_REF_SLUG }}-example
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
- name: Matrix - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Matrix - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: ts-packages
|
||||
NYM_PROJECT_NAME: "ts-packages"
|
||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||
NYM_CI_WWW_LOCATION: "ts-${{ env.GITHUB_REF_SLUG }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: ci-build-vpn-api-wasm
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'common/**'
|
||||
- 'nym-vpn-api/**'
|
||||
- '.github/workflows/ci-build-vpn-api-wasm.yml'
|
||||
|
||||
jobs:
|
||||
wasm:
|
||||
runs-on: arc-ubuntu-22.04
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '116'
|
||||
|
||||
- name: Install wasm-bindgen-cli
|
||||
run: cargo install wasm-bindgen-cli
|
||||
|
||||
- name: "Build"
|
||||
run: make
|
||||
working-directory: nym-vpn-api/vpn-api-lib-wasm
|
||||
@@ -0,0 +1,45 @@
|
||||
name: ci-build-vpn-api
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'common/**'
|
||||
- 'nym-vpn-api/**'
|
||||
- '.github/workspace/ci-build-vpn-api.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: arc-ubuntu-22.04
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
MANIFEST_PATH: "--manifest-path nym-vpn-api/Cargo.toml"
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: ${{ env.MANIFEST_PATH }} --all -- --check
|
||||
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: ${{ env.MANIFEST_PATH }} --workspace --all-targets
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: ${{ env.MANIFEST_PATH }} --workspace --all-targets -- -D warnings
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [arc-ubuntu-20.04, custom-runner-mac-m1]
|
||||
os: [ arc-ubuntu-20.04, custom-runner-mac-m1 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -62,14 +62,6 @@ jobs:
|
||||
with:
|
||||
command: build
|
||||
|
||||
# while disabled by default, this build ensures nothing is broken within
|
||||
# `axum` feature
|
||||
- name: Build with `axum` feature
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --features axum
|
||||
|
||||
- name: Build all examples
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -95,4 +87,4 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets --features axum -- -D warnings
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
runs-on: arc-ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Dependencies (Linux)
|
||||
@@ -70,24 +70,3 @@ jobs:
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/docs-${{ env.GITHUB_REF_SLUG }}
|
||||
EXCLUDE: "/node_modules/"
|
||||
|
||||
- name: Matrix - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Matrix - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: ci-docs
|
||||
NYM_PROJECT_NAME: "Docs CI"
|
||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||
NYM_CI_WWW_LOCATION: "docs-${{ env.GITHUB_REF_SLUG }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_DOCS }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name: ci-lint-typescript
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- "ts-packages/**"
|
||||
@@ -14,7 +15,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
runs-on: arc-ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
@@ -53,24 +54,3 @@ jobs:
|
||||
run: yarn lint
|
||||
- name: Typecheck with tsc
|
||||
run: yarn tsc
|
||||
|
||||
- name: Matrix - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Matrix - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: ts-packages
|
||||
NYM_PROJECT_NAME: "ts-packages"
|
||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||
NYM_CI_WWW_LOCATION: "ts-${{ env.GITHUB_REF_SLUG }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
runs-on: arc-ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Build and upload Node Status API container to harbor.nymte.ch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
WORKING_DIRECTORY: "nym-node-status-api"
|
||||
CONTAINER_NAME: "node-status-api"
|
||||
|
||||
jobs:
|
||||
build-container:
|
||||
runs-on: arc-ubuntu-22.04-dind
|
||||
steps:
|
||||
- name: Login to Harbor
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: harbor.nymte.ch
|
||||
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
|
||||
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure git identity
|
||||
run: |
|
||||
git config --global user.email "lawrence@nymtech.net"
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.3
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
- name: Check if tag exists
|
||||
run: |
|
||||
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
|
||||
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
|
||||
fi
|
||||
|
||||
- name: Remove existing tag if exists
|
||||
run: |
|
||||
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
|
||||
git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.value }}
|
||||
git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.value }}
|
||||
fi
|
||||
|
||||
- name: Create tag
|
||||
run: |
|
||||
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.value }} -m "Version ${{ steps.get_version.outputs.value }}"
|
||||
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.value }}
|
||||
|
||||
- name: BuildAndPushImageOnHarbor
|
||||
run: |
|
||||
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.value }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
|
||||
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
|
||||
Generated
+1150
-463
File diff suppressed because it is too large
Load Diff
+25
-12
@@ -118,6 +118,7 @@ members = [
|
||||
"nym-node",
|
||||
"nym-node/nym-node-http-api",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-node-status-api",
|
||||
"nym-outfox",
|
||||
"nym-validator-rewarder",
|
||||
"tools/echo-server",
|
||||
@@ -138,6 +139,8 @@ members = [
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/echo-server",
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -150,6 +153,7 @@ default-members = [
|
||||
"nym-data-observatory",
|
||||
"nym-node",
|
||||
"nym-validator-rewarder",
|
||||
"nym-node-status-api",
|
||||
"service-providers/authenticator",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
@@ -182,9 +186,9 @@ aes-gcm-siv = "0.11.1"
|
||||
aead = "0.5.2"
|
||||
anyhow = "1.0.89"
|
||||
argon2 = "0.5.0"
|
||||
async-trait = "0.1.82"
|
||||
async-trait = "0.1.83"
|
||||
axum = "0.7.5"
|
||||
axum-extra = "0.9.3"
|
||||
axum-extra = "0.9.4"
|
||||
base64 = "0.22.1"
|
||||
bincode = "1.3.3"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
@@ -197,7 +201,7 @@ blake3 = "1.5.4"
|
||||
bloomfilter = "1.0.14"
|
||||
bs58 = "0.5.1"
|
||||
bytecodec = "0.4.15"
|
||||
bytes = "1.7.1"
|
||||
bytes = "1.7.2"
|
||||
cargo_metadata = "0.18.1"
|
||||
celes = "2.4.0"
|
||||
cfg-if = "1.0.0"
|
||||
@@ -205,7 +209,7 @@ chacha20 = "0.9.0"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
chrono = "0.4.31"
|
||||
cipher = "0.4.3"
|
||||
clap = "4.5.17"
|
||||
clap = "4.5.18"
|
||||
clap_complete = "4.5"
|
||||
clap_complete_fig = "4.5"
|
||||
colored = "2.0"
|
||||
@@ -230,10 +234,12 @@ dotenvy = "0.15.6"
|
||||
ecdsa = "0.16"
|
||||
ed25519-dalek = "2.1"
|
||||
etherparse = "0.13.0"
|
||||
envy = "0.4"
|
||||
eyre = "0.6.9"
|
||||
fastrand = "2.1.1"
|
||||
flate2 = "1.0.33"
|
||||
flate2 = "1.0.34"
|
||||
futures = "0.3.28"
|
||||
futures-util = "0.3"
|
||||
generic-array = "0.14.7"
|
||||
getrandom = "0.2.10"
|
||||
getset = "0.1.3"
|
||||
@@ -263,6 +269,7 @@ ledger-transport-hid = "0.10.0"
|
||||
log = "0.4"
|
||||
maxminddb = "0.23.0"
|
||||
mime = "0.3.17"
|
||||
moka = { version = "0.12", features = ["future"] }
|
||||
nix = "0.27.1"
|
||||
notify = "5.1.0"
|
||||
okapi = "0.7.0"
|
||||
@@ -273,6 +280,7 @@ parking_lot = "0.12.3"
|
||||
pem = "0.8"
|
||||
petgraph = "0.6.5"
|
||||
pin-project = "1.0"
|
||||
pin-project-lite = "0.2.14"
|
||||
pretty_env_logger = "0.4.0"
|
||||
publicsuffix = "2.2.3"
|
||||
quote = "1"
|
||||
@@ -295,6 +303,7 @@ serde = "1.0.210"
|
||||
serde_bytes = "0.11.15"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0.128"
|
||||
serde_json_path = "0.6.7"
|
||||
serde_repr = "0.1"
|
||||
serde_with = "3.9.0"
|
||||
serde_yaml = "0.9.25"
|
||||
@@ -307,9 +316,9 @@ subtle-encoding = "0.5"
|
||||
syn = "1"
|
||||
sysinfo = "0.30.13"
|
||||
tap = "1.0.1"
|
||||
tar = "0.4.41"
|
||||
tar = "0.4.42"
|
||||
tempfile = "3.5.0"
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
time = "0.3.30"
|
||||
tokio = "1.39"
|
||||
tokio-stream = "0.1.16"
|
||||
@@ -324,7 +333,8 @@ tracing = "0.1.37"
|
||||
tracing-opentelemetry = "0.19.0"
|
||||
tracing-subscriber = "0.3.16"
|
||||
tracing-tree = "0.2.2"
|
||||
ts-rs = "7.0.0"
|
||||
tracing-log = "0.2"
|
||||
ts-rs = "10.0.0"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
url = "2.5"
|
||||
utoipa = "4.2"
|
||||
@@ -365,10 +375,13 @@ cw-controllers = { version = "=1.1.0" }
|
||||
# cosmrs-related
|
||||
bip32 = { version = "0.5.2", default-features = false }
|
||||
|
||||
cosmrs = { version = "0.21.1" }
|
||||
tendermint = "0.40.0"
|
||||
tendermint-rpc = "0.40.0"
|
||||
prost = { version = "0.13", default-features = false }
|
||||
# temporarily using a fork again (yay.) because we need staking and slashing support (which are already on main but not released)
|
||||
# plus response message parsing (which is, as of the time of writing this message, waiting to get merged)
|
||||
#cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" }
|
||||
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support
|
||||
tendermint = "0.37.0" # same version as used by cosmrs
|
||||
tendermint-rpc = "0.37.0" # same version as used by cosmrs
|
||||
prost = { version = "0.12", default-features = false }
|
||||
|
||||
# wasm-related dependencies
|
||||
gloo-utils = "0.2.0"
|
||||
|
||||
@@ -16,7 +16,7 @@ use nym_credential_storage::models::RetrievedTicketbook;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::ecash::bandwidth::CredentialSpendingData;
|
||||
use nym_credentials_interface::{
|
||||
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, VerificationKeyAuth,
|
||||
AnnotatedCoinIndexSignature, AnnotatedExpirationDateSignature, TicketType, VerificationKeyAuth,
|
||||
};
|
||||
use nym_ecash_time::Date;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
@@ -64,9 +64,10 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
||||
BandwidthController { storage, client }
|
||||
}
|
||||
|
||||
/// Tries to retrieve one of the stored, unused credentials that hasn't yet expired.
|
||||
/// Tries to retrieve one of the stored, unused credentials for the given type that hasn't yet expired.
|
||||
pub async fn get_next_usable_ticketbook(
|
||||
&self,
|
||||
ticketbook_type: TicketType,
|
||||
tickets: u32,
|
||||
) -> Result<RetrievedTicketbook, BandwidthControllerError>
|
||||
where
|
||||
@@ -74,7 +75,7 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
||||
{
|
||||
let Some(ticketbook) = self
|
||||
.storage
|
||||
.get_next_unspent_usable_ticketbook(tickets)
|
||||
.get_next_unspent_usable_ticketbook(ticketbook_type.to_string(), tickets)
|
||||
.await
|
||||
.map_err(BandwidthControllerError::credential_storage_error)?
|
||||
else {
|
||||
@@ -181,6 +182,7 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
||||
|
||||
pub async fn prepare_ecash_ticket(
|
||||
&self,
|
||||
ticketbook_type: TicketType,
|
||||
provider_pk: [u8; 32],
|
||||
tickets_to_spend: u32,
|
||||
) -> Result<PreparedCredential, BandwidthControllerError>
|
||||
@@ -188,7 +190,9 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
||||
C: DkgQueryClient + Sync + Send,
|
||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
let retrieved_ticketbook = self.get_next_usable_ticketbook(tickets_to_spend).await?;
|
||||
let retrieved_ticketbook = self
|
||||
.get_next_usable_ticketbook(ticketbook_type, tickets_to_spend)
|
||||
.await?;
|
||||
|
||||
let ticketbook_id = retrieved_ticketbook.ticketbook_id;
|
||||
let epoch_id = retrieved_ticketbook.ticketbook.epoch_id();
|
||||
|
||||
@@ -3,11 +3,11 @@ use log::{debug, error};
|
||||
use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond};
|
||||
use nym_network_defaults::var_names::EXPLORER_API;
|
||||
use nym_topology::{
|
||||
nym_topology_from_detailed,
|
||||
nym_topology_from_basic_info,
|
||||
provider_trait::{async_trait, TopologyProvider},
|
||||
NymTopology,
|
||||
};
|
||||
use nym_validator_client::client::MixId;
|
||||
use nym_validator_client::client::NodeId;
|
||||
use rand::{prelude::SliceRandom, thread_rng};
|
||||
use std::collections::HashMap;
|
||||
use tap::TapOptional;
|
||||
@@ -39,10 +39,10 @@ fn create_explorer_client() -> Option<ExplorerClient> {
|
||||
|
||||
fn group_mixnodes_by_country_code(
|
||||
mixnodes: Vec<PrettyDetailedMixNodeBond>,
|
||||
) -> HashMap<CountryGroup, Vec<MixId>> {
|
||||
) -> HashMap<CountryGroup, Vec<NodeId>> {
|
||||
mixnodes
|
||||
.into_iter()
|
||||
.fold(HashMap::<CountryGroup, Vec<MixId>>::new(), |mut acc, m| {
|
||||
.fold(HashMap::<CountryGroup, Vec<NodeId>>::new(), |mut acc, m| {
|
||||
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());
|
||||
@@ -53,7 +53,7 @@ fn group_mixnodes_by_country_code(
|
||||
})
|
||||
}
|
||||
|
||||
fn log_mixnode_distribution(mixnodes: &HashMap<CountryGroup, Vec<MixId>>) {
|
||||
fn log_mixnode_distribution(mixnodes: &HashMap<CountryGroup, Vec<NodeId>>) {
|
||||
let mixnode_distribution = mixnodes
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{}: {}", k, v.len()))
|
||||
@@ -110,7 +110,7 @@ impl GeoAwareTopologyProvider {
|
||||
}
|
||||
|
||||
async fn get_topology(&self) -> Option<NymTopology> {
|
||||
let mixnodes = match self.validator_client.get_cached_active_mixnodes().await {
|
||||
let mixnodes = match self.validator_client.get_basic_mixnodes(None).await {
|
||||
Err(err) => {
|
||||
error!("failed to get network mixnodes - {err}");
|
||||
return None;
|
||||
@@ -118,7 +118,7 @@ impl GeoAwareTopologyProvider {
|
||||
Ok(mixes) => mixes,
|
||||
};
|
||||
|
||||
let gateways = match self.validator_client.get_cached_gateways().await {
|
||||
let gateways = match self.validator_client.get_basic_gateways(None).await {
|
||||
Err(err) => {
|
||||
error!("failed to get network gateways - {err}");
|
||||
return None;
|
||||
@@ -182,10 +182,10 @@ impl GeoAwareTopologyProvider {
|
||||
|
||||
let mixnodes = mixnodes
|
||||
.into_iter()
|
||||
.filter(|m| filtered_mixnode_ids.contains(&m.mix_id()))
|
||||
.filter(|m| filtered_mixnode_ids.contains(&m.node_id))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let topology = nym_topology_from_detailed(mixnodes, gateways)
|
||||
let topology = nym_topology_from_basic_info(&mixnodes, &gateways)
|
||||
.filter_system_version(&self.client_version);
|
||||
|
||||
// TODO: return real error type
|
||||
|
||||
@@ -187,16 +187,6 @@ pub enum ClientCoreError {
|
||||
source: Ed25519RecoveryError,
|
||||
},
|
||||
|
||||
#[error("the account owner of gateway {gateway_id} ({raw_owner}) is malformed: {err}")]
|
||||
MalformedGatewayOwnerAccountAddress {
|
||||
gateway_id: String,
|
||||
|
||||
raw_owner: String,
|
||||
|
||||
// just use the string formatting as opposed to underlying type to avoid having to import cosmrs
|
||||
err: String,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"the listening address of gateway {gateway_id} ({raw_listener}) is malformed: {source}"
|
||||
)]
|
||||
|
||||
@@ -53,7 +53,7 @@ pub trait ConnectableGateway {
|
||||
fn is_wss(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ConnectableGateway for gateway::Node {
|
||||
impl ConnectableGateway for gateway::LegacyNode {
|
||||
fn identity(&self) -> &identity::PublicKey {
|
||||
self.identity()
|
||||
}
|
||||
@@ -82,7 +82,7 @@ pub async fn current_gateways<R: Rng>(
|
||||
rng: &mut R,
|
||||
nym_apis: &[Url],
|
||||
user_agent: Option<UserAgent>,
|
||||
) -> Result<Vec<gateway::Node>, ClientCoreError> {
|
||||
) -> Result<Vec<gateway::LegacyNode>, ClientCoreError> {
|
||||
let nym_api = nym_apis
|
||||
.choose(rng)
|
||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||
@@ -94,14 +94,14 @@ pub async fn current_gateways<R: Rng>(
|
||||
|
||||
log::debug!("Fetching list of gateways from: {nym_api}");
|
||||
|
||||
let gateways = client.get_cached_described_gateways().await?;
|
||||
let gateways = client.get_basic_gateways(None).await?;
|
||||
log::debug!("Found {} gateways", gateways.len());
|
||||
log::trace!("Gateways: {:#?}", gateways);
|
||||
|
||||
let valid_gateways = gateways
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
.collect::<Vec<gateway::Node>>();
|
||||
.collect::<Vec<gateway::LegacyNode>>();
|
||||
log::debug!("Ater checking validity: {}", valid_gateways.len());
|
||||
log::trace!("Valid gateways: {:#?}", valid_gateways);
|
||||
|
||||
@@ -118,7 +118,7 @@ pub async fn current_gateways<R: Rng>(
|
||||
pub async fn current_mixnodes<R: Rng>(
|
||||
rng: &mut R,
|
||||
nym_apis: &[Url],
|
||||
) -> Result<Vec<mix::Node>, ClientCoreError> {
|
||||
) -> Result<Vec<mix::LegacyNode>, ClientCoreError> {
|
||||
let nym_api = nym_apis
|
||||
.choose(rng)
|
||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||
@@ -126,11 +126,11 @@ pub async fn current_mixnodes<R: Rng>(
|
||||
|
||||
log::trace!("Fetching list of mixnodes from: {nym_api}");
|
||||
|
||||
let mixnodes = client.get_cached_mixnodes().await?;
|
||||
let mixnodes = client.get_basic_mixnodes(None).await?;
|
||||
let valid_mixnodes = mixnodes
|
||||
.into_iter()
|
||||
.filter_map(|mixnode| (&mixnode.bond_information).try_into().ok())
|
||||
.collect::<Vec<mix::Node>>();
|
||||
.iter()
|
||||
.filter_map(|mixnode| mixnode.try_into().ok())
|
||||
.collect::<Vec<mix::LegacyNode>>();
|
||||
|
||||
// we were always filtering by version so I'm not removing that 'feature'
|
||||
let filtered_mixnodes = valid_mixnodes.filter_by_version(env!("CARGO_PKG_VERSION"));
|
||||
@@ -273,9 +273,9 @@ fn filter_by_tls<G: ConnectableGateway>(
|
||||
|
||||
pub(super) fn uniformly_random_gateway<R: Rng>(
|
||||
rng: &mut R,
|
||||
gateways: &[gateway::Node],
|
||||
gateways: &[gateway::LegacyNode],
|
||||
must_use_tls: bool,
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
) -> Result<gateway::LegacyNode, ClientCoreError> {
|
||||
filter_by_tls(gateways, must_use_tls)?
|
||||
.choose(rng)
|
||||
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
|
||||
@@ -284,9 +284,9 @@ pub(super) fn uniformly_random_gateway<R: Rng>(
|
||||
|
||||
pub(super) fn get_specified_gateway(
|
||||
gateway_identity: IdentityKeyRef,
|
||||
gateways: &[gateway::Node],
|
||||
gateways: &[gateway::LegacyNode],
|
||||
must_use_tls: bool,
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
) -> Result<gateway::LegacyNode, ClientCoreError> {
|
||||
log::debug!("Requesting specified gateway: {}", gateway_identity);
|
||||
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||
|
||||
@@ -50,7 +50,7 @@ async fn setup_new_gateway<K, D>(
|
||||
key_store: &K,
|
||||
details_store: &D,
|
||||
selection_specification: GatewaySelectionSpecification,
|
||||
available_gateways: Vec<gateway::Node>,
|
||||
available_gateways: Vec<gateway::LegacyNode>,
|
||||
) -> Result<InitialisationResult, ClientCoreError>
|
||||
where
|
||||
K: KeyStore,
|
||||
|
||||
@@ -18,7 +18,6 @@ use nym_validator_client::client::IdentityKey;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
@@ -39,7 +38,7 @@ pub enum SelectedGateway {
|
||||
|
||||
impl SelectedGateway {
|
||||
pub fn from_topology_node(
|
||||
node: gateway::Node,
|
||||
node: gateway::LegacyNode,
|
||||
must_use_tls: bool,
|
||||
) -> Result<Self, ClientCoreError> {
|
||||
let gateway_listener = if must_use_tls {
|
||||
@@ -51,20 +50,6 @@ impl SelectedGateway {
|
||||
node.clients_address()
|
||||
};
|
||||
|
||||
let gateway_owner_address = node
|
||||
.owner
|
||||
.as_ref()
|
||||
.map(|raw_owner| {
|
||||
AccountId::from_str(raw_owner).map_err(|source| {
|
||||
ClientCoreError::MalformedGatewayOwnerAccountAddress {
|
||||
gateway_id: node.identity_key.to_base58_string(),
|
||||
raw_owner: raw_owner.clone(),
|
||||
err: source.to_string(),
|
||||
}
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let gateway_listener =
|
||||
Url::parse(&gateway_listener).map_err(|source| ClientCoreError::MalformedListener {
|
||||
gateway_id: node.identity_key.to_base58_string(),
|
||||
@@ -74,7 +59,7 @@ impl SelectedGateway {
|
||||
|
||||
Ok(SelectedGateway::Remote {
|
||||
gateway_id: node.identity_key,
|
||||
gateway_owner_address,
|
||||
gateway_owner_address: None,
|
||||
gateway_listener,
|
||||
})
|
||||
}
|
||||
@@ -215,7 +200,7 @@ pub enum GatewaySetup {
|
||||
specification: GatewaySelectionSpecification,
|
||||
|
||||
// TODO: seems to be a bit inefficient to pass them by value
|
||||
available_gateways: Vec<gateway::Node>,
|
||||
available_gateways: Vec<gateway::LegacyNode>,
|
||||
},
|
||||
|
||||
ReuseConnection {
|
||||
|
||||
@@ -24,6 +24,7 @@ zeroize.workspace = true
|
||||
nym-bandwidth-controller = { path = "../../bandwidth-controller" }
|
||||
nym-credentials = { path = "../../credentials" }
|
||||
nym-credential-storage = { path = "../../credential-storage" }
|
||||
nym-credentials-interface = { path = "../../credentials-interface" }
|
||||
nym-crypto = { path = "../../crypto" }
|
||||
nym-gateway-requests = { path = "../../gateway-requests" }
|
||||
nym-network-defaults = { path = "../../network-defaults" }
|
||||
|
||||
@@ -16,6 +16,7 @@ use nym_bandwidth_controller::{BandwidthController, BandwidthStatusMessage};
|
||||
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
use nym_credentials::CredentialSpendingData;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_gateway_requests::registration::handshake::client_handshake;
|
||||
use nym_gateway_requests::{
|
||||
@@ -748,7 +749,11 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
let prepared_credential = self
|
||||
.unchecked_bandwidth_controller()
|
||||
.prepare_ecash_ticket(self.gateway_identity.to_bytes(), TICKETS_TO_SPEND)
|
||||
.prepare_ecash_ticket(
|
||||
TicketType::V1MixnetEntry,
|
||||
self.gateway_identity.to_bytes(),
|
||||
TICKETS_TO_SPEND,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match self.claim_ecash_bandwidth(prepared_credential.data).await {
|
||||
|
||||
@@ -25,7 +25,6 @@ serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
nym-http-api-client = { path = "../../../common/http-api-client" }
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
tokio = { workspace = true, features = ["sync", "time"] }
|
||||
|
||||
@@ -17,11 +17,11 @@ use nym_api_requests::ecash::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse,
|
||||
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
|
||||
};
|
||||
use nym_api_requests::models::{DescribedGateway, MixNodeBondAnnotated};
|
||||
use nym_api_requests::models::{
|
||||
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
|
||||
use nym_api_requests::nym_nodes::SkimmedNode;
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_http_api_client::UserAgent;
|
||||
@@ -31,7 +31,7 @@ use url::Url;
|
||||
|
||||
pub use crate::nym_api::NymApiClientExt;
|
||||
pub use nym_mixnet_contract_common::{
|
||||
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, MixId,
|
||||
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, NodeId,
|
||||
};
|
||||
|
||||
// re-export the type to not break existing imports
|
||||
@@ -265,6 +265,13 @@ impl NymApiClient {
|
||||
NymApiClient { nym_api }
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self {
|
||||
let nym_api = nym_api::Client::new(api_url, Some(timeout));
|
||||
|
||||
NymApiClient { nym_api }
|
||||
}
|
||||
|
||||
pub fn new_with_user_agent(api_url: Url, user_agent: UserAgent) -> Self {
|
||||
let nym_api = nym_api::Client::builder::<_, ValidatorClientError>(api_url)
|
||||
.expect("invalid api url")
|
||||
@@ -327,7 +334,7 @@ impl NymApiClient {
|
||||
|
||||
pub async fn get_cached_described_gateways(
|
||||
&self,
|
||||
) -> Result<Vec<DescribedGateway>, ValidatorClientError> {
|
||||
) -> Result<Vec<LegacyDescribedGateway>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_gateways_described().await?)
|
||||
}
|
||||
|
||||
@@ -344,7 +351,7 @@ impl NymApiClient {
|
||||
|
||||
pub async fn get_mixnode_core_status_count(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
since: Option<i64>,
|
||||
) -> Result<MixnodeCoreStatusResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
@@ -355,21 +362,21 @@ impl NymApiClient {
|
||||
|
||||
pub async fn get_mixnode_status(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_mixnode_status(mix_id).await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_reward_estimation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<RewardEstimationResponse, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_mixnode_reward_estimation(mix_id).await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<StakeSaturationResponse, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_mixnode_stake_saturation(mix_id).await?)
|
||||
}
|
||||
|
||||
@@ -121,36 +121,36 @@ async fn test_nyxd_connection(
|
||||
{
|
||||
Ok(Err(NyxdError::TendermintErrorRpc(e))) => {
|
||||
// If we get a tendermint-rpc error, we classify the node as not contactable
|
||||
log::warn!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e);
|
||||
tracing::warn!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e);
|
||||
false
|
||||
}
|
||||
Ok(Err(NyxdError::AbciError { code, log, .. })) => {
|
||||
// We accept the mixnet contract not found as ok from a connection standpoint. This happens
|
||||
// for example on a pre-launch network.
|
||||
log::debug!(
|
||||
tracing::debug!(
|
||||
"Checking: nyxd url: {url}: {}, but with abci error: {code}: {log}",
|
||||
"success".green()
|
||||
);
|
||||
code == 18
|
||||
}
|
||||
Ok(Err(error @ NyxdError::NoContractAddressAvailable(_))) => {
|
||||
log::warn!("Checking: nyxd url: {url}: {}: {error}", "failed".red());
|
||||
tracing::warn!("Checking: nyxd url: {url}: {}: {error}", "failed".red());
|
||||
false
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
// For any other error, we're optimistic and just try anyway.
|
||||
log::warn!(
|
||||
tracing::warn!(
|
||||
"Checking: nyxd_url: {url}: {}, but with error: {e}",
|
||||
"success".green()
|
||||
);
|
||||
true
|
||||
}
|
||||
Ok(Ok(_)) => {
|
||||
log::debug!("Checking: nyxd_url: {url}: {}", "success".green());
|
||||
tracing::debug!("Checking: nyxd_url: {url}: {}", "success".green());
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Checking: nyxd_url: {url}: {}: {e}", "failed".red());
|
||||
tracing::warn!("Checking: nyxd_url: {url}: {}: {e}", "failed".red());
|
||||
false
|
||||
}
|
||||
};
|
||||
@@ -169,15 +169,15 @@ async fn test_nym_api_connection(
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
log::debug!("Checking: api_url: {url}: {}", "success".green());
|
||||
tracing::debug!("Checking: api_url: {url}: {}", "success".green());
|
||||
true
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
log::debug!("Checking: api_url: {url}: {}: {e}", "failed".red());
|
||||
tracing::debug!("Checking: api_url: {url}: {}: {e}", "failed".red());
|
||||
false
|
||||
}
|
||||
Err(e) => {
|
||||
log::debug!("Checking: api_url: {url}: {}: {e}", "failed".red());
|
||||
tracing::debug!("Checking: api_url: {url}: {}: {e}", "failed".red());
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,8 +10,9 @@ use nym_api_requests::ecash::models::{
|
||||
VerifyEcashTicketBody,
|
||||
};
|
||||
use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
use nym_api_requests::models::DescribedMixNode;
|
||||
use nym_api_requests::nym_nodes::{CachedNodesResponse, SkimmedNode};
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponse, LegacyDescribedMixNode, NodePerformanceResponse,
|
||||
};
|
||||
pub use nym_api_requests::{
|
||||
ecash::{
|
||||
models::{
|
||||
@@ -23,21 +24,23 @@ pub use nym_api_requests::{
|
||||
VerifyEcashCredentialBody,
|
||||
},
|
||||
models::{
|
||||
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
ComputeRewardEstParam, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
|
||||
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
|
||||
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse, UptimeResponse,
|
||||
LegacyDescribedGateway, MixNodeBondAnnotated, MixnodeCoreStatusResponse,
|
||||
MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
|
||||
},
|
||||
nym_nodes::{CachedNodesResponse, SkimmedNode},
|
||||
};
|
||||
pub use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_contracts_common::IdentityKey;
|
||||
pub use nym_http_api_client::Client;
|
||||
use nym_http_api_client::{ApiClient, NO_PARAMS};
|
||||
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
|
||||
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, NodeId};
|
||||
use time::format_description::BorrowedFormatItem;
|
||||
use time::Date;
|
||||
use tracing::instrument;
|
||||
|
||||
pub mod error;
|
||||
pub mod routes;
|
||||
@@ -49,11 +52,13 @@ pub fn rfc_3339_date() -> Vec<BorrowedFormatItem<'static>> {
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait NymApiClientExt: ApiClient {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.get_json(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -67,6 +72,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -80,6 +86,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnodes_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
@@ -95,12 +102,14 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
|
||||
self.get_json(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_gateways_described(&self) -> Result<Vec<DescribedGateway>, NymAPIError> {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_gateways_described(&self) -> Result<Vec<LegacyDescribedGateway>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::GATEWAYS, routes::DESCRIBED],
|
||||
NO_PARAMS,
|
||||
@@ -108,7 +117,8 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnodes_described(&self) -> Result<Vec<DescribedMixNode>, NymAPIError> {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnodes_described(&self) -> Result<Vec<LegacyDescribedMixNode>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::DESCRIBED],
|
||||
NO_PARAMS,
|
||||
@@ -116,6 +126,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_basic_mixnodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
@@ -139,6 +150,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_basic_gateways(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
@@ -162,6 +174,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
|
||||
@@ -170,6 +183,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_active_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -184,6 +198,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
|
||||
@@ -192,9 +207,10 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnode_report(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixnodeStatusReportResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -209,6 +225,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_gateway_report(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
@@ -226,9 +243,10 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnode_history(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixnodeUptimeHistoryResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -243,6 +261,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_gateway_history(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
@@ -260,6 +279,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_rewarded_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
@@ -276,6 +296,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
@@ -307,9 +328,10 @@ pub trait NymApiClientExt: ApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnode_core_status_count(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
since: Option<i64>,
|
||||
) -> Result<MixnodeCoreStatusResponse, NymAPIError> {
|
||||
if let Some(since) = since {
|
||||
@@ -339,9 +361,10 @@ pub trait NymApiClientExt: ApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnode_status(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixnodeStatusResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -356,9 +379,10 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnode_reward_estimation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<RewardEstimationResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -373,9 +397,10 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn compute_mixnode_reward_estimation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
request_body: &ComputeRewardEstParam,
|
||||
) -> Result<RewardEstimationResponse, NymAPIError> {
|
||||
self.post_json(
|
||||
@@ -392,9 +417,10 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<StakeSaturationResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -409,9 +435,10 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnode_inclusion_probability(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<InclusionProbabilityResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -426,7 +453,24 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnode_avg_uptime(&self, mix_id: MixId) -> Result<UptimeResponse, NymAPIError> {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_current_node_performance(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<NodePerformanceResponse, NymAPIError> {
|
||||
self.get_json_from(format!("/v1/nym-nodes/performance/{node_id}"))
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_node_annotation(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<AnnotationResponse, NymAPIError> {
|
||||
self.get_json_from(format!("/v1/nym-nodes/annotation/{node_id}"))
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnode_avg_uptime(&self, mix_id: NodeId) -> Result<UptimeResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
@@ -440,7 +484,8 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnodes_blacklisted(&self) -> Result<Vec<MixId>, NymAPIError> {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnodes_blacklisted(&self) -> Result<Vec<NodeId>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::BLACKLISTED],
|
||||
NO_PARAMS,
|
||||
@@ -448,6 +493,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_gateways_blacklisted(&self) -> Result<Vec<IdentityKey>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::GATEWAYS, routes::BLACKLISTED],
|
||||
@@ -456,6 +502,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, request_body))]
|
||||
async fn blind_sign(
|
||||
&self,
|
||||
request_body: &BlindSignRequestBody,
|
||||
@@ -472,6 +519,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, request_body))]
|
||||
async fn verify_ecash_ticket(
|
||||
&self,
|
||||
request_body: &VerifyEcashTicketBody,
|
||||
@@ -488,6 +536,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, request_body))]
|
||||
async fn batch_redeem_ecash_tickets(
|
||||
&self,
|
||||
request_body: &BatchRedeemTicketsBody,
|
||||
@@ -504,6 +553,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn double_spending_filter_v1(&self) -> Result<SpentCredentialsResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -516,6 +566,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn partial_expiration_date_signatures(
|
||||
&self,
|
||||
expiration_date: Option<Date>,
|
||||
@@ -539,6 +590,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn partial_coin_indices_signatures(
|
||||
&self,
|
||||
epoch_id: Option<EpochId>,
|
||||
@@ -559,6 +611,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn global_expiration_date_signatures(
|
||||
&self,
|
||||
expiration_date: Option<Date>,
|
||||
@@ -582,6 +635,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn global_coin_indices_signatures(
|
||||
&self,
|
||||
epoch_id: Option<EpochId>,
|
||||
@@ -602,6 +656,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn master_verification_key(
|
||||
&self,
|
||||
epoch_id: Option<EpochId>,
|
||||
@@ -621,6 +676,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn epoch_credentials(
|
||||
&self,
|
||||
dkg_epoch: EpochId,
|
||||
@@ -637,6 +693,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn issued_credential(
|
||||
&self,
|
||||
credential_id: i64,
|
||||
@@ -653,6 +710,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn issued_credentials(
|
||||
&self,
|
||||
credential_ids: Vec<i64>,
|
||||
|
||||
@@ -38,6 +38,7 @@ pub mod ecash {
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
pub const MIXNODE: &str = "mixnode";
|
||||
pub const GATEWAY: &str = "gateway";
|
||||
pub const NYM_NODES: &str = "nym-nodes";
|
||||
|
||||
pub const CORE_STATUS_COUNT: &str = "core-status-count";
|
||||
pub const SINCE_ARG: &str = "since";
|
||||
@@ -52,5 +53,6 @@ pub const STAKE_SATURATION: &str = "stake-saturation";
|
||||
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
|
||||
pub const SUBMIT_GATEWAY: &str = "submit-gateway-monitoring-results";
|
||||
pub const SUBMIT_NODE: &str = "submit-node-monitoring-results";
|
||||
pub const PERFORMANCE: &str = "performance";
|
||||
|
||||
pub const SERVICE_PROVIDERS: &str = "services";
|
||||
|
||||
@@ -8,9 +8,9 @@ use crate::nyxd::CosmWasmClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use cosmwasm_std::Addr;
|
||||
use log::trace;
|
||||
use nym_coconut_dkg_common::types::{ChunkIndex, NodeIndex, StateAdvanceResponse};
|
||||
use serde::Deserialize;
|
||||
use tracing::trace;
|
||||
|
||||
use nym_coconut_dkg_common::dealer::RegisteredDealerDetails;
|
||||
pub use nym_coconut_dkg_common::{
|
||||
|
||||
+350
-163
@@ -8,28 +8,33 @@ use crate::nyxd::CosmWasmClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use nym_contracts_common::signing::Nonce;
|
||||
use nym_mixnet_contract_common::gateway::{PreassignedGatewayIdsResponse, PreassignedId};
|
||||
use nym_mixnet_contract_common::nym_node::{
|
||||
EpochAssignmentResponse, NodeDetailsByIdentityResponse, NodeOwnershipResponse,
|
||||
NodeRewardingDetailsResponse, PagedNymNodeBondsResponse, PagedNymNodeDetailsResponse,
|
||||
PagedUnbondedNymNodesResponse, Role, RolesMetadataResponse, StakeSaturationResponse,
|
||||
UnbondedNodeResponse, UnbondedNymNode,
|
||||
};
|
||||
use nym_mixnet_contract_common::reward_params::WorkFactor;
|
||||
use nym_mixnet_contract_common::{
|
||||
delegation,
|
||||
delegation::{MixNodeDelegationResponse, OwnerProxySubKey},
|
||||
families::{Family, FamilyHead},
|
||||
delegation::{NodeDelegationResponse, OwnerProxySubKey},
|
||||
mixnode::{
|
||||
MixnodeRewardingDetailsResponse, PagedMixnodesDetailsResponse,
|
||||
PagedUnbondedMixnodesResponse, StakeSaturationResponse, UnbondedMixnodeResponse,
|
||||
MixStakeSaturationResponse, MixnodeRewardingDetailsResponse, PagedMixnodesDetailsResponse,
|
||||
PagedUnbondedMixnodesResponse, UnbondedMixnodeResponse,
|
||||
},
|
||||
reward_params::{Performance, RewardingParams},
|
||||
rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse},
|
||||
ContractBuildInformation, ContractState, ContractStateParams, CurrentIntervalResponse,
|
||||
Delegation, EpochEventId, EpochStatus, FamilyByHeadResponse, FamilyByLabelResponse,
|
||||
FamilyMembersByHeadResponse, FamilyMembersByLabelResponse, GatewayBond, GatewayBondResponse,
|
||||
GatewayOwnershipResponse, IdentityKey, IdentityKeyRef, IntervalEventId, LayerDistribution,
|
||||
MixId, MixNodeBond, MixNodeDetails, MixOwnershipResponse, MixnodeDetailsByIdentityResponse,
|
||||
MixnodeDetailsResponse, NumberOfPendingEventsResponse, PagedAllDelegationsResponse,
|
||||
PagedDelegatorDelegationsResponse, PagedFamiliesResponse, PagedGatewayResponse,
|
||||
PagedMembersResponse, PagedMixNodeDelegationsResponse, PagedMixnodeBondsResponse,
|
||||
PagedRewardedSetResponse, PendingEpochEvent, PendingEpochEventResponse,
|
||||
PendingEpochEventsResponse, PendingIntervalEvent, PendingIntervalEventResponse,
|
||||
PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg, RewardedSetNodeStatus,
|
||||
UnbondedMixnode,
|
||||
Delegation, EpochEventId, EpochStatus, GatewayBond, GatewayBondResponse,
|
||||
GatewayOwnershipResponse, IdentityKey, IdentityKeyRef, IntervalEventId, MixNodeBond,
|
||||
MixNodeDetails, MixOwnershipResponse, MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse,
|
||||
NodeId, NumberOfPendingEventsResponse, NymNodeBond, NymNodeDetails,
|
||||
PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedGatewayResponse,
|
||||
PagedMixnodeBondsResponse, PagedNodeDelegationsResponse, PendingEpochEvent,
|
||||
PendingEpochEventResponse, PendingEpochEventsResponse, PendingIntervalEvent,
|
||||
PendingIntervalEventResponse, PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg,
|
||||
RewardedSet, UnbondedMixnode,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -91,56 +96,11 @@ pub trait MixnetQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_rewarded_set_paged(
|
||||
&self,
|
||||
start_after: Option<MixId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedRewardedSetResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetRewardedSet { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_node_families_paged(
|
||||
&self,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedFamiliesResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_family_members_paged(
|
||||
&self,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedMembersResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetAllMembersPaged { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_family_members_by_head<S: Into<String> + Send>(
|
||||
&self,
|
||||
head: S,
|
||||
) -> Result<FamilyMembersByHeadResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyMembersByHead { head: head.into() })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_family_members_by_label<S: Into<String> + Send>(
|
||||
&self,
|
||||
label: S,
|
||||
) -> Result<FamilyMembersByLabelResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyMembersByLabel {
|
||||
label: label.into(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
// mixnode-related:
|
||||
|
||||
async fn get_mixnode_bonds_paged(
|
||||
&self,
|
||||
start_after: Option<MixId>,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedMixnodeBondsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetMixNodeBonds { limit, start_after })
|
||||
@@ -149,26 +109,26 @@ pub trait MixnetQueryClient {
|
||||
|
||||
async fn get_mixnodes_detailed_paged(
|
||||
&self,
|
||||
start_after: Option<MixId>,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedMixnodesDetailsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetMixNodesDetailed { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_paged(
|
||||
async fn get_unbonded_mixnodes_paged(
|
||||
&self,
|
||||
start_after: Option<MixId>,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedUnbondedMixnodesResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_by_owner_paged(
|
||||
async fn get_unbonded_mixnodes_by_owner_paged(
|
||||
&self,
|
||||
owner: &AccountId,
|
||||
start_after: Option<MixId>,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedUnbondedMixnodesResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByOwner {
|
||||
@@ -179,10 +139,10 @@ pub trait MixnetQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_by_identity_paged(
|
||||
async fn get_unbonded_mixnodes_by_identity_paged(
|
||||
&self,
|
||||
identity_key: IdentityKeyRef<'_>,
|
||||
start_after: Option<MixId>,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedUnbondedMixnodesResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey {
|
||||
@@ -205,7 +165,7 @@ pub trait MixnetQueryClient {
|
||||
|
||||
async fn get_mixnode_details(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixnodeDetailsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDetails { mix_id })
|
||||
.await
|
||||
@@ -223,7 +183,7 @@ pub trait MixnetQueryClient {
|
||||
|
||||
async fn get_mixnode_rewarding_details(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixnodeRewardingDetailsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id })
|
||||
.await
|
||||
@@ -231,24 +191,24 @@ pub trait MixnetQueryClient {
|
||||
|
||||
async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<StakeSaturationResponse, NyxdError> {
|
||||
mix_id: NodeId,
|
||||
) -> Result<MixStakeSaturationResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetStakeSaturation { mix_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_mixnode_information(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<UnbondedMixnodeResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_layer_distribution(&self) -> Result<LayerDistribution, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetLayerDistribution {})
|
||||
.await
|
||||
}
|
||||
// async fn get_layer_distribution(&self) -> Result<LayerDistribution, NyxdError> {
|
||||
// self.query_mixnet_contract(MixnetQueryMsg::GetRoleDistribution {})
|
||||
// .await
|
||||
// }
|
||||
|
||||
// gateway-related:
|
||||
|
||||
@@ -281,17 +241,142 @@ pub trait MixnetQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_preassigned_gateway_ids_paged(
|
||||
&self,
|
||||
start_after: Option<IdentityKey>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PreassignedGatewayIdsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPreassignedGatewayIds { start_after, limit })
|
||||
.await
|
||||
}
|
||||
|
||||
// nym-nodes related:
|
||||
async fn get_nymnode_bonds_paged(
|
||||
&self,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedNymNodeBondsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeBondsPaged { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_nymnodes_detailed_paged(
|
||||
&self,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedNymNodeDetailsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetNymNodesDetailedPaged { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_nymnodes_paged(
|
||||
&self,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedUnbondedNymNodesResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedNymNodesPaged { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_nymnodes_by_owner_paged(
|
||||
&self,
|
||||
owner: &AccountId,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedUnbondedNymNodesResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedNymNodesByOwnerPaged {
|
||||
owner: owner.to_string(),
|
||||
limit,
|
||||
start_after,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_nymnodes_by_identity_paged(
|
||||
&self,
|
||||
identity_key: IdentityKeyRef<'_>,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedUnbondedNymNodesResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedNymNodesByIdentityKeyPaged {
|
||||
identity_key: identity_key.to_string(),
|
||||
limit,
|
||||
start_after,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_owned_nymnode(
|
||||
&self,
|
||||
address: &AccountId,
|
||||
) -> Result<NodeOwnershipResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetOwnedNymNode {
|
||||
address: address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_nymnode_details(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<NodeOwnershipResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeDetails { node_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_nymnode_details_by_identity(
|
||||
&self,
|
||||
node_identity: IdentityKey,
|
||||
) -> Result<NodeDetailsByIdentityResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeDetailsByIdentityKey { node_identity })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_nymnode_rewarding_details(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<NodeRewardingDetailsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetNodeRewardingDetails { node_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_node_stake_saturation(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<StakeSaturationResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetNodeStakeSaturation { node_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_unbonded_nymnode_information(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<UnbondedNodeResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedNymNode { node_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_role_assignment(&self, role: Role) -> Result<EpochAssignmentResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetRoleAssignment { role })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_rewarded_set_metadata(&self) -> Result<RolesMetadataResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetRewardedSetMetadata {})
|
||||
.await
|
||||
}
|
||||
|
||||
// delegation-related:
|
||||
|
||||
/// Gets list of all delegations towards particular mixnode on particular page.
|
||||
async fn get_mixnode_delegations_paged(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
node_id: NodeId,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedMixNodeDelegationsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDelegations {
|
||||
mix_id,
|
||||
) -> Result<PagedNodeDelegationsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetNodeDelegations {
|
||||
node_id,
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
@@ -302,7 +387,7 @@ pub trait MixnetQueryClient {
|
||||
async fn get_delegator_delegations_paged(
|
||||
&self,
|
||||
delegator: &AccountId,
|
||||
start_after: Option<(MixId, OwnerProxySubKey)>,
|
||||
start_after: Option<(NodeId, OwnerProxySubKey)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedDelegatorDelegationsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetDelegatorDelegations {
|
||||
@@ -316,12 +401,12 @@ pub trait MixnetQueryClient {
|
||||
/// Checks value of delegation of given client towards particular mixnode.
|
||||
async fn get_delegation_details(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
node_id: NodeId,
|
||||
delegator: &AccountId,
|
||||
proxy: Option<String>,
|
||||
) -> Result<MixNodeDelegationResponse, NyxdError> {
|
||||
) -> Result<NodeDelegationResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetDelegationDetails {
|
||||
mix_id,
|
||||
node_id,
|
||||
delegator: delegator.to_string(),
|
||||
proxy,
|
||||
})
|
||||
@@ -351,21 +436,21 @@ pub trait MixnetQueryClient {
|
||||
|
||||
async fn get_pending_mixnode_operator_reward(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
node_id: NodeId,
|
||||
) -> Result<PendingRewardResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id })
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPendingNodeOperatorReward { node_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_pending_delegator_reward(
|
||||
&self,
|
||||
delegator: &AccountId,
|
||||
mix_id: MixId,
|
||||
node_id: NodeId,
|
||||
proxy: Option<String>,
|
||||
) -> Result<PendingRewardResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetPendingDelegatorReward {
|
||||
address: delegator.to_string(),
|
||||
mix_id,
|
||||
node_id,
|
||||
proxy,
|
||||
})
|
||||
.await
|
||||
@@ -374,12 +459,14 @@ pub trait MixnetQueryClient {
|
||||
// given the provided performance, estimate the reward at the end of the current epoch
|
||||
async fn get_estimated_current_epoch_operator_reward(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
node_id: NodeId,
|
||||
estimated_performance: Performance,
|
||||
estimated_work: Option<WorkFactor>,
|
||||
) -> Result<EstimatedCurrentEpochRewardResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward {
|
||||
mix_id,
|
||||
node_id,
|
||||
estimated_performance,
|
||||
estimated_work,
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -388,15 +475,15 @@ pub trait MixnetQueryClient {
|
||||
async fn get_estimated_current_epoch_delegator_reward(
|
||||
&self,
|
||||
delegator: &AccountId,
|
||||
mix_id: MixId,
|
||||
proxy: Option<String>,
|
||||
node_id: NodeId,
|
||||
estimated_performance: Performance,
|
||||
estimated_work: Option<WorkFactor>,
|
||||
) -> Result<EstimatedCurrentEpochRewardResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward {
|
||||
address: delegator.to_string(),
|
||||
mix_id,
|
||||
proxy,
|
||||
node_id,
|
||||
estimated_performance,
|
||||
estimated_work,
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -450,22 +537,6 @@ pub trait MixnetQueryClient {
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_node_family_by_label(
|
||||
&self,
|
||||
label: String,
|
||||
) -> Result<FamilyByLabelResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel { label })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_node_family_by_head(
|
||||
&self,
|
||||
head: String,
|
||||
) -> Result<FamilyByHeadResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByHead { head })
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// extension trait to the query client to deal with the paged queries
|
||||
@@ -473,18 +544,35 @@ pub trait MixnetQueryClient {
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait PagedMixnetQueryClient: MixnetQueryClient {
|
||||
async fn get_all_node_families(&self) -> Result<Vec<Family>, NyxdError> {
|
||||
collect_paged!(self, get_all_node_families_paged, families)
|
||||
async fn get_all_nymnode_bonds(&self) -> Result<Vec<NymNodeBond>, NyxdError> {
|
||||
collect_paged!(self, get_nymnode_bonds_paged, nodes)
|
||||
}
|
||||
|
||||
async fn get_all_family_members(&self) -> Result<Vec<(IdentityKey, FamilyHead)>, NyxdError> {
|
||||
collect_paged!(self, get_all_family_members_paged, members)
|
||||
async fn get_all_nymnodes_detailed(&self) -> Result<Vec<NymNodeDetails>, NyxdError> {
|
||||
collect_paged!(self, get_nymnodes_detailed_paged, nodes)
|
||||
}
|
||||
|
||||
async fn get_all_rewarded_set_mixnodes(
|
||||
async fn get_all_unbonded_nymnodes(&self) -> Result<Vec<UnbondedNymNode>, NyxdError> {
|
||||
collect_paged!(self, get_unbonded_nymnodes_paged, nodes)
|
||||
}
|
||||
|
||||
async fn get_all_unbonded_nymnodes_by_owner(
|
||||
&self,
|
||||
) -> Result<Vec<(MixId, RewardedSetNodeStatus)>, NyxdError> {
|
||||
collect_paged!(self, get_rewarded_set_paged, nodes)
|
||||
owner: &AccountId,
|
||||
) -> Result<Vec<UnbondedNymNode>, NyxdError> {
|
||||
collect_paged!(self, get_unbonded_nymnodes_by_owner_paged, nodes, owner)
|
||||
}
|
||||
|
||||
async fn get_all_unbonded_nymnodes_by_identity(
|
||||
&self,
|
||||
identity_key: IdentityKeyRef<'_>,
|
||||
) -> Result<Vec<UnbondedNymNode>, NyxdError> {
|
||||
collect_paged!(
|
||||
self,
|
||||
get_unbonded_nymnodes_by_identity_paged,
|
||||
nodes,
|
||||
identity_key
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_all_mixnode_bonds(&self) -> Result<Vec<MixNodeBond>, NyxdError> {
|
||||
@@ -495,31 +583,40 @@ pub trait PagedMixnetQueryClient: MixnetQueryClient {
|
||||
collect_paged!(self, get_mixnodes_detailed_paged, nodes)
|
||||
}
|
||||
|
||||
async fn get_all_unbonded_mixnodes(&self) -> Result<Vec<(MixId, UnbondedMixnode)>, NyxdError> {
|
||||
collect_paged!(self, get_unbonded_paged, nodes)
|
||||
async fn get_all_unbonded_mixnodes(&self) -> Result<Vec<(NodeId, UnbondedMixnode)>, NyxdError> {
|
||||
collect_paged!(self, get_unbonded_mixnodes_paged, nodes)
|
||||
}
|
||||
|
||||
async fn get_all_unbonded_mixnodes_by_owner(
|
||||
&self,
|
||||
owner: &AccountId,
|
||||
) -> Result<Vec<(MixId, UnbondedMixnode)>, NyxdError> {
|
||||
collect_paged!(self, get_unbonded_by_owner_paged, nodes, owner)
|
||||
) -> Result<Vec<(NodeId, UnbondedMixnode)>, NyxdError> {
|
||||
collect_paged!(self, get_unbonded_mixnodes_by_owner_paged, nodes, owner)
|
||||
}
|
||||
|
||||
async fn get_all_unbonded_mixnodes_by_identity(
|
||||
&self,
|
||||
identity_key: IdentityKeyRef<'_>,
|
||||
) -> Result<Vec<(MixId, UnbondedMixnode)>, NyxdError> {
|
||||
collect_paged!(self, get_unbonded_by_identity_paged, nodes, identity_key)
|
||||
) -> Result<Vec<(NodeId, UnbondedMixnode)>, NyxdError> {
|
||||
collect_paged!(
|
||||
self,
|
||||
get_unbonded_mixnodes_by_identity_paged,
|
||||
nodes,
|
||||
identity_key
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_all_gateways(&self) -> Result<Vec<GatewayBond>, NyxdError> {
|
||||
collect_paged!(self, get_gateways_paged, nodes)
|
||||
}
|
||||
|
||||
async fn get_all_preassigned_gateway_ids(&self) -> Result<Vec<PreassignedId>, NyxdError> {
|
||||
collect_paged!(self, get_preassigned_gateway_ids_paged, ids)
|
||||
}
|
||||
|
||||
async fn get_all_single_mixnode_delegations(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<Vec<Delegation>, NyxdError> {
|
||||
collect_paged!(self, get_mixnode_delegations_paged, delegations, mix_id)
|
||||
}
|
||||
@@ -554,6 +651,65 @@ pub trait PagedMixnetQueryClient: MixnetQueryClient {
|
||||
#[async_trait]
|
||||
impl<T> PagedMixnetQueryClient for T where T: MixnetQueryClient {}
|
||||
|
||||
// extension help to provide extra functionalities based on existing queries:
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait MixnetQueryClientExt: MixnetQueryClient {
|
||||
async fn get_rewarded_set(&self) -> Result<RewardedSet, NyxdError> {
|
||||
let error_response = |message| Err(NyxdError::extension_query_failure("mixnet", message));
|
||||
|
||||
let metadata = self.get_rewarded_set_metadata().await?;
|
||||
if !metadata.metadata.fully_assigned {
|
||||
return error_response("the rewarded set hasn't been fully assigned for this epoch");
|
||||
}
|
||||
let expected_epoch_id = metadata.metadata.epoch_id;
|
||||
|
||||
// if we have to query those things more frequently, we could do it concurrently,
|
||||
// but as it stands now, it happens so infrequently it might as well be sequential
|
||||
let entry = self.get_role_assignment(Role::EntryGateway).await?;
|
||||
if entry.epoch_id != expected_epoch_id {
|
||||
return error_response("the nodes assigned for 'entry' returned unexpected epoch_id");
|
||||
}
|
||||
|
||||
let exit = self.get_role_assignment(Role::ExitGateway).await?;
|
||||
if exit.epoch_id != expected_epoch_id {
|
||||
return error_response("the nodes assigned for 'exit' returned unexpected epoch_id");
|
||||
}
|
||||
|
||||
let layer1 = self.get_role_assignment(Role::Layer1).await?;
|
||||
if layer1.epoch_id != expected_epoch_id {
|
||||
return error_response("the nodes assigned for 'layer1' returned unexpected epoch_id");
|
||||
}
|
||||
|
||||
let layer2 = self.get_role_assignment(Role::Layer2).await?;
|
||||
if layer2.epoch_id != expected_epoch_id {
|
||||
return error_response("the nodes assigned for 'layer2' returned unexpected epoch_id");
|
||||
}
|
||||
|
||||
let layer3 = self.get_role_assignment(Role::Layer3).await?;
|
||||
if layer3.epoch_id != expected_epoch_id {
|
||||
return error_response("the nodes assigned for 'layer3' returned unexpected epoch_id");
|
||||
}
|
||||
|
||||
let standby = self.get_role_assignment(Role::Standby).await?;
|
||||
if standby.epoch_id != expected_epoch_id {
|
||||
return error_response("the nodes assigned for 'standby' returned unexpected epoch_id");
|
||||
}
|
||||
|
||||
Ok(RewardedSet {
|
||||
entry_gateways: entry.nodes,
|
||||
exit_gateways: exit.nodes,
|
||||
layer1: layer1.nodes,
|
||||
layer2: layer2.nodes,
|
||||
layer3: layer3.nodes,
|
||||
standby: standby.nodes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T> MixnetQueryClientExt for T where T: MixnetQueryClient {}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<C> MixnetQueryClient for C
|
||||
@@ -585,24 +741,6 @@ mod tests {
|
||||
) -> u32 {
|
||||
match msg {
|
||||
MixnetQueryMsg::Admin {} => client.admin().ignore(),
|
||||
MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after } => client
|
||||
.get_all_family_members_paged(start_after, limit)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetAllMembersPaged { limit, start_after } => client
|
||||
.get_all_family_members_paged(start_after, limit)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetFamilyByHead { head } => {
|
||||
client.get_node_family_by_head(head).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetFamilyByLabel { label } => {
|
||||
client.get_node_family_by_label(label).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetFamilyMembersByHead { head } => {
|
||||
client.get_family_members_by_head(head).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetFamilyMembersByLabel { label } => {
|
||||
client.get_family_members_by_label(label).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetContractVersion {} => client.get_mixnet_contract_version().ignore(),
|
||||
MixnetQueryMsg::GetCW2ContractVersion {} => {
|
||||
client.get_mixnet_contract_cw2_version().ignore()
|
||||
@@ -617,31 +755,28 @@ mod tests {
|
||||
MixnetQueryMsg::GetCurrentIntervalDetails {} => {
|
||||
client.get_current_interval_details().ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetRewardedSet { limit, start_after } => {
|
||||
client.get_rewarded_set_paged(start_after, limit).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetMixNodeBonds { limit, start_after } => {
|
||||
client.get_mixnode_bonds_paged(start_after, limit).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetMixNodesDetailed { limit, start_after } => client
|
||||
.get_mixnodes_detailed_paged(start_after, limit)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after } => {
|
||||
client.get_unbonded_paged(start_after, limit).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after } => client
|
||||
.get_unbonded_mixnodes_paged(start_after, limit)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetUnbondedMixNodesByOwner {
|
||||
owner,
|
||||
limit,
|
||||
start_after,
|
||||
} => client
|
||||
.get_unbonded_by_owner_paged(&owner.parse().unwrap(), start_after, limit)
|
||||
.get_unbonded_mixnodes_by_owner_paged(&owner.parse().unwrap(), start_after, limit)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey {
|
||||
identity_key,
|
||||
limit,
|
||||
start_after,
|
||||
} => client
|
||||
.get_unbonded_by_identity_paged(&identity_key, start_after, limit)
|
||||
.get_unbonded_mixnodes_by_identity_paged(&identity_key, start_after, limit)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetOwnedMixnode { address } => {
|
||||
client.get_owned_mixnode(&address.parse().unwrap()).ignore()
|
||||
@@ -661,7 +796,6 @@ mod tests {
|
||||
MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity { mix_identity } => client
|
||||
.get_mixnode_details_by_identity(mix_identity)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetLayerDistribution {} => client.get_layer_distribution().ignore(),
|
||||
MixnetQueryMsg::GetGateways { start_after, limit } => {
|
||||
client.get_gateways_paged(start_after, limit).ignore()
|
||||
}
|
||||
@@ -671,8 +805,8 @@ mod tests {
|
||||
MixnetQueryMsg::GetOwnedGateway { address } => {
|
||||
client.get_owned_gateway(&address.parse().unwrap()).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetMixnodeDelegations {
|
||||
mix_id,
|
||||
MixnetQueryMsg::GetNodeDelegations {
|
||||
node_id: mix_id,
|
||||
start_after,
|
||||
limit,
|
||||
} => client
|
||||
@@ -686,7 +820,7 @@ mod tests {
|
||||
.get_delegator_delegations_paged(&delegator.parse().unwrap(), start_after, limit)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetDelegationDetails {
|
||||
mix_id,
|
||||
node_id: mix_id,
|
||||
delegator,
|
||||
proxy,
|
||||
} => client
|
||||
@@ -698,33 +832,38 @@ mod tests {
|
||||
MixnetQueryMsg::GetPendingOperatorReward { address } => client
|
||||
.get_pending_operator_reward(&address.parse().unwrap())
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id } => {
|
||||
MixnetQueryMsg::GetPendingNodeOperatorReward { node_id: mix_id } => {
|
||||
client.get_pending_mixnode_operator_reward(mix_id).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetPendingDelegatorReward {
|
||||
address,
|
||||
mix_id,
|
||||
node_id: mix_id,
|
||||
proxy,
|
||||
} => client
|
||||
.get_pending_delegator_reward(&address.parse().unwrap(), mix_id, proxy)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward {
|
||||
mix_id,
|
||||
node_id,
|
||||
estimated_performance,
|
||||
estimated_work,
|
||||
} => client
|
||||
.get_estimated_current_epoch_operator_reward(mix_id, estimated_performance)
|
||||
.get_estimated_current_epoch_operator_reward(
|
||||
node_id,
|
||||
estimated_performance,
|
||||
estimated_work,
|
||||
)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward {
|
||||
address,
|
||||
mix_id,
|
||||
proxy,
|
||||
node_id,
|
||||
estimated_performance,
|
||||
estimated_work,
|
||||
} => client
|
||||
.get_estimated_current_epoch_delegator_reward(
|
||||
&address.parse().unwrap(),
|
||||
mix_id,
|
||||
proxy,
|
||||
node_id,
|
||||
estimated_performance,
|
||||
estimated_work,
|
||||
)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetPendingEpochEvents { limit, start_after } => client
|
||||
@@ -745,6 +884,54 @@ mod tests {
|
||||
MixnetQueryMsg::GetSigningNonce { address } => {
|
||||
client.get_signing_nonce(&address.parse().unwrap()).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetPreassignedGatewayIds { start_after, limit } => client
|
||||
.get_preassigned_gateway_ids_paged(start_after, limit)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetNymNodeBondsPaged { limit, start_after } => {
|
||||
client.get_nymnode_bonds_paged(limit, start_after).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetNymNodesDetailedPaged { limit, start_after } => client
|
||||
.get_nymnodes_detailed_paged(limit, start_after)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetUnbondedNymNode { node_id } => {
|
||||
client.get_unbonded_nymnode_information(node_id).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetUnbondedNymNodesPaged { limit, start_after } => client
|
||||
.get_unbonded_nymnodes_paged(limit, start_after)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetUnbondedNymNodesByOwnerPaged {
|
||||
owner,
|
||||
limit,
|
||||
start_after,
|
||||
} => client
|
||||
.get_unbonded_nymnodes_by_owner_paged(&owner.parse().unwrap(), limit, start_after)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetUnbondedNymNodesByIdentityKeyPaged {
|
||||
identity_key,
|
||||
limit,
|
||||
start_after,
|
||||
} => client
|
||||
.get_unbonded_nymnodes_by_identity_paged(&identity_key, limit, start_after)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetOwnedNymNode { address } => {
|
||||
client.get_owned_nymnode(&address.parse().unwrap()).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetNymNodeDetails { node_id } => {
|
||||
client.get_nymnode_details(node_id).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetNymNodeDetailsByIdentityKey { node_identity } => client
|
||||
.get_nymnode_details_by_identity(node_identity)
|
||||
.ignore(),
|
||||
MixnetQueryMsg::GetNodeRewardingDetails { node_id } => {
|
||||
client.get_nymnode_rewarding_details(node_id).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetNodeStakeSaturation { node_id } => {
|
||||
client.get_node_stake_saturation(node_id).ignore()
|
||||
}
|
||||
MixnetQueryMsg::GetRoleAssignment { role } => client.get_role_assignment(role).ignore(),
|
||||
MixnetQueryMsg::GetRewardedSetMetadata {} => {
|
||||
client.get_rewarded_set_metadata().ignore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+122
-240
@@ -10,13 +10,15 @@ use crate::signing::signer::OfflineSigner;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_mixnet_contract_common::gateway::GatewayConfigUpdate;
|
||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
|
||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, NodeCostParams};
|
||||
use nym_mixnet_contract_common::nym_node::NodeConfigUpdate;
|
||||
use nym_mixnet_contract_common::reward_params::{
|
||||
ActiveSetUpdate, IntervalRewardingParamsUpdate, NodeRewardingParameters,
|
||||
};
|
||||
use nym_mixnet_contract_common::{
|
||||
ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, Layer, LayerAssignment, MixId,
|
||||
MixNode,
|
||||
ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, MixNode, NodeId, NymNode,
|
||||
RoleAssignment,
|
||||
};
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -70,14 +72,14 @@ pub trait MixnetSigningClient {
|
||||
|
||||
async fn update_active_set_size(
|
||||
&self,
|
||||
active_set_size: u32,
|
||||
update: ActiveSetUpdate,
|
||||
force_immediately: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateActiveSetSize {
|
||||
active_set_size,
|
||||
MixnetExecuteMsg::UpdateActiveSetDistribution {
|
||||
update,
|
||||
force_immediately,
|
||||
},
|
||||
vec![],
|
||||
@@ -126,37 +128,6 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn advance_current_epoch(
|
||||
&self,
|
||||
new_rewarded_set: Vec<LayerAssignment>,
|
||||
expected_active_set_size: u32,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::AdvanceCurrentEpoch {
|
||||
new_rewarded_set,
|
||||
expected_active_set_size,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn assign_node_layer(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
layer: Layer,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::AssignNodeLayer { mix_id, layer },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn reconcile_epoch_events(
|
||||
&self,
|
||||
limit: Option<u32>,
|
||||
@@ -170,126 +141,21 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
// family related
|
||||
async fn create_family(
|
||||
async fn assign_roles(
|
||||
&self,
|
||||
label: String,
|
||||
assignment: RoleAssignment,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::CreateFamily { label }, vec![])
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::AssignRoles { assignment }, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_family_on_behalf(
|
||||
&self,
|
||||
owner_address: String,
|
||||
label: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::CreateFamilyOnBehalf {
|
||||
owner_address,
|
||||
label,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn join_family(
|
||||
&self,
|
||||
join_permit: MessageSignature,
|
||||
family_head: FamilyHead,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::JoinFamily {
|
||||
join_permit,
|
||||
family_head,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn join_family_on_behalf(
|
||||
&self,
|
||||
member_address: String,
|
||||
join_permit: MessageSignature,
|
||||
family_head: FamilyHead,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::JoinFamilyOnBehalf {
|
||||
member_address,
|
||||
join_permit,
|
||||
family_head,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn leave_family(
|
||||
&self,
|
||||
family_head: FamilyHead,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::LeaveFamily { family_head }, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn leave_family_on_behalf(
|
||||
&self,
|
||||
member_address: String,
|
||||
family_head: FamilyHead,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::LeaveFamilyOnBehalf {
|
||||
member_address,
|
||||
family_head,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn kick_family_member(
|
||||
&self,
|
||||
member: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::KickFamilyMember { member }, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn kick_family_member_on_behalf(
|
||||
&self,
|
||||
head_address: String,
|
||||
member: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::KickFamilyMemberOnBehalf {
|
||||
head_address,
|
||||
member,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
// mixnode-related:
|
||||
|
||||
async fn bond_mixnode(
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
cost_params: NodeCostParams,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
@@ -310,7 +176,7 @@ pub trait MixnetSigningClient {
|
||||
&self,
|
||||
owner: AccountId,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
cost_params: NodeCostParams,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
@@ -409,14 +275,14 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_mixnode_cost_params(
|
||||
async fn update_cost_params(
|
||||
&self,
|
||||
new_costs: MixNodeCostParams,
|
||||
new_costs: NodeCostParams,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UpdateMixnodeCostParams { new_costs },
|
||||
MixnetExecuteMsg::UpdateCostParams { new_costs },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
@@ -425,7 +291,7 @@ pub trait MixnetSigningClient {
|
||||
async fn update_mixnode_cost_params_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
new_costs: MixNodeCostParams,
|
||||
new_costs: NodeCostParams,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
@@ -559,26 +425,75 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
// delegation-related:
|
||||
// nym-node related:
|
||||
async fn migrate_legacy_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::MigrateMixnode {}, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delegate_to_mixnode(
|
||||
async fn migrate_legacy_gateway(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
amount: Coin,
|
||||
cost_params: Option<NodeCostParams>,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::DelegateToMixnode { mix_id },
|
||||
vec![amount],
|
||||
MixnetExecuteMsg::MigrateGateway { cost_params },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn bond_nymnode(
|
||||
&self,
|
||||
node: NymNode,
|
||||
cost_params: NodeCostParams,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::BondNymNode {
|
||||
node,
|
||||
cost_params,
|
||||
owner_signature,
|
||||
},
|
||||
vec![pledge],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn unbond_nymnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::UnbondNymNode {}, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_nymnode_config(
|
||||
&self,
|
||||
update: NodeConfigUpdate,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::UpdateNodeConfig { update }, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
// delegation-related:
|
||||
|
||||
async fn delegate(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::Delegate { node_id }, vec![amount])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delegate_to_mixnode_on_behalf(
|
||||
&self,
|
||||
delegate: AccountId,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
@@ -593,23 +508,19 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn undelegate_from_mixnode(
|
||||
async fn undelegate(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
node_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::UndelegateFromMixnode { mix_id },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
self.execute_mixnet_contract(fee, MixnetExecuteMsg::Undelegate { node_id }, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn undelegate_to_mixnode_on_behalf(
|
||||
&self,
|
||||
delegate: AccountId,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
@@ -625,18 +536,15 @@ pub trait MixnetSigningClient {
|
||||
|
||||
// reward-related
|
||||
|
||||
async fn reward_mixnode(
|
||||
async fn reward_node(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
performance: Performance,
|
||||
node_id: NodeId,
|
||||
params: NodeRewardingParameters,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::RewardMixnode {
|
||||
mix_id,
|
||||
performance,
|
||||
},
|
||||
MixnetExecuteMsg::RewardNode { node_id, params },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
@@ -664,12 +572,12 @@ pub trait MixnetSigningClient {
|
||||
|
||||
async fn withdraw_delegator_reward(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
node_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::WithdrawDelegatorReward { mix_id },
|
||||
MixnetExecuteMsg::WithdrawDelegatorReward { node_id },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
@@ -678,7 +586,7 @@ pub trait MixnetSigningClient {
|
||||
async fn withdraw_delegator_reward_on_behalf(
|
||||
&self,
|
||||
owner: AccountId,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
@@ -699,7 +607,7 @@ pub trait MixnetSigningClient {
|
||||
|
||||
async fn migrate_vested_delegation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
@@ -761,6 +669,7 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue};
|
||||
use nym_mixnet_contract_common::ExecuteMsg;
|
||||
|
||||
// it's enough that this compiles and clippy is happy about it
|
||||
#[allow(dead_code)]
|
||||
@@ -770,56 +679,17 @@ mod tests {
|
||||
) {
|
||||
match msg {
|
||||
MixnetExecuteMsg::UpdateAdmin { admin } => client.update_admin(admin, None).ignore(),
|
||||
MixnetExecuteMsg::AssignNodeLayer { mix_id, layer } => {
|
||||
client.assign_node_layer(mix_id, layer, None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::CreateFamily { label } => client.create_family(label, None).ignore(),
|
||||
MixnetExecuteMsg::JoinFamily {
|
||||
join_permit,
|
||||
family_head,
|
||||
} => client.join_family(join_permit, family_head, None).ignore(),
|
||||
MixnetExecuteMsg::LeaveFamily { family_head } => {
|
||||
client.leave_family(family_head, None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::KickFamilyMember { member } => {
|
||||
client.kick_family_member(member, None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::CreateFamilyOnBehalf {
|
||||
owner_address,
|
||||
label,
|
||||
} => client
|
||||
.create_family_on_behalf(owner_address, label, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::JoinFamilyOnBehalf {
|
||||
member_address,
|
||||
join_permit,
|
||||
family_head,
|
||||
} => client
|
||||
.join_family_on_behalf(member_address, join_permit, family_head, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::LeaveFamilyOnBehalf {
|
||||
member_address,
|
||||
family_head,
|
||||
} => client
|
||||
.leave_family_on_behalf(member_address, family_head, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::KickFamilyMemberOnBehalf {
|
||||
head_address,
|
||||
member,
|
||||
} => client
|
||||
.kick_family_member_on_behalf(head_address, member, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::UpdateRewardingValidatorAddress { address } => client
|
||||
.update_rewarding_validator_address(address.parse().unwrap(), None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::UpdateContractStateParams { updated_parameters } => client
|
||||
.update_contract_state_params(updated_parameters, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::UpdateActiveSetSize {
|
||||
active_set_size,
|
||||
MixnetExecuteMsg::UpdateActiveSetDistribution {
|
||||
update,
|
||||
force_immediately,
|
||||
} => client
|
||||
.update_active_set_size(active_set_size, force_immediately, None)
|
||||
.update_active_set_size(update, force_immediately, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::UpdateRewardingParams {
|
||||
updated_params,
|
||||
@@ -842,12 +712,6 @@ mod tests {
|
||||
MixnetExecuteMsg::BeginEpochTransition {} => {
|
||||
client.begin_epoch_transition(None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::AdvanceCurrentEpoch {
|
||||
new_rewarded_set,
|
||||
expected_active_set_size,
|
||||
} => client
|
||||
.advance_current_epoch(new_rewarded_set, expected_active_set_size, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::ReconcileEpochEvents { limit } => {
|
||||
client.reconcile_epoch_events(limit, None).ignore()
|
||||
}
|
||||
@@ -887,8 +751,8 @@ mod tests {
|
||||
MixnetExecuteMsg::UnbondMixnodeOnBehalf { owner } => client
|
||||
.unbond_mixnode_on_behalf(owner.parse().unwrap(), None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::UpdateMixnodeCostParams { new_costs } => {
|
||||
client.update_mixnode_cost_params(new_costs, None).ignore()
|
||||
MixnetExecuteMsg::UpdateCostParams { new_costs } => {
|
||||
client.update_cost_params(new_costs, None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::UpdateMixnodeCostParamsOnBehalf { new_costs, owner } => client
|
||||
.update_mixnode_cost_params_on_behalf(owner.parse().unwrap(), new_costs, None)
|
||||
@@ -928,29 +792,28 @@ mod tests {
|
||||
MixnetExecuteMsg::UpdateGatewayConfigOnBehalf { new_config, owner } => client
|
||||
.update_gateway_config_on_behalf(owner.parse().unwrap(), new_config, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::DelegateToMixnode { mix_id } => client
|
||||
.delegate_to_mixnode(mix_id, mock_coin(), None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::Delegate { node_id: mix_id } => {
|
||||
client.delegate(mix_id, mock_coin(), None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, delegate } => client
|
||||
.delegate_to_mixnode_on_behalf(delegate.parse().unwrap(), mix_id, mock_coin(), None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::UndelegateFromMixnode { mix_id } => {
|
||||
client.undelegate_from_mixnode(mix_id, None).ignore()
|
||||
MixnetExecuteMsg::Undelegate { node_id: mix_id } => {
|
||||
client.undelegate(mix_id, None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, delegate } => client
|
||||
.undelegate_to_mixnode_on_behalf(delegate.parse().unwrap(), mix_id, None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::RewardMixnode {
|
||||
mix_id,
|
||||
performance,
|
||||
} => client.reward_mixnode(mix_id, performance, None).ignore(),
|
||||
MixnetExecuteMsg::RewardNode { node_id, params } => {
|
||||
client.reward_node(node_id, params, None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::WithdrawOperatorReward {} => {
|
||||
client.withdraw_operator_reward(None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::WithdrawOperatorRewardOnBehalf { owner } => client
|
||||
.withdraw_operator_reward_on_behalf(owner.parse().unwrap(), None)
|
||||
.ignore(),
|
||||
MixnetExecuteMsg::WithdrawDelegatorReward { mix_id } => {
|
||||
MixnetExecuteMsg::WithdrawDelegatorReward { node_id: mix_id } => {
|
||||
client.withdraw_delegator_reward(mix_id, None).ignore()
|
||||
}
|
||||
MixnetExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, owner } => client
|
||||
@@ -963,6 +826,25 @@ mod tests {
|
||||
client.migrate_vested_delegation(mix_id, None).ignore()
|
||||
}
|
||||
|
||||
ExecuteMsg::AssignRoles { assignment } => {
|
||||
client.assign_roles(assignment, None).ignore()
|
||||
}
|
||||
ExecuteMsg::MigrateMixnode {} => client.migrate_legacy_mixnode(None).ignore(),
|
||||
ExecuteMsg::MigrateGateway { cost_params } => {
|
||||
client.migrate_legacy_gateway(cost_params, None).ignore()
|
||||
}
|
||||
ExecuteMsg::BondNymNode {
|
||||
node,
|
||||
cost_params,
|
||||
owner_signature,
|
||||
} => client
|
||||
.bond_nymnode(node, cost_params, owner_signature, mock_coin(), None)
|
||||
.ignore(),
|
||||
ExecuteMsg::UnbondNymNode {} => client.unbond_nymnode(None).ignore(),
|
||||
ExecuteMsg::UpdateNodeConfig { update } => {
|
||||
client.update_nymnode_config(update, None).ignore()
|
||||
}
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => {
|
||||
client.testing_resolve_all_pending_events(None).ignore()
|
||||
|
||||
+5
-5
@@ -9,7 +9,7 @@ use crate::nyxd::CosmWasmClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
|
||||
use nym_contracts_common::ContractBuildInformation;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_vesting_contract_common::{
|
||||
messages::QueryMsg as VestingQueryMsg, Account, AccountVestingCoins, AccountsResponse,
|
||||
AllDelegationsResponse, BaseVestingAccountInfo, DelegationTimesResponse,
|
||||
@@ -238,7 +238,7 @@ pub trait VestingQueryClient {
|
||||
async fn get_vesting_delegation(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
block_timestamp_secs: u64,
|
||||
) -> Result<VestingDelegation, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetDelegation {
|
||||
@@ -252,7 +252,7 @@ pub trait VestingQueryClient {
|
||||
async fn get_total_delegation_amount(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<Coin, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetTotalDelegationAmount {
|
||||
address: address.to_string(),
|
||||
@@ -264,7 +264,7 @@ pub trait VestingQueryClient {
|
||||
async fn get_delegation_timestamps(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<DelegationTimesResponse, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetDelegationTimes {
|
||||
address: address.to_string(),
|
||||
@@ -275,7 +275,7 @@ pub trait VestingQueryClient {
|
||||
|
||||
async fn get_all_vesting_delegations_paged(
|
||||
&self,
|
||||
start_after: Option<(u32, MixId, u64)>,
|
||||
start_after: Option<(u32, NodeId, u64)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AllDelegationsResponse, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetAllDelegations { start_after, limit })
|
||||
|
||||
+8
-68
@@ -9,10 +9,9 @@ use crate::signing::signer::OfflineSigner;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_mixnet_contract_common::gateway::GatewayConfigUpdate;
|
||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
|
||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, NodeCostParams};
|
||||
use nym_mixnet_contract_common::{Gateway, MixNode, NodeId};
|
||||
use nym_vesting_contract_common::messages::ExecuteMsg as VestingExecuteMsg;
|
||||
use nym_vesting_contract_common::{PledgeCap, VestingSpecification};
|
||||
|
||||
@@ -28,7 +27,7 @@ pub trait VestingSigningClient {
|
||||
|
||||
async fn vesting_update_mixnode_cost_params(
|
||||
&self,
|
||||
new_costs: MixNodeCostParams,
|
||||
new_costs: NodeCostParams,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_vesting_contract(
|
||||
@@ -124,7 +123,7 @@ pub trait VestingSigningClient {
|
||||
async fn vesting_bond_mixnode(
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
cost_params: NodeCostParams,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
@@ -204,7 +203,7 @@ pub trait VestingSigningClient {
|
||||
async fn vesting_track_undelegation(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
amount: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
@@ -222,7 +221,7 @@ pub trait VestingSigningClient {
|
||||
|
||||
async fn vesting_delegate_to_mixnode(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
amount: Coin,
|
||||
on_behalf_of: Option<String>,
|
||||
fee: Option<Fee>,
|
||||
@@ -241,7 +240,7 @@ pub trait VestingSigningClient {
|
||||
|
||||
async fn vesting_undelegate_from_mixnode(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
on_behalf_of: Option<String>,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
@@ -301,7 +300,7 @@ pub trait VestingSigningClient {
|
||||
|
||||
async fn vesting_withdraw_delegator_reward(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_vesting_contract(
|
||||
@@ -354,50 +353,6 @@ pub trait VestingSigningClient {
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_create_family(
|
||||
&self,
|
||||
label: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_vesting_contract(fee, VestingExecuteMsg::CreateFamily { label }, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_join_family(
|
||||
&self,
|
||||
join_permit: MessageSignature,
|
||||
family_head: FamilyHead,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_vesting_contract(
|
||||
fee,
|
||||
VestingExecuteMsg::JoinFamily {
|
||||
join_permit,
|
||||
family_head,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_leave_family(
|
||||
&self,
|
||||
family_head: FamilyHead,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_vesting_contract(fee, VestingExecuteMsg::LeaveFamily { family_head }, vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_kick_family_member(
|
||||
&self,
|
||||
member: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_vesting_contract(fee, VestingExecuteMsg::KickFamilyMember { member }, vec![])
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -446,21 +401,6 @@ mod tests {
|
||||
msg: VestingExecuteMsg,
|
||||
) {
|
||||
match msg {
|
||||
VestingExecuteMsg::CreateFamily { label } => {
|
||||
client.vesting_create_family(label, None).ignore()
|
||||
}
|
||||
VestingExecuteMsg::JoinFamily {
|
||||
join_permit,
|
||||
family_head,
|
||||
} => client
|
||||
.vesting_join_family(join_permit, family_head, None)
|
||||
.ignore(),
|
||||
VestingExecuteMsg::LeaveFamily { family_head } => {
|
||||
client.vesting_leave_family(family_head, None).ignore()
|
||||
}
|
||||
VestingExecuteMsg::KickFamilyMember { member } => {
|
||||
client.vesting_kick_family_member(member, None).ignore()
|
||||
}
|
||||
VestingExecuteMsg::TrackReward { amount, address } => client
|
||||
.vesting_track_reward(amount.into(), address, None)
|
||||
.ignore(),
|
||||
|
||||
-31
@@ -153,20 +153,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
let req = QueryAllBalancesRequest {
|
||||
address: address.to_string(),
|
||||
pagination,
|
||||
resolve_denom: false,
|
||||
};
|
||||
|
||||
let mut res = self
|
||||
.make_abci_query::<_, QueryAllBalancesResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.balances.is_empty();
|
||||
raw_balances.append(&mut res.balances);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -194,13 +187,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.supply.is_empty();
|
||||
supply.append(&mut res.supply);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -341,13 +328,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryCodesResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.code_infos.is_empty();
|
||||
raw_codes.append(&mut res.code_infos);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -392,13 +373,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryContractsByCodeResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.contracts.is_empty();
|
||||
raw_contracts.append(&mut res.contracts);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -454,13 +429,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryContractHistoryResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.entries.is_empty();
|
||||
raw_entries.append(&mut res.entries);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -25,12 +25,12 @@ use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode;
|
||||
use cosmrs::staking::{MsgDelegate, MsgUndelegate};
|
||||
use cosmrs::tx::{self, Msg};
|
||||
use cosmrs::{cosmwasm, AccountId, Any, Tx};
|
||||
use log::debug;
|
||||
use serde::Serialize;
|
||||
use sha2::Digest;
|
||||
use sha2::Sha256;
|
||||
use std::time::SystemTime;
|
||||
use tendermint_rpc::endpoint::broadcast;
|
||||
use tracing::debug;
|
||||
|
||||
fn empty_fee() -> tx::Fee {
|
||||
tx::Fee {
|
||||
|
||||
@@ -7,9 +7,9 @@ use base64::Engine;
|
||||
use cosmrs::abci::TxMsgData;
|
||||
use cosmrs::cosmwasm::MsgExecuteContractResponse;
|
||||
use cosmrs::proto::cosmos::base::query::v1beta1::{PageRequest, PageResponse};
|
||||
use log::error;
|
||||
use prost::bytes::Bytes;
|
||||
use tendermint_rpc::endpoint::broadcast;
|
||||
use tracing::error;
|
||||
|
||||
pub use cosmrs::abci::MsgResponse;
|
||||
|
||||
|
||||
@@ -154,6 +154,23 @@ pub enum NyxdError {
|
||||
|
||||
#[error("the response data has invalid size. got {got} bytes, but expected {expected} bytes instead")]
|
||||
MalformedResponseData { got: usize, expected: usize },
|
||||
|
||||
#[error(
|
||||
"one of the extension query for {contract} failed with the following message: {message}"
|
||||
)]
|
||||
ExtensionQueryFailure { contract: String, message: String },
|
||||
}
|
||||
|
||||
impl NyxdError {
|
||||
pub fn extension_query_failure(
|
||||
contract: impl Into<String>,
|
||||
message: impl Into<String>,
|
||||
) -> Self {
|
||||
NyxdError::ExtensionQueryFailure {
|
||||
contract: contract.into(),
|
||||
message: message.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The purpose of parsing the abci query result is that we want to generate the `pretty_log` if
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
use crate::rpc::TendermintRpcClient;
|
||||
use async_trait::async_trait;
|
||||
use base64::Engine;
|
||||
use cosmrs::tendermint;
|
||||
use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash};
|
||||
use reqwest::header::HeaderMap;
|
||||
use reqwest::{header, RequestBuilder};
|
||||
use tendermint_rpc::dialect::{v0_34, v0_37, v0_38, LatestDialect};
|
||||
use tendermint_rpc::{
|
||||
client::CompatMode,
|
||||
dialect::{self, Dialect},
|
||||
@@ -23,21 +21,8 @@ macro_rules! perform_with_compat {
|
||||
($self:expr, $request:expr) => {{
|
||||
let request = $request;
|
||||
match $self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_38::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_37::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_34 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_34::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => $self.perform_v0_37(request).await,
|
||||
CompatMode::V0_34 => $self.perform_v0_34(request).await,
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -85,11 +70,7 @@ impl ReqwestRpcClient {
|
||||
.headers(headers)
|
||||
}
|
||||
|
||||
async fn perform_request_with_dialect<R, S>(
|
||||
&self,
|
||||
request: R,
|
||||
_dialect: S,
|
||||
) -> Result<R::Output, Error>
|
||||
async fn perform_request<R, S>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<S>,
|
||||
S: Dialect,
|
||||
@@ -100,25 +81,26 @@ impl ReqwestRpcClient {
|
||||
.send()
|
||||
.await
|
||||
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
||||
let response_status = response.status();
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
||||
|
||||
// Successful JSON-RPC requests are expected to return a 200 OK HTTP status.
|
||||
// Otherwise, this means that the HTTP request failed as a whole,
|
||||
// as opposed to the JSON-RPC request returning an error,
|
||||
// and we cannot expect the response body to be a valid JSON-RPC response.
|
||||
if response_status != reqwest::StatusCode::OK {
|
||||
// hehe, that's so nasty but we have to somehow convert between different versions of the same lib
|
||||
return Err(Error::http_request_failed(
|
||||
response_status.as_u16().try_into().unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
R::Response::from_string(bytes).map(Into::into)
|
||||
}
|
||||
|
||||
async fn perform_v0_34<R>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<dialect::v0_34::Dialect>,
|
||||
{
|
||||
self.perform_request(request).await
|
||||
}
|
||||
|
||||
async fn perform_v0_37<R>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<dialect::v0_37::Dialect>,
|
||||
{
|
||||
self.perform_request(request).await
|
||||
}
|
||||
}
|
||||
|
||||
trait TendermintRpcErrorMap {
|
||||
@@ -138,50 +120,18 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
where
|
||||
R: SimpleRequest,
|
||||
{
|
||||
self.perform_request_with_dialect(request, LatestDialect)
|
||||
.await
|
||||
self.perform_request(request).await
|
||||
}
|
||||
|
||||
async fn block<H>(&self, height: H) -> Result<endpoint::block::Response, Error>
|
||||
async fn block_results<H>(&self, height: H) -> Result<block_results::Response, Error>
|
||||
where
|
||||
H: Into<Height> + Send,
|
||||
{
|
||||
perform_with_compat!(self, endpoint::block::Request::new(height.into()))
|
||||
perform_with_compat!(self, block_results::Request::new(height.into()))
|
||||
}
|
||||
|
||||
async fn block_by_hash(
|
||||
&self,
|
||||
hash: tendermint::Hash,
|
||||
) -> Result<endpoint::block_by_hash::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block_by_hash::Request::new(hash))
|
||||
}
|
||||
|
||||
async fn latest_block(&self) -> Result<endpoint::block::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block::Request::default())
|
||||
}
|
||||
|
||||
async fn block_results<H>(&self, height: H) -> Result<endpoint::block_results::Response, Error>
|
||||
where
|
||||
H: Into<Height> + Send,
|
||||
{
|
||||
perform_with_compat!(self, endpoint::block_results::Request::new(height.into()))
|
||||
}
|
||||
|
||||
async fn latest_block_results(&self) -> Result<endpoint::block_results::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block_results::Request::default())
|
||||
}
|
||||
|
||||
async fn block_search(
|
||||
&self,
|
||||
query: Query,
|
||||
page: u32,
|
||||
per_page: u8,
|
||||
order: Order,
|
||||
) -> Result<endpoint::block_search::Response, Error> {
|
||||
perform_with_compat!(
|
||||
self,
|
||||
endpoint::block_search::Request::new(query, page, per_page, order)
|
||||
)
|
||||
async fn latest_block_results(&self) -> Result<block_results::Response, Error> {
|
||||
perform_with_compat!(self, block_results::Request::default())
|
||||
}
|
||||
|
||||
async fn header<H>(&self, height: H) -> Result<endpoint::header::Response, Error>
|
||||
@@ -190,26 +140,11 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
{
|
||||
let height = height.into();
|
||||
match self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(
|
||||
endpoint::header::Request::new(height),
|
||||
v0_38::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(
|
||||
endpoint::header::Request::new(height),
|
||||
v0_37::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => self.perform(endpoint::header::Request::new(height)).await,
|
||||
CompatMode::V0_34 => {
|
||||
// Back-fill with a request to /block endpoint and
|
||||
// taking just the header from the response.
|
||||
let resp = self
|
||||
.perform_request_with_dialect(block::Request::new(height), v0_34::Dialect)
|
||||
.await?;
|
||||
let resp = self.perform_v0_34(block::Request::new(height)).await?;
|
||||
Ok(resp.into())
|
||||
}
|
||||
}
|
||||
@@ -217,25 +152,12 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
|
||||
async fn header_by_hash(&self, hash: Hash) -> Result<header_by_hash::Response, Error> {
|
||||
match self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(
|
||||
header_by_hash::Request::new(hash),
|
||||
v0_38::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(
|
||||
header_by_hash::Request::new(hash),
|
||||
v0_37::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => self.perform(header_by_hash::Request::new(hash)).await,
|
||||
CompatMode::V0_34 => {
|
||||
// Back-fill with a request to /block_by_hash endpoint and
|
||||
// taking just the header from the response.
|
||||
let resp = self
|
||||
.perform_request_with_dialect(block_by_hash::Request::new(hash), v0_34::Dialect)
|
||||
.perform_v0_34(block_by_hash::Request::new(hash))
|
||||
.await?;
|
||||
Ok(resp.into())
|
||||
}
|
||||
@@ -245,18 +167,8 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
/// `/broadcast_evidence`: broadcast an evidence.
|
||||
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
|
||||
match self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_38::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_37::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_34 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_34::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => self.perform(evidence::Request::new(e)).await,
|
||||
CompatMode::V0_34 => self.perform_v0_34(evidence::Request::new(e)).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,15 @@ use comfy_table::Table;
|
||||
use nym_credential_storage::initialise_persistent_storage;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// Specify which type of ticketbook
|
||||
#[clap(long, default_value_t = TicketType::V1MixnetEntry)]
|
||||
pub(crate) ticketbook_type: TicketType,
|
||||
|
||||
/// Specify the index of the ticket to retrieve from the ticketbook.
|
||||
/// By default, the current unspent value is used.
|
||||
#[clap(long, group = "output")]
|
||||
@@ -62,7 +67,7 @@ pub async fn execute(args: Args) -> anyhow::Result<()> {
|
||||
|
||||
let persistent_storage = initialise_persistent_storage(&credentials_store).await;
|
||||
let Some(mut next_ticketbook) = persistent_storage
|
||||
.get_next_unspent_usable_ticketbook(0)
|
||||
.get_next_unspent_usable_ticketbook(args.ticketbook_type.to_string(), 0)
|
||||
.await?
|
||||
else {
|
||||
bail!(
|
||||
|
||||
@@ -107,7 +107,7 @@ async fn issue_to_file(args: Args, client: SigningClient) -> anyhow::Result<()>
|
||||
utils::issue_credential(&client, &credentials_store, &secret, args.ticketbook_type).await?;
|
||||
|
||||
let ticketbook = credentials_store
|
||||
.get_next_unspent_usable_ticketbook(0)
|
||||
.get_next_unspent_usable_ticketbook(args.ticketbook_type.to_string(), 0)
|
||||
.await?
|
||||
.ok_or(anyhow!("we just issued a ticketbook, it must be present!"))?
|
||||
.ticketbook;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Decimal;
|
||||
use log::{debug, info};
|
||||
use nym_mixnet_contract_common::reward_params::RewardedSetParams;
|
||||
use nym_mixnet_contract_common::{
|
||||
InitialRewardingParams, InstantiateMsg, OperatingCostRange, Percent, ProfitMarginRange,
|
||||
};
|
||||
@@ -56,11 +57,17 @@ pub struct Args {
|
||||
#[clap(long, default_value_t = 2)]
|
||||
pub interval_pool_emission: u64,
|
||||
|
||||
#[clap(long, default_value_t = 240)]
|
||||
pub rewarded_set_size: u32,
|
||||
#[clap(long, default_value_t = 50)]
|
||||
pub(crate) entry_gateways: u32,
|
||||
|
||||
#[clap(long, default_value_t = 240)]
|
||||
pub active_set_size: u32,
|
||||
#[clap(long, default_value_t = 70)]
|
||||
pub(crate) exit_gateways: u32,
|
||||
|
||||
#[clap(long, default_value_t = 120)]
|
||||
pub(crate) mixnodes: u32,
|
||||
|
||||
#[clap(long, default_value_t = 0)]
|
||||
pub(crate) standby: u32,
|
||||
|
||||
#[clap(long, default_value_t = Percent::zero())]
|
||||
pub minimum_profit_margin_percent: Percent,
|
||||
@@ -95,8 +102,13 @@ pub async fn generate(args: Args) {
|
||||
.expect("active_set_work_factor can't be converted to Decimal"),
|
||||
interval_pool_emission: Percent::from_percentage_value(args.interval_pool_emission)
|
||||
.expect("interval_pool_emission can't be converted to Percent"),
|
||||
rewarded_set_size: args.rewarded_set_size,
|
||||
active_set_size: args.active_set_size,
|
||||
|
||||
rewarded_set_params: RewardedSetParams {
|
||||
entry_gateways: args.entry_gateways,
|
||||
exit_gateways: args.exit_gateways,
|
||||
mixnodes: args.mixnodes,
|
||||
standby: args.standby,
|
||||
},
|
||||
};
|
||||
|
||||
debug!("initial_rewarding_params: {:?}", initial_rewarding_params);
|
||||
|
||||
@@ -7,13 +7,14 @@ pub mod execute_contract;
|
||||
pub mod generators;
|
||||
pub mod init_contract;
|
||||
pub mod migrate_contract;
|
||||
pub mod raw_contract_state;
|
||||
pub mod upload_contract;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
pub struct Cosmwasm {
|
||||
#[clap(subcommand)]
|
||||
pub command: Option<CosmwasmCommands>,
|
||||
pub command: CosmwasmCommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
@@ -28,4 +29,6 @@ pub enum CosmwasmCommands {
|
||||
Migrate(crate::validator::cosmwasm::migrate_contract::Args),
|
||||
/// Execute a WASM smart contract method
|
||||
Execute(crate::validator::cosmwasm::execute_contract::Args),
|
||||
/// Obtain raw contract state of a cosmwasm smart contract
|
||||
RawContractState(crate::validator::cosmwasm::raw_contract_state::Args),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::QueryClient;
|
||||
use clap::Parser;
|
||||
use cosmrs::AccountId;
|
||||
use log::trace;
|
||||
use nym_validator_client::nyxd::CosmWasmClient;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long, value_parser)]
|
||||
#[clap(help = "The address of contract to get the state of")]
|
||||
pub contract: AccountId,
|
||||
|
||||
#[clap(short, long)]
|
||||
#[clap(help = "Output file for the retrieved contract state")]
|
||||
pub output: PathBuf,
|
||||
}
|
||||
|
||||
pub async fn execute(args: Args, client: QueryClient) -> anyhow::Result<()> {
|
||||
trace!("args: {args:?}");
|
||||
|
||||
let output = File::create(&args.output)?;
|
||||
let raw = client.query_all_contract_state(&args.contract).await?;
|
||||
|
||||
serde_json::to_writer(output, &raw)?;
|
||||
println!(
|
||||
"wrote {} key-value from {} pairs into '{}'",
|
||||
raw.len(),
|
||||
args.contract,
|
||||
fs::canonicalize(args.output)?.display()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -4,13 +4,13 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::{Coin, MixId};
|
||||
use nym_mixnet_contract_common::{Coin, NodeId};
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub mix_id: Option<MixId>,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
@@ -43,7 +43,7 @@ pub async fn delegate_to_mixnode(args: Args, client: SigningClient) {
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let res = client
|
||||
.delegate_to_mixnode(mix_id, coin.into(), None)
|
||||
.delegate(mix_id, coin.into(), None)
|
||||
.await
|
||||
.expect("failed to delegate to mixnode!");
|
||||
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
use std::fs::OpenOptions;
|
||||
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use csv::WriterBuilder;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::ExecuteMsg;
|
||||
use nym_mixnet_contract_common::ExecuteMsg::{DelegateToMixnode, UndelegateFromMixnode};
|
||||
|
||||
use nym_mixnet_contract_common::PendingEpochEventKind::{Delegate, Undelegate};
|
||||
use nym_mixnet_contract_common::PendingEpochEventKind;
|
||||
use nym_validator_client::nyxd::contract_traits::{NymContractsProvider, PagedMixnetQueryClient};
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
use std::fs::OpenOptions;
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use crate::utils::pretty_coin;
|
||||
@@ -40,7 +37,7 @@ pub struct Args {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InputFileRow {
|
||||
pub mix_id: String,
|
||||
pub node_id: String,
|
||||
pub amount: Coin,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
@@ -76,7 +73,7 @@ impl InputFileReader {
|
||||
}
|
||||
|
||||
rows.push(InputFileRow {
|
||||
mix_id,
|
||||
node_id: mix_id,
|
||||
amount: Coin {
|
||||
amount: micro_nym_amount,
|
||||
denom: "unym".to_string(),
|
||||
@@ -140,8 +137,10 @@ async fn fetch_delegation_data(
|
||||
let mut pending_delegation_map: HashMap<String, Coin> = HashMap::new();
|
||||
|
||||
for delegation in delegations {
|
||||
existing_delegation_map
|
||||
.insert(delegation.mix_id.to_string(), Coin::from(delegation.amount));
|
||||
existing_delegation_map.insert(
|
||||
delegation.node_id.to_string(),
|
||||
Coin::from(delegation.amount),
|
||||
);
|
||||
}
|
||||
|
||||
// Look for pending delegate / undelegate events which might be of interest to us
|
||||
@@ -155,27 +154,27 @@ async fn fetch_delegation_data(
|
||||
for event in pending_events {
|
||||
match event.event.kind {
|
||||
// If a pending undelegate tx is found, remove it from delegation map
|
||||
Undelegate { owner, mix_id, .. } => {
|
||||
PendingEpochEventKind::Undelegate { owner, node_id, .. } => {
|
||||
if owner == address.as_ref()
|
||||
&& existing_delegation_map.contains_key(&mix_id.to_string())
|
||||
&& existing_delegation_map.contains_key(&node_id.to_string())
|
||||
{
|
||||
existing_delegation_map.remove(&mix_id.to_string());
|
||||
existing_delegation_map.remove(&node_id.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// If a pending delegation event is found, gather them to consolidate later
|
||||
Delegate {
|
||||
PendingEpochEventKind::Delegate {
|
||||
owner,
|
||||
mix_id,
|
||||
node_id,
|
||||
amount,
|
||||
..
|
||||
} => {
|
||||
if owner == address.as_ref() {
|
||||
let mut amount = Coin::from(amount);
|
||||
if let Some(pending_record) = pending_delegation_map.get(&mix_id.to_string()) {
|
||||
if let Some(pending_record) = pending_delegation_map.get(&node_id.to_string()) {
|
||||
amount.amount += pending_record.amount;
|
||||
}
|
||||
pending_delegation_map.insert(mix_id.to_string(), amount);
|
||||
pending_delegation_map.insert(node_id.to_string(), amount);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -217,7 +216,7 @@ pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) {
|
||||
for row in &records.rows {
|
||||
let input_amount = row.amount.amount;
|
||||
let existing_delegation_amount = existing_delegation_map
|
||||
.get(&row.mix_id)
|
||||
.get(&row.node_id)
|
||||
.map_or(0, |coin| coin.amount);
|
||||
|
||||
match existing_delegation_amount.cmp(&input_amount) {
|
||||
@@ -229,25 +228,26 @@ pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) {
|
||||
amount: input_amount - existing_delegation_amount,
|
||||
denom: row.amount.denom.clone(),
|
||||
};
|
||||
let mix_id = row.mix_id.clone().parse::<u32>().unwrap();
|
||||
delegation_msgs.push((DelegateToMixnode { mix_id }, vec![difference.clone()]));
|
||||
let node_id = row.node_id.clone().parse::<u32>().unwrap();
|
||||
delegation_msgs.push((ExecuteMsg::Delegate { node_id }, vec![difference.clone()]));
|
||||
delegation_table.add_row(&[
|
||||
row.mix_id.clone(),
|
||||
row.node_id.clone(),
|
||||
pretty_coin(&row.amount),
|
||||
pretty_coin(&difference),
|
||||
]);
|
||||
}
|
||||
|
||||
Ordering::Greater => {
|
||||
let mix_id = row.mix_id.clone().parse::<u32>().unwrap();
|
||||
let node_id = row.node_id.clone().parse::<u32>().unwrap();
|
||||
let coins: Vec<Coin> = vec![];
|
||||
undelegation_msgs.push((UndelegateFromMixnode { mix_id }, coins));
|
||||
undelegation_table.add_row(&[row.mix_id.clone()]);
|
||||
undelegation_msgs.push((ExecuteMsg::Undelegate { node_id }, coins));
|
||||
undelegation_table.add_row(&[row.node_id.clone()]);
|
||||
|
||||
if row.amount.amount > 0 {
|
||||
delegation_msgs.push((DelegateToMixnode { mix_id }, vec![row.amount.clone()]));
|
||||
delegation_msgs
|
||||
.push((ExecuteMsg::Delegate { node_id }, vec![row.amount.clone()]));
|
||||
delegation_table.add_row(&[
|
||||
row.mix_id.clone(),
|
||||
row.node_id.clone(),
|
||||
pretty_coin(&row.amount),
|
||||
pretty_coin(&row.amount),
|
||||
]);
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub mix_id: Option<MixId>,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
|
||||
@@ -66,7 +66,7 @@ async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientW
|
||||
for delegation in delegations {
|
||||
table.add_row(vec![
|
||||
to_iso_timestamp(delegation.height as u32, client).await,
|
||||
delegation.mix_id.to_string(),
|
||||
delegation.node_id.to_string(),
|
||||
pretty_cosmwasm_coin(&delegation.amount),
|
||||
delegation
|
||||
.proxy
|
||||
@@ -93,7 +93,7 @@ async fn print_delegation_events(events: Vec<PendingEpochEvent>, client: &Signin
|
||||
match event.event.kind {
|
||||
PendingEpochEventKind::Delegate {
|
||||
owner,
|
||||
mix_id,
|
||||
node_id: mix_id,
|
||||
amount,
|
||||
proxy,
|
||||
..
|
||||
@@ -110,7 +110,7 @@ async fn print_delegation_events(events: Vec<PendingEpochEvent>, client: &Signin
|
||||
}
|
||||
PendingEpochEventKind::Undelegate {
|
||||
owner,
|
||||
mix_id,
|
||||
node_id: mix_id,
|
||||
proxy,
|
||||
..
|
||||
} => {
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub mix_id: Option<MixId>,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
|
||||
+2
-2
@@ -4,13 +4,13 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub mix_id: Option<MixId>,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub mix_id: Option<MixId>,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
@@ -36,7 +36,7 @@ pub async fn undelegate_from_mixnode(args: Args, client: SigningClient) {
|
||||
};
|
||||
|
||||
let res = client
|
||||
.undelegate_from_mixnode(mix_id, None)
|
||||
.undelegate(mix_id, None)
|
||||
.await
|
||||
.expect("failed to remove stake from mixnode!");
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
|
||||
use nym_mixnet_contract_common::{Coin, MixId};
|
||||
use nym_mixnet_contract_common::{Coin, NodeId};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
use nym_validator_client::nyxd::contract_traits::VestingSigningClient;
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::context::SigningClient;
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub mix_id: Option<MixId>,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
use nym_validator_client::nyxd::contract_traits::VestingSigningClient;
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::context::SigningClient;
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub mix_id: Option<MixId>,
|
||||
pub mix_id: Option<NodeId>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
|
||||
@@ -5,6 +5,7 @@ use clap::{Args, Subcommand};
|
||||
|
||||
pub mod bond_gateway;
|
||||
pub mod gateway_bonding_sign_payload;
|
||||
pub mod nymnode_migration;
|
||||
pub mod settings;
|
||||
pub mod unbond_gateway;
|
||||
pub mod vesting_bond_gateway;
|
||||
@@ -31,4 +32,6 @@ pub enum MixnetOperatorsGatewayCommands {
|
||||
VestingUnbond(vesting_unbond_gateway::Args),
|
||||
/// Create base58-encoded payload required for producing valid bonding signature.
|
||||
CreateGatewayBondingSignPayload(gateway_bonding_sign_payload::Args),
|
||||
/// Migrate the gateway into a Nym Node
|
||||
MigrateToNymnode(nymnode_migration::Args),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Uint128;
|
||||
use log::info;
|
||||
use nym_contracts_common::Percent;
|
||||
use nym_mixnet_contract_common::{
|
||||
NodeCostParams, DEFAULT_INTERVAL_OPERATING_COST_AMOUNT, DEFAULT_PROFIT_MARGIN_PERCENT,
|
||||
};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::CosmWasmCoin;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub profit_margin_percent: Option<u64>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub interval_operating_cost: Option<u128>,
|
||||
}
|
||||
|
||||
pub async fn migrate_to_nymnode(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
let cost_params =
|
||||
if args.profit_margin_percent.is_some() || args.interval_operating_cost.is_some() {
|
||||
Some(NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(
|
||||
args.profit_margin_percent
|
||||
.unwrap_or(DEFAULT_PROFIT_MARGIN_PERCENT),
|
||||
)
|
||||
.unwrap(),
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(
|
||||
args.interval_operating_cost
|
||||
.unwrap_or(DEFAULT_INTERVAL_OPERATING_COST_AMOUNT),
|
||||
),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let res = client
|
||||
.migrate_legacy_gateway(cost_params, None)
|
||||
.await
|
||||
.expect("failed to migrate gateway!");
|
||||
|
||||
info!("migration result: {:?}", res)
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Uint128;
|
||||
use log::{info, warn};
|
||||
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::{Coin, MixNodeCostParams, Percent};
|
||||
use nym_mixnet_contract_common::{
|
||||
Coin, NodeCostParams, Percent, DEFAULT_INTERVAL_OPERATING_COST_AMOUNT,
|
||||
DEFAULT_PROFIT_MARGIN_PERCENT,
|
||||
};
|
||||
use nym_network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::CosmWasmCoin;
|
||||
|
||||
use crate::context::SigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
@@ -42,7 +43,7 @@ pub struct Args {
|
||||
pub version: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
pub profit_margin_percent: Option<u64>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
@@ -85,14 +86,18 @@ pub async fn bond_mixnode(args: Args, client: SigningClient) {
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
let cost_params = NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(
|
||||
args.profit_margin_percent.unwrap_or(10) as u64,
|
||||
args.profit_margin_percent
|
||||
.unwrap_or(DEFAULT_PROFIT_MARGIN_PERCENT),
|
||||
)
|
||||
.unwrap(),
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
|
||||
amount: Uint128::new(
|
||||
args.interval_operating_cost
|
||||
.unwrap_or(DEFAULT_INTERVAL_OPERATING_COST_AMOUNT),
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// Label that is going to be used for creating the family
|
||||
#[arg(long)]
|
||||
pub family_label: String,
|
||||
}
|
||||
|
||||
pub async fn create_family(args: Args, client: SigningClient) {
|
||||
info!("Create family");
|
||||
|
||||
let res = client
|
||||
.create_family(args.family_label, None)
|
||||
.await
|
||||
.expect("failed to create family");
|
||||
|
||||
info!("Family creation result: {:?}", res);
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::QueryClient;
|
||||
use crate::utils::DataWrapper;
|
||||
use clap::Parser;
|
||||
use cosmrs::AccountId;
|
||||
use log::info;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_mixnet_contract_common::construct_family_join_permit;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// Account address (i.e. owner of the family head) which will be used for issuing the permit
|
||||
#[arg(long)]
|
||||
pub address: AccountId,
|
||||
|
||||
// might as well validate the value when parsing the arguments
|
||||
/// Identity of the member for whom we're issuing the permit
|
||||
#[arg(long)]
|
||||
pub member: identity::PublicKey,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
}
|
||||
|
||||
pub async fn create_family_join_permit_sign_payload(args: Args, client: QueryClient) {
|
||||
info!("Create family join permit sign payload");
|
||||
|
||||
// get the address of our mixnode to recover the family head information
|
||||
let Some(mixnode) = client
|
||||
.get_owned_mixnode(&args.address)
|
||||
.await
|
||||
.unwrap()
|
||||
.mixnode_details
|
||||
else {
|
||||
eprintln!("{} does not seem to even own a mixnode!", args.address);
|
||||
return;
|
||||
};
|
||||
|
||||
// make sure this mixnode is actually a family head
|
||||
if client
|
||||
.get_node_family_by_head(mixnode.bond_information.identity().to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.family
|
||||
.is_none()
|
||||
{
|
||||
eprintln!("{} does not even seem to own a family!", args.address);
|
||||
return;
|
||||
}
|
||||
|
||||
let nonce = match client.get_signing_nonce(&args.address).await {
|
||||
Ok(nonce) => nonce,
|
||||
Err(err) => {
|
||||
eprint!(
|
||||
"failed to query for the signing nonce of {}: {err}",
|
||||
args.address
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let head = FamilyHead::new(mixnode.bond_information.identity());
|
||||
|
||||
let payload = construct_family_join_permit(nonce, head, args.member.to_base58_string());
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// The head of the family that we intend to join
|
||||
#[arg(long)]
|
||||
pub family_head: identity::PublicKey,
|
||||
|
||||
/// Permission, as provided by the family head, for joining the family
|
||||
#[arg(long)]
|
||||
pub join_permit: MessageSignature,
|
||||
}
|
||||
|
||||
pub async fn join_family(args: Args, client: SigningClient) {
|
||||
info!("Join family");
|
||||
|
||||
let family_head = FamilyHead::new(args.family_head.to_base58_string());
|
||||
|
||||
let res = client
|
||||
.join_family(args.join_permit, family_head, None)
|
||||
.await
|
||||
.expect("failed to join family");
|
||||
|
||||
info!("Family join result: {:?}", res);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::VestingSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// The member of the family that we intend to kick
|
||||
#[arg(long)]
|
||||
pub member: identity::PublicKey,
|
||||
|
||||
/// Indicates whether the family was created (and managed) via the vesting contract
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
}
|
||||
|
||||
pub async fn kick_family_member(args: Args, client: SigningClient) {
|
||||
info!("Leave family");
|
||||
|
||||
let member = args.member.to_base58_string();
|
||||
|
||||
let res = if args.with_vesting_account {
|
||||
client
|
||||
.vesting_kick_family_member(member, None)
|
||||
.await
|
||||
.expect("failed to kick family member with vesting account")
|
||||
} else {
|
||||
client
|
||||
.kick_family_member(member, None)
|
||||
.await
|
||||
.expect("failed to kick family member")
|
||||
};
|
||||
|
||||
info!("Family leave result: {:?}", res);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
/// The head of the family that we intend to leave
|
||||
#[arg(long)]
|
||||
pub family_head: identity::PublicKey,
|
||||
}
|
||||
|
||||
pub async fn leave_family(args: Args, client: SigningClient) {
|
||||
info!("Leave family");
|
||||
|
||||
let family_head = FamilyHead::new(args.family_head.to_base58_string());
|
||||
|
||||
let res = client
|
||||
.leave_family(family_head, None)
|
||||
.await
|
||||
.expect("failed to leave family");
|
||||
|
||||
info!("Family leave result: {:?}", res);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod create_family;
|
||||
pub mod create_family_join_permit_sign_payload;
|
||||
pub mod join_family;
|
||||
pub mod kick_family_member;
|
||||
pub mod leave_family;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
pub struct MixnetOperatorsMixnodeFamilies {
|
||||
#[clap(subcommand)]
|
||||
pub command: MixnetOperatorsMixnodeFamiliesCommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsMixnodeFamiliesCommands {
|
||||
/// Create family
|
||||
CreateFamily(create_family::Args),
|
||||
|
||||
/// Join family
|
||||
JoinFamily(join_family::Args),
|
||||
|
||||
/// Leave family,
|
||||
LeaveFamily(leave_family::Args),
|
||||
|
||||
/// Kick family member
|
||||
KickFamilyMember(kick_family_member::Args),
|
||||
|
||||
/// Create a message payload that is required to get signed in order to obtain a permit for joining family
|
||||
CreateFamilyJoinPermitSignPayload(create_family_join_permit_sign_payload::Args),
|
||||
}
|
||||
+10
-5
@@ -8,7 +8,8 @@ use cosmwasm_std::{Coin, Uint128};
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_contracts_common::Percent;
|
||||
use nym_mixnet_contract_common::{
|
||||
construct_legacy_mixnode_bonding_sign_payload, MixNodeCostParams,
|
||||
construct_legacy_mixnode_bonding_sign_payload, NodeCostParams,
|
||||
DEFAULT_INTERVAL_OPERATING_COST_AMOUNT, DEFAULT_PROFIT_MARGIN_PERCENT,
|
||||
};
|
||||
use nym_network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
@@ -40,7 +41,7 @@ pub struct Args {
|
||||
pub version: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
pub profit_margin_percent: Option<u64>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
@@ -75,14 +76,18 @@ pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
let cost_params = NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(
|
||||
args.profit_margin_percent.unwrap_or(10) as u64,
|
||||
args.profit_margin_percent
|
||||
.unwrap_or(DEFAULT_PROFIT_MARGIN_PERCENT),
|
||||
)
|
||||
.unwrap(),
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
|
||||
amount: Uint128::new(
|
||||
args.interval_operating_cost
|
||||
.unwrap_or(DEFAULT_INTERVAL_OPERATING_COST_AMOUNT),
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ use clap::{Args, Subcommand};
|
||||
|
||||
pub mod bond_mixnode;
|
||||
pub mod decrease_pledge;
|
||||
pub mod families;
|
||||
pub mod keys;
|
||||
pub mod migrate_vested_mixnode;
|
||||
pub mod mixnode_bonding_sign_payload;
|
||||
pub mod nymnode_migration;
|
||||
pub mod pledge_more;
|
||||
pub mod rewards;
|
||||
pub mod settings;
|
||||
@@ -33,8 +33,6 @@ pub enum MixnetOperatorsMixnodeCommands {
|
||||
Rewards(rewards::MixnetOperatorsMixnodeRewards),
|
||||
/// Manage your mixnode settings stored in the directory
|
||||
Settings(settings::MixnetOperatorsMixnodeSettings),
|
||||
/// Operations for mixnode families
|
||||
Families(families::MixnetOperatorsMixnodeFamilies),
|
||||
/// Bond to a mixnode
|
||||
Bond(bond_mixnode::Args),
|
||||
/// Unbond from a mixnode
|
||||
@@ -55,4 +53,6 @@ pub enum MixnetOperatorsMixnodeCommands {
|
||||
DecreasePledgeVesting(vesting_decrease_pledge::Args),
|
||||
/// Migrate the mixnode to use liquid tokens
|
||||
MigrateVestedNode(migrate_vested_mixnode::Args),
|
||||
/// Migrate the mixnode into a Nym Node
|
||||
MigrateToNymnode(nymnode_migration::Args),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {}
|
||||
|
||||
pub async fn migrate_to_nymnode(_args: Args, client: SigningClient) {
|
||||
let res = client
|
||||
.migrate_legacy_mixnode(None)
|
||||
.await
|
||||
.expect("failed to migrate mixnode!");
|
||||
|
||||
info!("migration result: {:?}", res)
|
||||
}
|
||||
+10
-62
@@ -2,12 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use crate::validator::mixnet::operators::nymnode;
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Uint128;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::{MixNodeCostParams, Percent};
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
use nym_validator_client::nyxd::CosmWasmCoin;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
@@ -24,62 +20,14 @@ pub struct Args {
|
||||
pub interval_operating_cost: Option<u128>,
|
||||
}
|
||||
|
||||
pub async fn update_cost_params(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
fn convert_to_percent(value: u64) -> Percent {
|
||||
Percent::from_percentage_value(value).expect("Invalid value")
|
||||
}
|
||||
|
||||
let default_profit_margin: Percent = convert_to_percent(20);
|
||||
|
||||
let mixownership_response = match client.get_owned_mixnode(&client.address()).await {
|
||||
Ok(response) => response,
|
||||
Err(_) => {
|
||||
eprintln!("Failed to obtain owned mixnode");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mix_id = match mixownership_response.mixnode_details {
|
||||
Some(details) => details.bond_information.mix_id,
|
||||
None => {
|
||||
eprintln!("Failed to obtain mixnode details");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let rewarding_response = match client.get_mixnode_rewarding_details(mix_id).await {
|
||||
Ok(details) => details,
|
||||
Err(_) => {
|
||||
eprintln!("Failed to obtain rewarding details");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let profit_margin_percent = rewarding_response
|
||||
.rewarding_details
|
||||
.map(|rd| rd.cost_params.profit_margin_percent)
|
||||
.unwrap_or(default_profit_margin);
|
||||
|
||||
let profit_margin_value = args
|
||||
.profit_margin_percent
|
||||
.map(|pm| convert_to_percent(pm as u64))
|
||||
.unwrap_or(profit_margin_percent);
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
profit_margin_percent: profit_margin_value,
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
|
||||
pub async fn update_cost_params(args: Args, client: SigningClient) -> anyhow::Result<()> {
|
||||
// the below can handle both, nymnode and legacy mixnode
|
||||
nymnode::settings::update_cost_params::update_cost_params(
|
||||
nymnode::settings::update_cost_params::Args {
|
||||
profit_margin_percent: args.profit_margin_percent,
|
||||
interval_operating_cost: args.interval_operating_cost,
|
||||
},
|
||||
};
|
||||
|
||||
info!("Starting mixnode params updating!");
|
||||
let res = client
|
||||
.update_mixnode_cost_params(cost_params, None)
|
||||
.await
|
||||
.expect("failed to update cost params");
|
||||
|
||||
info!("Cost params result: {:?}", res)
|
||||
client,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ use clap::Parser;
|
||||
use cosmwasm_std::Uint128;
|
||||
use log::{info, warn};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::{Coin, MixNodeCostParams};
|
||||
use nym_mixnet_contract_common::{
|
||||
Coin, NodeCostParams, DEFAULT_INTERVAL_OPERATING_COST_AMOUNT, DEFAULT_PROFIT_MARGIN_PERCENT,
|
||||
};
|
||||
use nym_mixnet_contract_common::{MixNode, Percent};
|
||||
use nym_network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
@@ -40,7 +42,7 @@ pub struct Args {
|
||||
pub version: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
pub profit_margin_percent: Option<u64>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
@@ -84,14 +86,18 @@ pub async fn vesting_bond_mixnode(client: SigningClient, args: Args, denom: &str
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
let cost_params = NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(
|
||||
args.profit_margin_percent.unwrap_or(10) as u64,
|
||||
args.profit_margin_percent
|
||||
.unwrap_or(DEFAULT_PROFIT_MARGIN_PERCENT),
|
||||
)
|
||||
.unwrap(),
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
|
||||
amount: Uint128::new(
|
||||
args.interval_operating_cost
|
||||
.unwrap_or(DEFAULT_INTERVAL_OPERATING_COST_AMOUNT),
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use clap::{Args, Subcommand};
|
||||
pub mod gateway;
|
||||
pub mod identity_key;
|
||||
pub mod mixnode;
|
||||
pub mod nymnode;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
@@ -17,9 +18,11 @@ pub struct MixnetOperators {
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsCommands {
|
||||
/// Manage your mixnode
|
||||
/// Manage your Nym Node
|
||||
Nymnode(nymnode::MixnetOperatorsNymNode),
|
||||
/// Manage your legacy mixnode
|
||||
Mixnode(mixnode::MixnetOperatorsMixnode),
|
||||
/// Manage your gateway
|
||||
/// Manage your legacy gateway
|
||||
Gateway(gateway::MixnetOperatorsGateway),
|
||||
/// Sign messages using your private identity key
|
||||
IdentityKey(identity_key::MixnetOperatorsIdentityKey),
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Uint128;
|
||||
use log::{info, warn};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::{
|
||||
Coin, NodeCostParams, Percent, DEFAULT_INTERVAL_OPERATING_COST_AMOUNT,
|
||||
DEFAULT_PROFIT_MARGIN_PERCENT,
|
||||
};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
use nym_validator_client::nyxd::CosmWasmCoin;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub host: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub signature: MessageSignature,
|
||||
|
||||
#[clap(long)]
|
||||
pub http_api_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub profit_margin_percent: Option<u64>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub interval_operating_cost: Option<u128>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub amount: u128,
|
||||
|
||||
#[clap(short, long)]
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
pub async fn bond_nymnode(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
info!("Starting nym node bonding!");
|
||||
|
||||
// if we're trying to bond less than 1 token
|
||||
if args.amount < 1_000_000 && !args.force {
|
||||
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
|
||||
return;
|
||||
}
|
||||
|
||||
let nymnode = nym_mixnet_contract_common::NymNode {
|
||||
host: args.host,
|
||||
custom_http_port: args.http_api_port,
|
||||
identity_key: args.identity_key,
|
||||
};
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let cost_params = NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(
|
||||
args.profit_margin_percent
|
||||
.unwrap_or(DEFAULT_PROFIT_MARGIN_PERCENT),
|
||||
)
|
||||
.unwrap(),
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(
|
||||
args.interval_operating_cost
|
||||
.unwrap_or(DEFAULT_INTERVAL_OPERATING_COST_AMOUNT),
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
let res = client
|
||||
.bond_nymnode(nymnode, cost_params, args.signature, coin.into(), None)
|
||||
.await
|
||||
.expect("failed to bond nymnode!");
|
||||
|
||||
info!("Bonding result: {:?}", res)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use base64::Engine;
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(short, long)]
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
pub fn decode_node_key(args: Args) {
|
||||
let b64_decoded = base64::prelude::BASE64_STANDARD
|
||||
.decode(args.key)
|
||||
.expect("failed to decode base64 string");
|
||||
let b58_encoded = bs58::encode(&b64_decoded).into_string();
|
||||
|
||||
println!("{b58_encoded}")
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod decode_node_key;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
pub struct MixnetOperatorsNymNodeKeys {
|
||||
#[clap(subcommand)]
|
||||
pub command: MixnetOperatorsNymNodeKeysCommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsNymNodeKeysCommands {
|
||||
/// Decode a Nym Node key
|
||||
DecodeNodeKey(decode_node_key::Args),
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod bond_nymnode;
|
||||
pub mod keys;
|
||||
pub mod nymnode_bonding_sign_payload;
|
||||
pub mod pledge;
|
||||
pub mod rewards;
|
||||
pub mod settings;
|
||||
pub mod unbond_nymnode;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
pub struct MixnetOperatorsNymNode {
|
||||
#[clap(subcommand)]
|
||||
pub command: MixnetOperatorsNymNodeCommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsNymNodeCommands {
|
||||
/// Operations for Nym Node keys
|
||||
Keys(keys::MixnetOperatorsNymNodeKeys),
|
||||
|
||||
/// Manage your Nym Node operator rewards
|
||||
Rewards(rewards::MixnetOperatorsNymNodeRewards),
|
||||
|
||||
/// Manage your Nym Node settings stored in the directory
|
||||
Settings(settings::MixnetOperatorsNymNodeSettings),
|
||||
|
||||
/// Manage your Nym Node pledge
|
||||
Pledge(pledge::MixnetOperatorsNymNodePledge),
|
||||
|
||||
/// Bond to a Nym Node
|
||||
Bond(bond_nymnode::Args),
|
||||
|
||||
/// Unbond from a Nym Node
|
||||
Unbond(unbond_nymnode::Args),
|
||||
|
||||
/// Create base58-encoded payload required for producing valid bonding signature.
|
||||
CreateNodeBondingSignPayload(nymnode_bonding_sign_payload::Args),
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use crate::utils::{account_id_to_cw_addr, DataWrapper};
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::{Coin, Uint128};
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_contracts_common::Percent;
|
||||
use nym_mixnet_contract_common::{
|
||||
construct_nym_node_bonding_sign_payload, NodeCostParams,
|
||||
DEFAULT_INTERVAL_OPERATING_COST_AMOUNT, DEFAULT_PROFIT_MARGIN_PERCENT,
|
||||
};
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
|
||||
use nym_validator_client::nyxd::CosmWasmCoin;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub host: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub custom_http_api_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub profit_margin_percent: Option<u64>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub interval_operating_cost: Option<u128>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub amount: u128,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
pub output: OutputFormat,
|
||||
}
|
||||
|
||||
pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
let mixnode = nym_mixnet_contract_common::NymNode {
|
||||
host: args.host,
|
||||
custom_http_port: args.custom_http_api_port,
|
||||
identity_key: args.identity_key,
|
||||
};
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let cost_params = NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(
|
||||
args.profit_margin_percent
|
||||
.unwrap_or(DEFAULT_PROFIT_MARGIN_PERCENT),
|
||||
)
|
||||
.unwrap(),
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(
|
||||
args.interval_operating_cost
|
||||
.unwrap_or(DEFAULT_INTERVAL_OPERATING_COST_AMOUNT),
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
let nonce = match client.get_signing_nonce(&client.address()).await {
|
||||
Ok(nonce) => nonce,
|
||||
Err(err) => {
|
||||
eprint!(
|
||||
"failed to query for the signing nonce of {}: {err}",
|
||||
client.address()
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let address = account_id_to_cw_addr(&client.address());
|
||||
|
||||
let payload =
|
||||
construct_nym_node_bonding_sign_payload(nonce, address, coin, mixnode, cost_params);
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::Coin;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub decrease_by: u128,
|
||||
}
|
||||
|
||||
pub async fn decrease_pledge(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
info!("Starting to decrease pledge");
|
||||
|
||||
let coin = Coin::new(args.decrease_by, denom);
|
||||
|
||||
let res = client
|
||||
.pledge_more(coin.into(), None)
|
||||
.await
|
||||
.expect("failed to decrease pledge!");
|
||||
|
||||
info!("decreasing pledge: {:?}", res);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::Coin;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub amount: u128,
|
||||
}
|
||||
|
||||
pub async fn increase_pledge(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
info!("Starting to pledge more");
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let res = client
|
||||
.pledge_more(coin.into(), None)
|
||||
.await
|
||||
.expect("failed to pledge more!");
|
||||
|
||||
info!("pledging more: {:?}", res);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod decrease_pledge;
|
||||
pub mod increase_pledge;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
pub struct MixnetOperatorsNymNodePledge {
|
||||
#[clap(subcommand)]
|
||||
pub command: MixnetOperatorsNymNodePledgeCommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsNymNodePledgeCommands {
|
||||
/// Increase current pledge
|
||||
Increase(increase_pledge::Args),
|
||||
/// decrease current pledge
|
||||
Decrease(decrease_pledge::Args),
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {}
|
||||
|
||||
pub async fn claim_operator_reward(_args: Args, client: SigningClient) {
|
||||
info!("Claim operator reward");
|
||||
|
||||
let res = client
|
||||
.withdraw_operator_reward(None)
|
||||
.await
|
||||
.expect("failed to claim operator reward");
|
||||
|
||||
info!("Claiming operator reward: {:?}", res)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod claim_operator_reward;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
pub struct MixnetOperatorsNymNodeRewards {
|
||||
#[clap(subcommand)]
|
||||
pub command: MixnetOperatorsNymNodeRewardsCommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsNymNodeRewardsCommands {
|
||||
/// Claim rewards
|
||||
Claim(claim_operator_reward::Args),
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod update_config;
|
||||
pub mod update_cost_params;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
pub struct MixnetOperatorsNymNodeSettings {
|
||||
#[clap(subcommand)]
|
||||
pub command: MixnetOperatorsNymNodeSettingsCommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsNymNodeSettingsCommands {
|
||||
/// Update Nym Node configuration
|
||||
UpdateConfig(update_config::Args),
|
||||
/// Update Nym Node cost parameters
|
||||
UpdateCostParameters(update_cost_params::Args),
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::nym_node::NodeConfigUpdate;
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub host: Option<String>,
|
||||
|
||||
// ideally this would have been `Option<Option<u16>>`, but not sure if clap would have recognised it
|
||||
#[clap(long)]
|
||||
pub custom_http_port: Option<u16>,
|
||||
|
||||
// equivalent to setting `custom_http_port` to `None`
|
||||
#[clap(long)]
|
||||
pub restore_default_http_port: bool,
|
||||
}
|
||||
|
||||
pub async fn update_config(args: Args, client: SigningClient) {
|
||||
info!("Update nym node config!");
|
||||
|
||||
if client
|
||||
.get_owned_nymnode(&client.address())
|
||||
.await
|
||||
.expect("failed to query the chain for nym node details")
|
||||
.details
|
||||
.is_none()
|
||||
{
|
||||
log::warn!("this operator does not own a nym node to update");
|
||||
return;
|
||||
}
|
||||
|
||||
let update = NodeConfigUpdate {
|
||||
host: args.host,
|
||||
custom_http_port: args.custom_http_port,
|
||||
restore_default_http_port: args.restore_default_http_port,
|
||||
};
|
||||
|
||||
let res = client
|
||||
.update_nymnode_config(update, None)
|
||||
.await
|
||||
.expect("updating nym node config");
|
||||
|
||||
info!("nym node config updated: {:?}", res)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use anyhow::anyhow;
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Uint128;
|
||||
use log::info;
|
||||
use nym_mixnet_contract_common::{NodeCostParams, Percent};
|
||||
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
|
||||
use nym_validator_client::nyxd::CosmWasmCoin;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(
|
||||
long,
|
||||
help = "input your profit margin as follows; (so it would be 20, rather than 0.2)"
|
||||
)]
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub interval_operating_cost: Option<u128>,
|
||||
}
|
||||
|
||||
pub async fn update_cost_params(args: Args, client: SigningClient) -> anyhow::Result<()> {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
let current_parameters = if let Some(client_mixnode) = client
|
||||
.get_owned_mixnode(&client.address())
|
||||
.await?
|
||||
.mixnode_details
|
||||
{
|
||||
client_mixnode.rewarding_details.cost_params
|
||||
} else {
|
||||
client
|
||||
.get_owned_nymnode(&client.address())
|
||||
.await?
|
||||
.details
|
||||
.ok_or_else(|| anyhow!("the client does not own any nodes"))?
|
||||
.rewarding_details
|
||||
.cost_params
|
||||
};
|
||||
|
||||
let profit_margin_percent = args
|
||||
.profit_margin_percent
|
||||
.map(|pm| Percent::from_percentage_value(pm as u64))
|
||||
.unwrap_or(Ok(current_parameters.profit_margin_percent))?;
|
||||
|
||||
let interval_operating_cost = args
|
||||
.interval_operating_cost
|
||||
.map(|oc| CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(oc),
|
||||
})
|
||||
.unwrap_or(current_parameters.interval_operating_cost);
|
||||
|
||||
let cost_params = NodeCostParams {
|
||||
profit_margin_percent,
|
||||
interval_operating_cost,
|
||||
};
|
||||
|
||||
info!("Starting cost params updating using {cost_params:?} !");
|
||||
let res = client
|
||||
.update_cost_params(cost_params, None)
|
||||
.await
|
||||
.expect("failed to update cost params");
|
||||
|
||||
info!("Cost params result: {:?}", res);
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
|
||||
|
||||
use crate::context::SigningClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {}
|
||||
|
||||
pub async fn unbond_nymnode(_args: Args, client: SigningClient) {
|
||||
info!("Starting Nym Node unbonding!");
|
||||
|
||||
let res = client
|
||||
.unbond_nymnode(None)
|
||||
.await
|
||||
.expect("failed to unbond Nym Node!");
|
||||
|
||||
info!("Unbonding result: {:?}", res)
|
||||
}
|
||||
@@ -39,7 +39,7 @@ pub async fn query(args: Args, client: &QueryClientWithNyxd) {
|
||||
node.owner.to_string(),
|
||||
node.gateway.host.to_string(),
|
||||
pretty_cosmwasm_coin(&node.pledge_amount),
|
||||
node.gateway.version,
|
||||
node.gateway.version.clone(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{BankMsg, Coin, CosmosMsg, Response};
|
||||
|
||||
pub trait ResponseExt<T> {
|
||||
fn add_optional_message(self, msg: Option<impl Into<CosmosMsg<T>>>) -> Self;
|
||||
|
||||
fn send_tokens(self, to: impl AsRef<str>, amount: Coin) -> Self;
|
||||
}
|
||||
|
||||
impl<T> ResponseExt<T> for Response<T> {
|
||||
fn add_optional_message(self, msg: Option<impl Into<CosmosMsg<T>>>) -> Self {
|
||||
if let Some(msg) = msg {
|
||||
self.add_message(msg)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn send_tokens(self, to: impl AsRef<str>, amount: Coin) -> Self {
|
||||
self.add_message(BankMsg::Send {
|
||||
to_address: to.as_ref().to_string(),
|
||||
amount: vec![amount],
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,6 @@ pub mod events;
|
||||
pub mod signing;
|
||||
pub mod types;
|
||||
|
||||
pub mod helpers;
|
||||
|
||||
pub use types::*;
|
||||
|
||||
@@ -8,7 +8,7 @@ use cosmwasm_std::Uint128;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::Mul;
|
||||
use std::ops::{Deref, Mul};
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -23,7 +23,7 @@ pub fn truncate_decimal(amount: Decimal) -> Uint128 {
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ContractsCommonError {
|
||||
#[error("Provided percent value ({0}) is greater than 100%")]
|
||||
InvalidPercent(Decimal),
|
||||
InvalidPercent(String),
|
||||
|
||||
#[error("{source}")]
|
||||
StdErr {
|
||||
@@ -41,7 +41,7 @@ pub struct Percent(#[serde(deserialize_with = "de_decimal_percent")] Decimal);
|
||||
impl Percent {
|
||||
pub fn new(value: Decimal) -> Result<Self, ContractsCommonError> {
|
||||
if value > Decimal::one() {
|
||||
Err(ContractsCommonError::InvalidPercent(value))
|
||||
Err(ContractsCommonError::InvalidPercent(value.to_string()))
|
||||
} else {
|
||||
Ok(Percent(value))
|
||||
}
|
||||
@@ -51,11 +51,15 @@ impl Percent {
|
||||
self.0 == Decimal::zero()
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
pub fn is_hundred(&self) -> bool {
|
||||
self == &Self::hundred()
|
||||
}
|
||||
|
||||
pub const fn zero() -> Self {
|
||||
Self(Decimal::zero())
|
||||
}
|
||||
|
||||
pub fn hundred() -> Self {
|
||||
pub const fn hundred() -> Self {
|
||||
Self(Decimal::one())
|
||||
}
|
||||
|
||||
@@ -117,6 +121,70 @@ impl Mul<Uint128> for Percent {
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Percent {
|
||||
type Target = Decimal;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
// this is not implemented via From traits due to its naive nature and loss of precision
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub trait NaiveFloat {
|
||||
fn naive_to_f64(&self) -> f64;
|
||||
|
||||
fn naive_try_from_f64(val: f64) -> Result<Self, ContractsCommonError>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl NaiveFloat for Percent {
|
||||
fn naive_to_f64(&self) -> f64 {
|
||||
use cosmwasm_std::Fraction;
|
||||
|
||||
// note: this conversion loses precision with too many decimal places,
|
||||
// but for the purposes of displaying basic performance, that's not an issue
|
||||
self.numerator().u128() as f64 / self.denominator().u128() as f64
|
||||
}
|
||||
|
||||
fn naive_try_from_f64(val: f64) -> Result<Self, ContractsCommonError>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// we are only interested in positive values between 0 and 1
|
||||
if !(0. ..=1.).contains(&val) {
|
||||
return Err(ContractsCommonError::InvalidPercent(val.to_string()));
|
||||
}
|
||||
|
||||
fn gcd(mut x: u64, mut y: u64) -> u64 {
|
||||
while y > 0 {
|
||||
let rem = x % y;
|
||||
x = y;
|
||||
y = rem;
|
||||
}
|
||||
|
||||
x
|
||||
}
|
||||
|
||||
fn to_rational(x: f64) -> (u64, u64) {
|
||||
let log = x.log2().floor();
|
||||
if log >= 0.0 {
|
||||
(x as u64, 1)
|
||||
} else {
|
||||
let num: u64 = (x / f64::EPSILON) as _;
|
||||
let den: u64 = (1.0 / f64::EPSILON) as _;
|
||||
let gcd = gcd(num, den);
|
||||
(num / gcd, den / gcd)
|
||||
}
|
||||
}
|
||||
|
||||
let (n, d) = to_rational(val);
|
||||
Percent::new(Decimal::from_ratio(n, d))
|
||||
}
|
||||
}
|
||||
|
||||
// implement custom Deserialize because we want to validate Percent has the correct range
|
||||
fn de_decimal_percent<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
|
||||
where
|
||||
@@ -243,4 +311,19 @@ mod tests {
|
||||
let p = serde_json::from_str::<'_, Percent>("\"1.00\"").unwrap();
|
||||
assert_eq!(p.round_to_integer(), 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn naive_float_conversion() {
|
||||
// around 15 decimal places is the maximum precision we can handle
|
||||
// which is still way more than enough for what we use it for
|
||||
let float: f64 = "0.546295475423853".parse().unwrap();
|
||||
let percent: Percent = "0.546295475423853".parse().unwrap();
|
||||
|
||||
assert_eq!(float, percent.naive_to_f64());
|
||||
|
||||
let epsilon = Decimal::from_ratio(1u64, 1000000000000000u64);
|
||||
let converted = Percent::naive_try_from_f64(float).unwrap();
|
||||
|
||||
assert!(converted.0 - converted.0 < epsilon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ repository = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cw-storage-plus.workspace = true
|
||||
cw-controllers = { workspace = true }
|
||||
cw2 = { workspace = true, optional = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -5,6 +5,9 @@ use cosmwasm_std::{Decimal, Uint128};
|
||||
|
||||
pub const TOKEN_SUPPLY: Uint128 = Uint128::new(1_000_000_000_000_000);
|
||||
|
||||
pub const DEFAULT_INTERVAL_OPERATING_COST_AMOUNT: u128 = 40_000_000;
|
||||
pub const DEFAULT_PROFIT_MARGIN_PERCENT: u64 = 20;
|
||||
|
||||
// I'm still not 100% sure how to feel about existence of this file
|
||||
// This is equivalent of representing our display coin with 6 decimal places.
|
||||
// I'm using this one as opposed to "Decimal::one()", as this provides us with higher accuracy
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
use crate::constants::TOKEN_SUPPLY;
|
||||
use crate::helpers::IntoBaseDecimal;
|
||||
use crate::{Addr, MixId};
|
||||
use crate::{Addr, NodeId};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Coin, Decimal, StdResult};
|
||||
|
||||
// just use a string representation of those so that we wouldn't need to bother with decoding bytes
|
||||
// and trying to figure out whether they're valid, etc
|
||||
pub type OwnerProxySubKey = String;
|
||||
pub type StorageKey = (MixId, OwnerProxySubKey);
|
||||
pub type StorageKey = (NodeId, OwnerProxySubKey);
|
||||
|
||||
// throughout the contract we ensure that our proxy can ONLY ever be the vesting contract
|
||||
// thus this method is equivalent to either using the existing address (for when there's no proxy)
|
||||
@@ -40,8 +40,9 @@ pub struct Delegation {
|
||||
/// Address of the owner of this delegation.
|
||||
pub owner: Addr,
|
||||
|
||||
/// Id of the MixNode that this delegation was performed against.
|
||||
pub mix_id: MixId,
|
||||
/// Id of the Node that this delegation was performed against.
|
||||
#[serde(alias = "mix_id")]
|
||||
pub node_id: NodeId,
|
||||
|
||||
// Note to UI/UX devs: there's absolutely no point in displaying this value to the users,
|
||||
// it would serve them no purpose. It's only used for calculating rewards
|
||||
@@ -56,12 +57,13 @@ pub struct Delegation {
|
||||
|
||||
/// Proxy address used to delegate the funds on behalf of another address
|
||||
pub proxy: Option<Addr>,
|
||||
// TODO: perhaps add a field to indicate if it was made against old mixnode with #[serde(default)]?
|
||||
}
|
||||
|
||||
impl Delegation {
|
||||
pub fn new(
|
||||
owner: Addr,
|
||||
mix_id: MixId,
|
||||
node_id: NodeId,
|
||||
cumulative_reward_ratio: Decimal,
|
||||
amount: Coin,
|
||||
height: u64,
|
||||
@@ -73,7 +75,7 @@ impl Delegation {
|
||||
|
||||
Delegation {
|
||||
owner,
|
||||
mix_id,
|
||||
node_id,
|
||||
cumulative_reward_ratio,
|
||||
amount,
|
||||
height,
|
||||
@@ -82,7 +84,7 @@ impl Delegation {
|
||||
}
|
||||
|
||||
pub fn generate_storage_key(
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
owner_address: &Addr,
|
||||
proxy: Option<&Addr>,
|
||||
) -> StorageKey {
|
||||
@@ -92,7 +94,7 @@ impl Delegation {
|
||||
// this function might seem a bit redundant, but I'd rather explicitly keep it around in case
|
||||
// some types change in the future
|
||||
pub fn generate_storage_key_with_subkey(
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
owner_proxy_subkey: OwnerProxySubKey,
|
||||
) -> StorageKey {
|
||||
(mix_id, owner_proxy_subkey)
|
||||
@@ -107,13 +109,13 @@ impl Delegation {
|
||||
}
|
||||
|
||||
pub fn storage_key(&self) -> StorageKey {
|
||||
Self::generate_storage_key(self.mix_id, &self.owner, self.proxy.as_ref())
|
||||
Self::generate_storage_key(self.node_id, &self.owner, self.proxy.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
/// Response containing paged list of all delegations made towards particular mixnode.
|
||||
/// Response containing paged list of all delegations made towards particular node.
|
||||
#[cw_serde]
|
||||
pub struct PagedMixNodeDelegationsResponse {
|
||||
pub struct PagedNodeDelegationsResponse {
|
||||
/// Each individual delegation made.
|
||||
pub delegations: Vec<Delegation>,
|
||||
|
||||
@@ -121,9 +123,9 @@ pub struct PagedMixNodeDelegationsResponse {
|
||||
pub start_next_after: Option<OwnerProxySubKey>,
|
||||
}
|
||||
|
||||
impl PagedMixNodeDelegationsResponse {
|
||||
impl PagedNodeDelegationsResponse {
|
||||
pub fn new(delegations: Vec<Delegation>, start_next_after: Option<OwnerProxySubKey>) -> Self {
|
||||
PagedMixNodeDelegationsResponse {
|
||||
PagedNodeDelegationsResponse {
|
||||
delegations,
|
||||
start_next_after,
|
||||
}
|
||||
@@ -137,13 +139,13 @@ pub struct PagedDelegatorDelegationsResponse {
|
||||
pub delegations: Vec<Delegation>,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<(MixId, OwnerProxySubKey)>,
|
||||
pub start_next_after: Option<(NodeId, OwnerProxySubKey)>,
|
||||
}
|
||||
|
||||
impl PagedDelegatorDelegationsResponse {
|
||||
pub fn new(
|
||||
delegations: Vec<Delegation>,
|
||||
start_next_after: Option<(MixId, OwnerProxySubKey)>,
|
||||
start_next_after: Option<(NodeId, OwnerProxySubKey)>,
|
||||
) -> Self {
|
||||
PagedDelegatorDelegationsResponse {
|
||||
delegations,
|
||||
@@ -154,19 +156,24 @@ impl PagedDelegatorDelegationsResponse {
|
||||
|
||||
/// Response containing delegation details.
|
||||
#[cw_serde]
|
||||
pub struct MixNodeDelegationResponse {
|
||||
pub struct NodeDelegationResponse {
|
||||
/// If the delegation exists, this field contains its detailed information.
|
||||
pub delegation: Option<Delegation>,
|
||||
|
||||
/// Flag indicating whether the node towards which the delegation was made is still bonded in the network.
|
||||
#[deprecated(note = "this field will be removed. use .node_still_bonded instead")]
|
||||
pub mixnode_still_bonded: bool,
|
||||
|
||||
pub node_still_bonded: bool,
|
||||
}
|
||||
|
||||
impl MixNodeDelegationResponse {
|
||||
pub fn new(delegation: Option<Delegation>, mixnode_still_bonded: bool) -> Self {
|
||||
MixNodeDelegationResponse {
|
||||
impl NodeDelegationResponse {
|
||||
pub fn new(delegation: Option<Delegation>, node_still_bonded: bool) -> Self {
|
||||
#[allow(deprecated)]
|
||||
NodeDelegationResponse {
|
||||
delegation,
|
||||
mixnode_still_bonded,
|
||||
mixnode_still_bonded: node_still_bonded,
|
||||
node_still_bonded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{EpochEventId, EpochState, IdentityKey, MixId, OperatingCostRange, ProfitMarginRange};
|
||||
use crate::nym_node::Role;
|
||||
use crate::{
|
||||
EpochEventId, EpochState, IntervalEventId, NodeId, OperatingCostRange, ProfitMarginRange,
|
||||
};
|
||||
use contracts_common::signing::verifier::ApiVerifierError;
|
||||
use contracts_common::Percent;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
|
||||
@@ -34,6 +37,16 @@ pub enum MixnetContractError {
|
||||
#[error("Not enough funds sent for node pledge. (received {received}, minimum {minimum})")]
|
||||
InsufficientPledge { received: Coin, minimum: Coin },
|
||||
|
||||
#[error(
|
||||
"the provided value for node host is too long. it must not be longer than 255 characters"
|
||||
)]
|
||||
HostTooLong,
|
||||
|
||||
#[error(
|
||||
"the provided node identity public key is not a correctly encoded base58 slice of 32 bytes"
|
||||
)]
|
||||
InvalidPubKey,
|
||||
|
||||
#[error("Attempted to reduce node pledge ({current}{denom} - {decrease_by}{denom}) below the minimum amount: {minimum}{denom}")]
|
||||
InvalidPledgeReduction {
|
||||
current: Uint128,
|
||||
@@ -45,11 +58,19 @@ pub enum MixnetContractError {
|
||||
#[error("A pledge change is already pending in this epoch. The event id: {pending_event_id}")]
|
||||
PendingPledgeChange { pending_event_id: EpochEventId },
|
||||
|
||||
#[error(
|
||||
"A cost params change is already pending in this epoch. The event id: {pending_event_id}"
|
||||
)]
|
||||
PendingParamsChange { pending_event_id: IntervalEventId },
|
||||
|
||||
#[error("Not enough funds sent for node delegation. (received {received}, minimum {minimum})")]
|
||||
InsufficientDelegation { received: Coin, minimum: Coin },
|
||||
|
||||
#[error("Node ({node_id}) does not exist")]
|
||||
NymNodeBondNotFound { node_id: NodeId },
|
||||
|
||||
#[error("Mixnode ({mix_id}) does not exist")]
|
||||
MixNodeBondNotFound { mix_id: MixId },
|
||||
MixNodeBondNotFound { mix_id: NodeId },
|
||||
|
||||
#[error("{owner} does not seem to own any mixnodes")]
|
||||
NoAssociatedMixNodeBond { owner: Addr },
|
||||
@@ -57,12 +78,18 @@ pub enum MixnetContractError {
|
||||
#[error("{owner} does not seem to own any gateways")]
|
||||
NoAssociatedGatewayBond { owner: Addr },
|
||||
|
||||
#[error("{owner} does not seem to own any nodes")]
|
||||
NoAssociatedNodeBond { owner: Addr },
|
||||
|
||||
#[error("This address has already bonded a mixnode")]
|
||||
AlreadyOwnsMixnode,
|
||||
|
||||
#[error("This address has already bonded a gateway")]
|
||||
AlreadyOwnsGateway,
|
||||
|
||||
#[error("This address has already bonded a nym-node")]
|
||||
AlreadyOwnsNymNode,
|
||||
|
||||
#[error("Gateway with this identity already exists. Its owner is {owner}")]
|
||||
DuplicateGateway { owner: Addr },
|
||||
|
||||
@@ -103,32 +130,35 @@ pub enum MixnetContractError {
|
||||
epoch_end: i64,
|
||||
},
|
||||
|
||||
#[error("Mixnode {mix_id} has already been rewarded during the current rewarding epoch ({absolute_epoch_id})")]
|
||||
MixnodeAlreadyRewarded {
|
||||
mix_id: MixId,
|
||||
#[error("attempted to reward a gateway node - this has not been fully integrated yet")]
|
||||
GatewayRewarding,
|
||||
|
||||
#[error("node {node_id} has already been rewarded during the current rewarding epoch ({absolute_epoch_id})")]
|
||||
NodeAlreadyRewarded {
|
||||
node_id: NodeId,
|
||||
absolute_epoch_id: u32,
|
||||
},
|
||||
|
||||
#[error("Mixnode {mix_id} hasn't been selected to the rewarding set in this epoch ({absolute_epoch_id})")]
|
||||
MixnodeNotInRewardedSet {
|
||||
mix_id: MixId,
|
||||
absolute_epoch_id: u32,
|
||||
},
|
||||
#[error("node {node_id} hasn't been assigned the role of {role} for this epoch")]
|
||||
IncorrectEpochRole { node_id: NodeId, role: Role },
|
||||
|
||||
#[error("Mixnode {mix_id} is currently in the process of unbonding")]
|
||||
MixnodeIsUnbonding { mix_id: MixId },
|
||||
MixnodeIsUnbonding { mix_id: NodeId },
|
||||
|
||||
#[error("Node {node_id} is currently in the process of unbonding")]
|
||||
NodeIsUnbonding { node_id: NodeId },
|
||||
|
||||
#[error("Mixnode {mix_id} has already unbonded")]
|
||||
MixnodeHasUnbonded { mix_id: MixId },
|
||||
MixnodeHasUnbonded { mix_id: NodeId },
|
||||
|
||||
#[error("The contract has ended up in a state that was deemed impossible: {comment}")]
|
||||
InconsistentState { comment: String },
|
||||
|
||||
#[error(
|
||||
"Could not find any delegation information associated with mixnode {mix_id} for {address} (proxy: {proxy:?})"
|
||||
"Could not find any delegation information associated with node {node_id} for {address} (proxy: {proxy:?})"
|
||||
)]
|
||||
NoMixnodeDelegationFound {
|
||||
mix_id: MixId,
|
||||
NodeDelegationNotFound {
|
||||
node_id: NodeId,
|
||||
address: String,
|
||||
proxy: Option<String>,
|
||||
},
|
||||
@@ -136,63 +166,18 @@ pub enum MixnetContractError {
|
||||
#[error("Provided message to update rewarding params did not contain any updates")]
|
||||
EmptyParamsChangeMsg,
|
||||
|
||||
#[error("Provided active set size is bigger than the rewarded set")]
|
||||
#[error("one of the roles in the new active set is empty")]
|
||||
EmptyRoleAssignment,
|
||||
|
||||
#[error("the number of mixnodes in the rewarded set is not divisible by the number of mix-layers (3)")]
|
||||
UnevenLayerAssignment,
|
||||
|
||||
#[error("provided active set is bigger than the rewarded set")]
|
||||
InvalidActiveSetSize,
|
||||
|
||||
#[error("Provided rewarded set size is smaller than the active set")]
|
||||
InvalidRewardedSetSize,
|
||||
|
||||
#[error("Provided active set size is zero")]
|
||||
ZeroActiveSet,
|
||||
|
||||
#[error("Provided rewarded set size is zero")]
|
||||
ZeroRewardedSet,
|
||||
|
||||
#[error("Received unexpected value for the active set. Got: {received}, expected: {expected}")]
|
||||
UnexpectedActiveSetSize { received: u32, expected: u32 },
|
||||
|
||||
#[error("Received unexpected value for the rewarded set. Got: {received}, expected at most: {expected}")]
|
||||
UnexpectedRewardedSetSize { received: u32, expected: u32 },
|
||||
|
||||
#[error("Mixnode {mix_id} appears multiple times in the provided rewarded set update!")]
|
||||
DuplicateRewardedSetNode { mix_id: MixId },
|
||||
|
||||
#[error("Family with head {head} does not exist!")]
|
||||
FamilyDoesNotExist { head: String },
|
||||
|
||||
#[error("Family with label {label} does not exist!")]
|
||||
FamilyLabelDoesNotExist { label: String },
|
||||
|
||||
#[error("Family with label '{0}' already exists")]
|
||||
FamilyWithLabelExists(String),
|
||||
|
||||
#[error("Invalid layer expected 1, 2 or 3, got {0}")]
|
||||
InvalidLayer(u8),
|
||||
|
||||
#[error("Head already has a family")]
|
||||
FamilyCanHaveOnlyOne,
|
||||
|
||||
#[error("Already member of family {0}")]
|
||||
AlreadyMemberOfFamily(String),
|
||||
|
||||
#[error("Can't join own family, family head {head}, member {member}")]
|
||||
CantJoinOwnFamily {
|
||||
head: IdentityKey,
|
||||
member: IdentityKey,
|
||||
},
|
||||
|
||||
#[error("Can't leave own family, family head {head}, member {member}")]
|
||||
CantLeaveOwnFamily {
|
||||
head: IdentityKey,
|
||||
member: IdentityKey,
|
||||
},
|
||||
|
||||
#[error("{member} is not a member of family {head}")]
|
||||
NotAMember {
|
||||
head: IdentityKey,
|
||||
member: IdentityKey,
|
||||
},
|
||||
|
||||
#[error("Feature is not yet implemented")]
|
||||
NotImplemented,
|
||||
|
||||
@@ -219,13 +204,28 @@ pub enum MixnetContractError {
|
||||
|
||||
#[error("attempted to reward mixnode out of order. Attempted to reward {attempted_to_reward} while last rewarded was {last_rewarded}.")]
|
||||
RewardingOutOfOrder {
|
||||
last_rewarded: MixId,
|
||||
attempted_to_reward: MixId,
|
||||
last_rewarded: NodeId,
|
||||
attempted_to_reward: NodeId,
|
||||
},
|
||||
|
||||
#[error("the epoch is currently not in the 'event reconciliation' state. (the state is {current_state})")]
|
||||
EpochNotInEventReconciliationState { current_state: EpochState },
|
||||
|
||||
#[error(
|
||||
"the epoch is currently not in the 'role assignment' state. (the state is {current_state})"
|
||||
)]
|
||||
EpochNotInRoleAssignmentState { current_state: EpochState },
|
||||
|
||||
#[error("unexpected role assignment. got: {got} while expected: {expected}")]
|
||||
UnexpectedRoleAssignment { expected: Role, got: Role },
|
||||
|
||||
#[error("attempted to assign an invalid number of nodes for a role of {role}. got {assigned}, but the maximum allowed is {allowed}")]
|
||||
IllegalRoleCount {
|
||||
role: Role,
|
||||
assigned: u32,
|
||||
allowed: u32,
|
||||
},
|
||||
|
||||
#[error("the epoch is currently not in the 'epoch advancement' state. (the state is {current_state})")]
|
||||
EpochNotInAdvancementState { current_state: EpochState },
|
||||
|
||||
@@ -258,6 +258,17 @@ pub enum MixnetContractError {
|
||||
provided: Uint128,
|
||||
range: OperatingCostRange,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"currently it's not possible to migrate nodes bonded with vesting tokens into a nym-node. please perform vesting->liquid migration first."
|
||||
)]
|
||||
VestingNodeMigration,
|
||||
|
||||
#[error("value {got} does not correspond to any known node role")]
|
||||
UnknownRoleRepresentation { got: u8 },
|
||||
|
||||
#[error("the total work for this epoch seems to be bigger than 1.0!")]
|
||||
TotalWorkAboveOne,
|
||||
}
|
||||
|
||||
impl MixnetContractError {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::gateway::GatewayConfigUpdate;
|
||||
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use crate::reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate};
|
||||
use crate::mixnode::{MixNodeConfigUpdate, NodeCostParams};
|
||||
use crate::nym_node::Role;
|
||||
use crate::reward_params::{ActiveSetUpdate, IntervalRewardParams, IntervalRewardingParamsUpdate};
|
||||
use crate::rewarding::RewardDistribution;
|
||||
use crate::{BlockHeight, ContractStateParams, IdentityKeyRef, Interval, Layer, MixId};
|
||||
use crate::{BlockHeight, ContractStateParams, EpochId, IdentityKeyRef, Interval, NodeId};
|
||||
pub use contracts_common::events::*;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, Event};
|
||||
use std::fmt::Display;
|
||||
@@ -14,6 +16,11 @@ pub const EVENT_VERSION_PREFIX: &str = "v2_";
|
||||
|
||||
pub enum MixnetEventType {
|
||||
MixnodeBonding,
|
||||
NymNodeBonding,
|
||||
NymNodeUnbonding,
|
||||
PendingNymNodeUnbonding,
|
||||
GatewayMigration,
|
||||
MixnodeMigration,
|
||||
PendingPledgeIncrease,
|
||||
PledgeIncrease,
|
||||
PendingPledgeDecrease,
|
||||
@@ -23,9 +30,9 @@ pub enum MixnetEventType {
|
||||
PendingMixnodeUnbonding,
|
||||
MixnodeUnbonding,
|
||||
MixnodeConfigUpdate,
|
||||
PendingMixnodeCostParamsUpdate,
|
||||
MixnodeCostParamsUpdate,
|
||||
MixnodeRewarding,
|
||||
PendingCostParamsUpdate,
|
||||
CostParamsUpdate,
|
||||
NodeRewarding,
|
||||
WithdrawDelegatorReward,
|
||||
WithdrawOperatorReward,
|
||||
PendingActiveSetUpdate,
|
||||
@@ -41,6 +48,7 @@ pub enum MixnetEventType {
|
||||
RewardingValidatorUpdate,
|
||||
BeginEpochTransition,
|
||||
AdvanceEpoch,
|
||||
RoleAssignment,
|
||||
ExecutePendingEpochEvents,
|
||||
ExecutePendingIntervalEvents,
|
||||
ReconcilePendingEvents,
|
||||
@@ -59,6 +67,11 @@ impl Display for MixnetEventType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let event_name = match self {
|
||||
MixnetEventType::MixnodeBonding => "mixnode_bonding",
|
||||
MixnetEventType::NymNodeBonding => "nymnode_bonding",
|
||||
MixnetEventType::NymNodeUnbonding => "nymnode_unbonding",
|
||||
MixnetEventType::PendingNymNodeUnbonding => "pending_nymnode_unbonding",
|
||||
MixnetEventType::GatewayMigration => "gateway_migration",
|
||||
MixnetEventType::MixnodeMigration => "mixnode_migration",
|
||||
MixnetEventType::PendingPledgeIncrease => "pending_pledge_increase",
|
||||
MixnetEventType::PledgeIncrease => "pledge_increase",
|
||||
MixnetEventType::PendingPledgeDecrease => "pending_pledge_decrease",
|
||||
@@ -68,9 +81,9 @@ impl Display for MixnetEventType {
|
||||
MixnetEventType::PendingMixnodeUnbonding => "pending_mixnode_unbonding",
|
||||
MixnetEventType::MixnodeConfigUpdate => "mixnode_config_update",
|
||||
MixnetEventType::MixnodeUnbonding => "mixnode_unbonding",
|
||||
MixnetEventType::PendingMixnodeCostParamsUpdate => "pending_mixnode_cost_params_update",
|
||||
MixnetEventType::MixnodeCostParamsUpdate => "mixnode_cost_params_update",
|
||||
MixnetEventType::MixnodeRewarding => "mix_rewarding",
|
||||
MixnetEventType::PendingCostParamsUpdate => "pending_cost_params_update",
|
||||
MixnetEventType::CostParamsUpdate => "cost_params_update",
|
||||
MixnetEventType::NodeRewarding => "node_rewarding",
|
||||
MixnetEventType::WithdrawDelegatorReward => "withdraw_delegator_reward",
|
||||
MixnetEventType::WithdrawOperatorReward => "withdraw_operator_reward",
|
||||
MixnetEventType::PendingActiveSetUpdate => "pending_active_set_update",
|
||||
@@ -87,6 +100,7 @@ impl Display for MixnetEventType {
|
||||
MixnetEventType::RewardingValidatorUpdate => "rewarding_validator_address_update",
|
||||
MixnetEventType::BeginEpochTransition => "beginning_epoch_transition",
|
||||
MixnetEventType::AdvanceEpoch => "advance_epoch",
|
||||
MixnetEventType::RoleAssignment => "role_assignment",
|
||||
MixnetEventType::ExecutePendingEpochEvents => "execute_pending_epoch_events",
|
||||
MixnetEventType::ExecutePendingIntervalEvents => "execute_pending_interval_events",
|
||||
MixnetEventType::ReconcilePendingEvents => "reconcile_pending_events",
|
||||
@@ -103,6 +117,7 @@ impl Display for MixnetEventType {
|
||||
// attributes that are used in multiple places
|
||||
pub const OWNER_KEY: &str = "owner";
|
||||
pub const AMOUNT_KEY: &str = "amount";
|
||||
pub const ERROR_MESSAGE_KEY: &str = "error_message";
|
||||
|
||||
// event-specific attributes
|
||||
|
||||
@@ -113,16 +128,14 @@ pub const UNIT_REWARD_KEY: &str = "unit_reward";
|
||||
|
||||
// bonding/unbonding
|
||||
pub const MIX_ID_KEY: &str = "mix_id";
|
||||
pub const NODE_ID_KEY: &str = "node_id";
|
||||
pub const NODE_IDENTITY_KEY: &str = "identity";
|
||||
pub const ASSIGNED_LAYER_KEY: &str = "assigned_layer";
|
||||
|
||||
// settings change
|
||||
pub const OLD_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "old_minimum_mixnode_pledge";
|
||||
pub const OLD_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "old_minimum_gateway_pledge";
|
||||
pub const OLD_MINIMUM_PLEDGE_KEY: &str = "old_minimum_pledge";
|
||||
pub const OLD_MINIMUM_DELEGATION_KEY: &str = "old_minimum_delegation";
|
||||
|
||||
pub const NEW_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "new_minimum_mixnode_pledge";
|
||||
pub const NEW_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "new_minimum_gateway_pledge";
|
||||
pub const NEW_MINIMUM_PLEDGE_KEY: &str = "new_minimum_pledge";
|
||||
pub const NEW_MINIMUM_DELEGATION_KEY: &str = "new_minimum_delegation";
|
||||
|
||||
pub const OLD_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "old_rewarding_validator_address";
|
||||
@@ -144,14 +157,19 @@ pub const PRIOR_UNIT_REWARD_KEY: &str = "prior_unit_reward";
|
||||
|
||||
pub const NO_REWARD_REASON_KEY: &str = "no_reward_reason";
|
||||
pub const BOND_NOT_FOUND_VALUE: &str = "bond_not_found";
|
||||
pub const ZERO_PERFORMANCE_VALUE: &str = "zero_performance";
|
||||
pub const ZERO_PERFORMANCE_OR_WORK_VALUE: &str = "zero_performance_or_work";
|
||||
|
||||
// rewarded set update
|
||||
pub const ACTIVE_SET_SIZE_KEY: &str = "active_set_size";
|
||||
pub const NUM_MIXNODES_KEY: &str = "num_mixnodes";
|
||||
pub const NUM_ENTRIES_KEY: &str = "num_entry_gateways";
|
||||
pub const NUM_EXITS_KEY: &str = "num_exit_gateways";
|
||||
|
||||
pub const CURRENT_EPOCH_KEY: &str = "current_epoch";
|
||||
pub const NEW_CURRENT_EPOCH_KEY: &str = "new_current_epoch";
|
||||
|
||||
pub const ROLE_KEY: &str = "role";
|
||||
pub const NODE_COUNT_KEY: &str = "node_count";
|
||||
|
||||
// interval
|
||||
pub const EVENTS_EXECUTED_KEY: &str = "number_of_events_executed";
|
||||
pub const EVENT_CREATION_HEIGHT_KEY: &str = "created_at";
|
||||
@@ -163,7 +181,7 @@ pub fn new_delegation_event(
|
||||
created_at: BlockHeight,
|
||||
delegator: &Addr,
|
||||
amount: &Coin,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
unit_reward: Decimal,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::Delegation)
|
||||
@@ -174,45 +192,57 @@ pub fn new_delegation_event(
|
||||
.add_attribute(UNIT_REWARD_KEY, unit_reward.to_string())
|
||||
}
|
||||
|
||||
pub fn new_delegation_on_unbonded_node_event(delegator: &Addr, mix_id: MixId) -> Event {
|
||||
pub fn new_delegation_on_unbonded_node_event(delegator: &Addr, mix_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::Delegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_delegation_event(delegator: &Addr, amount: &Coin, mix_id: MixId) -> Event {
|
||||
pub fn new_pending_delegation_event(delegator: &Addr, amount: &Coin, mix_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::PendingDelegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_withdraw_operator_reward_event(owner: &Addr, amount: Coin, mix_id: MixId) -> Event {
|
||||
pub fn new_withdraw_operator_reward_event(owner: &Addr, amount: Coin, mix_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::WithdrawOperatorReward)
|
||||
.add_attribute(OWNER_KEY, owner.as_str())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_withdraw_delegator_reward_event(delegator: &Addr, amount: Coin, mix_id: MixId) -> Event {
|
||||
pub fn new_withdraw_delegator_reward_event(
|
||||
delegator: &Addr,
|
||||
amount: Coin,
|
||||
mix_id: NodeId,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::WithdrawDelegatorReward)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_active_set_update_event(created_at: BlockHeight, new_size: u32) -> Event {
|
||||
pub fn new_active_set_update_failure(err: MixnetContractError) -> Event {
|
||||
Event::new(MixnetEventType::ActiveSetUpdate).add_attribute(ERROR_MESSAGE_KEY, err.to_string())
|
||||
}
|
||||
|
||||
pub fn new_active_set_update_event(created_at: BlockHeight, update: ActiveSetUpdate) -> Event {
|
||||
Event::new(MixnetEventType::ActiveSetUpdate)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
.add_attribute(ACTIVE_SET_SIZE_KEY, new_size.to_string())
|
||||
.add_attribute(NUM_MIXNODES_KEY, update.mixnodes.to_string())
|
||||
.add_attribute(NUM_ENTRIES_KEY, update.entry_gateways.to_string())
|
||||
.add_attribute(NUM_EXITS_KEY, update.exit_gateways.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_active_set_update_event(
|
||||
new_size: u32,
|
||||
update: ActiveSetUpdate,
|
||||
approximate_time_remaining_secs: i64,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::PendingActiveSetUpdate)
|
||||
.add_attribute(ACTIVE_SET_SIZE_KEY, new_size.to_string())
|
||||
.add_attribute(NUM_MIXNODES_KEY, update.mixnodes.to_string())
|
||||
.add_attribute(NUM_ENTRIES_KEY, update.entry_gateways.to_string())
|
||||
.add_attribute(NUM_EXITS_KEY, update.exit_gateways.to_string())
|
||||
.add_attribute(
|
||||
APPROXIMATE_TIME_LEFT_SECS_KEY,
|
||||
approximate_time_remaining_secs.to_string(),
|
||||
@@ -221,7 +251,6 @@ pub fn new_pending_active_set_update_event(
|
||||
|
||||
pub fn new_rewarding_params_update_event(
|
||||
created_at: BlockHeight,
|
||||
|
||||
update: IntervalRewardingParamsUpdate,
|
||||
updated: IntervalRewardParams,
|
||||
) -> Event {
|
||||
@@ -252,30 +281,19 @@ pub fn new_pending_rewarding_params_update_event(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_undelegation_event(created_at: BlockHeight, delegator: &Addr, mix_id: MixId) -> Event {
|
||||
pub fn new_undelegation_event(created_at: BlockHeight, delegator: &Addr, mix_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::Undelegation)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_undelegation_event(delegator: &Addr, mix_id: MixId) -> Event {
|
||||
pub fn new_pending_undelegation_event(delegator: &Addr, mix_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::PendingUndelegation)
|
||||
.add_attribute(DELEGATOR_KEY, delegator)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_gateway_bonding_event(
|
||||
owner: &Addr,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::GatewayBonding)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_gateway_unbonding_event(
|
||||
owner: &Addr,
|
||||
amount: &Coin,
|
||||
@@ -287,49 +305,85 @@ pub fn new_gateway_unbonding_event(
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_bonding_event(
|
||||
pub fn new_nym_node_bonding_event(
|
||||
owner: &Addr,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: MixId,
|
||||
assigned_layer: Layer,
|
||||
node_id: NodeId,
|
||||
) -> Event {
|
||||
// coin implements Display trait and we use that implementation here
|
||||
Event::new(MixnetEventType::MixnodeBonding)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
Event::new(MixnetEventType::NymNodeBonding)
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(ASSIGNED_LAYER_KEY, assigned_layer)
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_pledge_increase_event(mix_id: MixId, amount: &Coin) -> Event {
|
||||
pub fn new_nym_node_unbonding_event(created_at: BlockHeight, node_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::NymNodeUnbonding)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_nym_node_unbonding_event(
|
||||
owner: &Addr,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
node_id: NodeId,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::PendingNymNodeUnbonding)
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
}
|
||||
|
||||
pub fn new_migrated_gateway_event(
|
||||
owner: &Addr,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
node_id: NodeId,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::GatewayMigration)
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
}
|
||||
|
||||
pub fn new_migrated_mixnode_event(
|
||||
owner: &Addr,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
node_id: NodeId,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeMigration)
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
}
|
||||
|
||||
pub fn new_pending_pledge_increase_event(node_id: NodeId, amount: &Coin) -> Event {
|
||||
Event::new(MixnetEventType::PendingPledgeIncrease)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pledge_increase_event(created_at: BlockHeight, mix_id: MixId, amount: &Coin) -> Event {
|
||||
pub fn new_pledge_increase_event(created_at: BlockHeight, node_id: NodeId, amount: &Coin) -> Event {
|
||||
Event::new(MixnetEventType::PledgeIncrease)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_pledge_decrease_event(mix_id: MixId, amount: &Coin) -> Event {
|
||||
pub fn new_pending_pledge_decrease_event(node_id: NodeId, amount: &Coin) -> Event {
|
||||
Event::new(MixnetEventType::PendingPledgeDecrease)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pledge_decrease_event(created_at: BlockHeight, mix_id: MixId, amount: &Coin) -> Event {
|
||||
pub fn new_pledge_decrease_event(created_at: BlockHeight, node_id: NodeId, amount: &Coin) -> Event {
|
||||
Event::new(MixnetEventType::PledgeDecrease)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_unbonding_event(created_at: BlockHeight, mix_id: MixId) -> Event {
|
||||
pub fn new_mixnode_unbonding_event(created_at: BlockHeight, mix_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeUnbonding)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
@@ -338,7 +392,7 @@ pub fn new_mixnode_unbonding_event(created_at: BlockHeight, mix_id: MixId) -> Ev
|
||||
pub fn new_pending_mixnode_unbonding_event(
|
||||
owner: &Addr,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::PendingMixnodeUnbonding)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
@@ -347,7 +401,7 @@ pub fn new_pending_mixnode_unbonding_event(
|
||||
}
|
||||
|
||||
pub fn new_mixnode_config_update_event(
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
owner: &Addr,
|
||||
update: &MixNodeConfigUpdate,
|
||||
) -> Event {
|
||||
@@ -363,23 +417,18 @@ pub fn new_gateway_config_update_event(owner: &Addr, update: &GatewayConfigUpdat
|
||||
.add_attribute(UPDATED_GATEWAY_CONFIG_KEY, update.to_inline_json())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_pending_cost_params_update_event(
|
||||
mix_id: MixId,
|
||||
owner: &Addr,
|
||||
new_costs: &MixNodeCostParams,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::PendingMixnodeCostParamsUpdate)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
pub fn new_pending_cost_params_update_event(mix_id: NodeId, new_costs: &NodeCostParams) -> Event {
|
||||
Event::new(MixnetEventType::PendingCostParamsUpdate)
|
||||
.add_attribute(NODE_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(UPDATED_MIXNODE_COST_PARAMS_KEY, new_costs.to_inline_json())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_cost_params_update_event(
|
||||
pub fn new_cost_params_update_event(
|
||||
created_at: BlockHeight,
|
||||
mix_id: MixId,
|
||||
new_costs: &MixNodeCostParams,
|
||||
mix_id: NodeId,
|
||||
new_costs: &NodeCostParams,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeCostParamsUpdate)
|
||||
Event::new(MixnetEventType::CostParamsUpdate)
|
||||
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(UPDATED_MIXNODE_COST_PARAMS_KEY, new_costs.to_inline_json())
|
||||
@@ -397,37 +446,25 @@ pub fn new_settings_update_event(
|
||||
) -> Event {
|
||||
let mut event = Event::new(MixnetEventType::ContractSettingsUpdate);
|
||||
|
||||
if old_params.minimum_mixnode_pledge != new_params.minimum_mixnode_pledge {
|
||||
if old_params.minimum_pledge != new_params.minimum_pledge {
|
||||
event = event
|
||||
.add_attribute(
|
||||
OLD_MINIMUM_MIXNODE_PLEDGE_KEY,
|
||||
old_params.minimum_mixnode_pledge.to_string(),
|
||||
OLD_MINIMUM_PLEDGE_KEY,
|
||||
old_params.minimum_pledge.to_string(),
|
||||
)
|
||||
.add_attribute(
|
||||
NEW_MINIMUM_MIXNODE_PLEDGE_KEY,
|
||||
new_params.minimum_mixnode_pledge.to_string(),
|
||||
NEW_MINIMUM_PLEDGE_KEY,
|
||||
new_params.minimum_pledge.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
if old_params.minimum_gateway_pledge != new_params.minimum_gateway_pledge {
|
||||
event = event
|
||||
.add_attribute(
|
||||
OLD_MINIMUM_GATEWAY_PLEDGE_KEY,
|
||||
old_params.minimum_gateway_pledge.to_string(),
|
||||
)
|
||||
.add_attribute(
|
||||
NEW_MINIMUM_GATEWAY_PLEDGE_KEY,
|
||||
new_params.minimum_gateway_pledge.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
if old_params.minimum_mixnode_delegation != new_params.minimum_mixnode_delegation {
|
||||
if let Some(ref old) = old_params.minimum_mixnode_delegation {
|
||||
if old_params.minimum_delegation != new_params.minimum_delegation {
|
||||
if let Some(ref old) = old_params.minimum_delegation {
|
||||
event = event.add_attribute(OLD_MINIMUM_DELEGATION_KEY, old.to_string())
|
||||
} else {
|
||||
event = event.add_attribute(OLD_MINIMUM_DELEGATION_KEY, "None")
|
||||
}
|
||||
if let Some(ref new) = new_params.minimum_mixnode_delegation {
|
||||
if let Some(ref new) = new_params.minimum_delegation {
|
||||
event = event.add_attribute(NEW_MINIMUM_DELEGATION_KEY, new.to_string())
|
||||
} else {
|
||||
event = event.add_attribute(NEW_MINIMUM_DELEGATION_KEY, "None")
|
||||
@@ -437,41 +474,41 @@ pub fn new_settings_update_event(
|
||||
event
|
||||
}
|
||||
|
||||
pub fn new_not_found_mix_operator_rewarding_event(interval: Interval, mix_id: MixId) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeRewarding)
|
||||
pub fn new_not_found_node_operator_rewarding_event(interval: Interval, node_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::NodeRewarding)
|
||||
.add_attribute(
|
||||
INTERVAL_KEY,
|
||||
interval.current_epoch_absolute_id().to_string(),
|
||||
)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(NO_REWARD_REASON_KEY, BOND_NOT_FOUND_VALUE)
|
||||
}
|
||||
|
||||
pub fn new_zero_uptime_mix_operator_rewarding_event(interval: Interval, mix_id: MixId) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeRewarding)
|
||||
pub fn new_zero_uptime_mix_operator_rewarding_event(interval: Interval, node_id: NodeId) -> Event {
|
||||
Event::new(MixnetEventType::NodeRewarding)
|
||||
.add_attribute(
|
||||
INTERVAL_KEY,
|
||||
interval.current_epoch_absolute_id().to_string(),
|
||||
)
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NO_REWARD_REASON_KEY, ZERO_PERFORMANCE_VALUE)
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(NO_REWARD_REASON_KEY, ZERO_PERFORMANCE_OR_WORK_VALUE)
|
||||
}
|
||||
|
||||
pub fn new_mix_rewarding_event(
|
||||
interval: Interval,
|
||||
mix_id: MixId,
|
||||
node_id: NodeId,
|
||||
reward_distribution: RewardDistribution,
|
||||
prior_delegates: Decimal,
|
||||
prior_unit_reward: Decimal,
|
||||
) -> Event {
|
||||
Event::new(MixnetEventType::MixnodeRewarding)
|
||||
Event::new(MixnetEventType::NodeRewarding)
|
||||
.add_attribute(
|
||||
INTERVAL_KEY,
|
||||
interval.current_epoch_absolute_id().to_string(),
|
||||
)
|
||||
.add_attribute(PRIOR_DELEGATES_KEY, prior_delegates.to_string())
|
||||
.add_attribute(PRIOR_UNIT_REWARD_KEY, prior_unit_reward.to_string())
|
||||
.add_attribute(MIX_ID_KEY, mix_id.to_string())
|
||||
.add_attribute(NODE_ID_KEY, node_id.to_string())
|
||||
.add_attribute(
|
||||
OPERATOR_REWARD_KEY,
|
||||
reward_distribution.operator.to_string(),
|
||||
@@ -489,13 +526,15 @@ pub fn new_epoch_transition_start_event(current_interval: Interval) -> Event {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_advance_epoch_event(interval: Interval, rewarded_nodes: u32) -> Event {
|
||||
pub fn new_assigned_role_event(role: Role, nodes: u32) -> Event {
|
||||
Event::new(MixnetEventType::RoleAssignment)
|
||||
.add_attribute(ROLE_KEY, role.to_string())
|
||||
.add_attribute(NODE_COUNT_KEY, nodes.to_string())
|
||||
}
|
||||
|
||||
pub fn new_advance_epoch_event(epoch_id: EpochId) -> Event {
|
||||
Event::new(MixnetEventType::AdvanceEpoch)
|
||||
.add_attribute(
|
||||
NEW_CURRENT_EPOCH_KEY,
|
||||
interval.current_epoch_absolute_id().to_string(),
|
||||
)
|
||||
.add_attribute(REWARDED_SET_NODES_KEY, rewarded_nodes.to_string())
|
||||
.add_attribute(NEW_CURRENT_EPOCH_KEY, epoch_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_pending_epoch_events_execution_event(executed: u32) -> Event {
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{IdentityKey, IdentityKeyRef};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// A group of mixnodes associated with particular staking entity.
|
||||
/// When defined all nodes belonging to the same family will be prioritised to be put onto the same layer.
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/NodeFamily.ts")
|
||||
)]
|
||||
#[cw_serde]
|
||||
pub struct Family {
|
||||
/// Owner of this family.
|
||||
head: FamilyHead,
|
||||
|
||||
/// Optional proxy (i.e. vesting contract address) used when creating the family.
|
||||
proxy: Option<String>,
|
||||
|
||||
/// Human readable label for this family.
|
||||
label: String,
|
||||
}
|
||||
|
||||
/// Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/NodeFamilyHead.ts")
|
||||
)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, JsonSchema)]
|
||||
pub struct FamilyHead(IdentityKey);
|
||||
|
||||
impl Serialize for FamilyHead {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for FamilyHead {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let inner = IdentityKey::deserialize(deserializer)?;
|
||||
Ok(FamilyHead(inner))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for FamilyHead {
|
||||
type Err = <IdentityKey as FromStr>::Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// theoretically we should be verifying whether it's a valid base58 value
|
||||
// (or even better, whether it's a valid ed25519 public key), but definition of
|
||||
// `FamilyHead` might change later
|
||||
Ok(FamilyHead(IdentityKey::from_str(s)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FamilyHead {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl FamilyHead {
|
||||
pub fn new<S: Into<String>>(identity: S) -> Self {
|
||||
FamilyHead(identity.into())
|
||||
}
|
||||
|
||||
pub fn identity(&self) -> IdentityKeyRef<'_> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Family {
|
||||
pub fn new(head: FamilyHead, label: String) -> Self {
|
||||
Family {
|
||||
head,
|
||||
proxy: None,
|
||||
label,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn head(&self) -> &FamilyHead {
|
||||
&self.head
|
||||
}
|
||||
|
||||
pub fn head_identity(&self) -> IdentityKeyRef<'_> {
|
||||
self.head.identity()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn proxy(&self) -> Option<&String> {
|
||||
self.proxy.as_ref()
|
||||
}
|
||||
|
||||
pub fn label(&self) -> &str {
|
||||
&self.label
|
||||
}
|
||||
}
|
||||
|
||||
/// Response containing paged list of all families registered in the contract.
|
||||
#[cw_serde]
|
||||
pub struct PagedFamiliesResponse {
|
||||
/// The families registered in the contract.
|
||||
pub families: Vec<Family>,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<String>,
|
||||
}
|
||||
|
||||
/// Response containing paged list of all family members (of ALL families) registered in the contract.
|
||||
#[cw_serde]
|
||||
pub struct PagedMembersResponse {
|
||||
/// The members alongside their family heads.
|
||||
pub members: Vec<(IdentityKey, FamilyHead)>,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<String>,
|
||||
}
|
||||
|
||||
/// Response containing family information.
|
||||
#[cw_serde]
|
||||
pub struct FamilyByHeadResponse {
|
||||
/// The family head used for the query.
|
||||
pub head: FamilyHead,
|
||||
|
||||
/// If applicable, the family associated with the provided head.
|
||||
pub family: Option<Family>,
|
||||
}
|
||||
|
||||
/// Response containing family information.
|
||||
#[cw_serde]
|
||||
pub struct FamilyByLabelResponse {
|
||||
/// The family label used for the query.
|
||||
pub label: String,
|
||||
|
||||
/// If applicable, the family associated with the provided label.
|
||||
pub family: Option<Family>,
|
||||
}
|
||||
|
||||
/// Response containing family members information.
|
||||
#[cw_serde]
|
||||
pub struct FamilyMembersByHeadResponse {
|
||||
/// The family head used for the query.
|
||||
pub head: FamilyHead,
|
||||
|
||||
/// All members belonging to the specified family.
|
||||
pub members: Vec<IdentityKey>,
|
||||
}
|
||||
|
||||
/// Response containing family members information.
|
||||
#[cw_serde]
|
||||
pub struct FamilyMembersByLabelResponse {
|
||||
/// The family label used for the query.
|
||||
pub label: String,
|
||||
|
||||
/// All members belonging to the specified family.
|
||||
pub members: Vec<IdentityKey>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn family_head_serde() {
|
||||
let dummy = FamilyHead::new("foomp");
|
||||
|
||||
let ser_str = serde_json_wasm::to_string(&dummy).unwrap();
|
||||
let de_str: FamilyHead = serde_json_wasm::from_str(&ser_str).unwrap();
|
||||
assert_eq!(dummy, de_str);
|
||||
|
||||
let ser_bytes = serde_json_wasm::to_vec(&dummy).unwrap();
|
||||
let de_bytes: FamilyHead = serde_json_wasm::from_slice(&ser_bytes).unwrap();
|
||||
assert_eq!(dummy, de_bytes);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{IdentityKey, SphinxKey};
|
||||
use crate::{IdentityKey, NodeId, SphinxKey};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
use std::cmp::Ordering;
|
||||
@@ -135,7 +135,10 @@ impl Display for GatewayBond {
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/GatewayConfigUpdate.ts")
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/GatewayConfigUpdate.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
pub struct GatewayConfigUpdate {
|
||||
@@ -200,6 +203,23 @@ pub struct GatewayBondResponse {
|
||||
pub gateway: Option<GatewayBond>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct PreassignedId {
|
||||
/// The identity key (base58-encoded ed25519 public key) of the gateway.
|
||||
pub identity: IdentityKey,
|
||||
|
||||
/// The id pre-assigned to this gateway
|
||||
pub node_id: NodeId,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct PreassignedGatewayIdsResponse {
|
||||
pub ids: Vec<PreassignedId>,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<IdentityKey>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{Decimal, StdError, StdResult, Uint128};
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::mixnode::PendingMixNodeChanges;
|
||||
use crate::{
|
||||
EpochEventId, IntervalEventId, MixNodeBond, MixNodeDetails, NodeId, NodeRewarding, NymNodeBond,
|
||||
NymNodeDetails, PendingNodeChanges,
|
||||
};
|
||||
use contracts_common::IdentityKeyRef;
|
||||
use cosmwasm_std::{Coin, Decimal, StdError, StdResult, Uint128};
|
||||
|
||||
pub fn compare_decimals(a: Decimal, b: Decimal, epsilon: Option<Decimal>) {
|
||||
let epsilon = epsilon.unwrap_or_else(|| Decimal::from_ratio(1u128, 100_000_000u128));
|
||||
@@ -31,3 +38,158 @@ where
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NodeDetails {
|
||||
type Bond: NodeBond;
|
||||
type PendingChanges: PendingChanges;
|
||||
|
||||
fn split(self) -> (Self::Bond, NodeRewarding, Self::PendingChanges);
|
||||
fn rewarding_info(&self) -> &NodeRewarding;
|
||||
fn bond_info(&self) -> &Self::Bond;
|
||||
fn pending_changes(&self) -> &Self::PendingChanges;
|
||||
}
|
||||
|
||||
pub trait NodeBond {
|
||||
fn node_id(&self) -> NodeId;
|
||||
|
||||
fn is_unbonding(&self) -> bool;
|
||||
|
||||
fn identity(&self) -> IdentityKeyRef;
|
||||
|
||||
fn original_pledge(&self) -> &Coin;
|
||||
|
||||
fn ensure_bonded(&self) -> Result<(), MixnetContractError> {
|
||||
if self.is_unbonding() {
|
||||
return Err(MixnetContractError::NodeIsUnbonding {
|
||||
node_id: self.node_id(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PendingChanges {
|
||||
fn pending_pledge_changes(&self) -> Option<EpochEventId>;
|
||||
|
||||
fn pending_cost_params_changes(&self) -> Option<IntervalEventId>;
|
||||
|
||||
fn ensure_no_pending_pledge_changes(&self) -> Result<(), MixnetContractError> {
|
||||
if let Some(pending_event_id) = self.pending_pledge_changes() {
|
||||
return Err(MixnetContractError::PendingPledgeChange { pending_event_id });
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_no_pending_params_changes(&self) -> Result<(), MixnetContractError> {
|
||||
if let Some(pending_event_id) = self.pending_cost_params_changes() {
|
||||
return Err(MixnetContractError::PendingParamsChange { pending_event_id });
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeDetails for MixNodeDetails {
|
||||
type Bond = MixNodeBond;
|
||||
type PendingChanges = PendingMixNodeChanges;
|
||||
|
||||
fn split(self) -> (Self::Bond, NodeRewarding, Self::PendingChanges) {
|
||||
(
|
||||
self.bond_information,
|
||||
self.rewarding_details,
|
||||
self.pending_changes,
|
||||
)
|
||||
}
|
||||
|
||||
fn rewarding_info(&self) -> &NodeRewarding {
|
||||
&self.rewarding_details
|
||||
}
|
||||
|
||||
fn bond_info(&self) -> &Self::Bond {
|
||||
&self.bond_information
|
||||
}
|
||||
|
||||
fn pending_changes(&self) -> &Self::PendingChanges {
|
||||
&self.pending_changes
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeBond for MixNodeBond {
|
||||
fn node_id(&self) -> NodeId {
|
||||
self.mix_id
|
||||
}
|
||||
|
||||
fn is_unbonding(&self) -> bool {
|
||||
self.is_unbonding
|
||||
}
|
||||
|
||||
fn identity(&self) -> IdentityKeyRef {
|
||||
self.identity()
|
||||
}
|
||||
|
||||
fn original_pledge(&self) -> &Coin {
|
||||
self.original_pledge()
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingChanges for PendingMixNodeChanges {
|
||||
fn pending_pledge_changes(&self) -> Option<EpochEventId> {
|
||||
self.pledge_change
|
||||
}
|
||||
|
||||
fn pending_cost_params_changes(&self) -> Option<IntervalEventId> {
|
||||
self.cost_params_change
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeDetails for NymNodeDetails {
|
||||
type Bond = NymNodeBond;
|
||||
type PendingChanges = PendingNodeChanges;
|
||||
|
||||
fn split(self) -> (Self::Bond, NodeRewarding, Self::PendingChanges) {
|
||||
(
|
||||
self.bond_information,
|
||||
self.rewarding_details,
|
||||
self.pending_changes,
|
||||
)
|
||||
}
|
||||
|
||||
fn rewarding_info(&self) -> &NodeRewarding {
|
||||
&self.rewarding_details
|
||||
}
|
||||
|
||||
fn bond_info(&self) -> &Self::Bond {
|
||||
&self.bond_information
|
||||
}
|
||||
|
||||
fn pending_changes(&self) -> &Self::PendingChanges {
|
||||
&self.pending_changes
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeBond for NymNodeBond {
|
||||
fn node_id(&self) -> NodeId {
|
||||
self.node_id
|
||||
}
|
||||
|
||||
fn is_unbonding(&self) -> bool {
|
||||
self.is_unbonding
|
||||
}
|
||||
|
||||
fn identity(&self) -> IdentityKeyRef {
|
||||
self.identity()
|
||||
}
|
||||
|
||||
fn original_pledge(&self) -> &Coin {
|
||||
&self.original_pledge
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingChanges for PendingNodeChanges {
|
||||
fn pending_pledge_changes(&self) -> Option<EpochEventId> {
|
||||
self.pledge_change
|
||||
}
|
||||
|
||||
fn pending_cost_params_changes(&self) -> Option<IntervalEventId> {
|
||||
self.cost_params_change
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::MixId;
|
||||
use crate::nym_node::Role;
|
||||
use crate::NodeId;
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_schema::schemars::gen::SchemaGenerator;
|
||||
use cosmwasm_schema::schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||
@@ -86,7 +87,7 @@ impl EpochStatus {
|
||||
|
||||
pub fn update_last_rewarded(
|
||||
&mut self,
|
||||
new_last_rewarded: MixId,
|
||||
new_last_rewarded: NodeId,
|
||||
) -> Result<bool, MixnetContractError> {
|
||||
match &mut self.state {
|
||||
EpochState::Rewarding {
|
||||
@@ -109,7 +110,7 @@ impl EpochStatus {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_rewarded(&self) -> Result<MixId, MixnetContractError> {
|
||||
pub fn last_rewarded(&self) -> Result<NodeId, MixnetContractError> {
|
||||
match self.state {
|
||||
EpochState::Rewarding { last_rewarded, .. } => Ok(last_rewarded),
|
||||
state => Err(MixnetContractError::UnexpectedNonRewardingEpochState {
|
||||
@@ -127,12 +128,23 @@ impl EpochStatus {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ensure_is_in_advancement_state(&self) -> Result<(), MixnetContractError> {
|
||||
if !matches!(self.state, EpochState::AdvancingEpoch) {
|
||||
return Err(MixnetContractError::EpochNotInAdvancementState {
|
||||
pub fn ensure_is_in_expected_role_assignment_state(
|
||||
&self,
|
||||
caller: Role,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
let EpochState::RoleAssignment { next } = self.state else {
|
||||
return Err(MixnetContractError::EpochNotInRoleAssignmentState {
|
||||
current_state: self.state,
|
||||
});
|
||||
};
|
||||
|
||||
if caller != next {
|
||||
return Err(MixnetContractError::UnexpectedRoleAssignment {
|
||||
expected: next,
|
||||
got: caller,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -147,10 +159,6 @@ impl EpochStatus {
|
||||
pub fn is_reconciling(&self) -> bool {
|
||||
matches!(self.state, EpochState::ReconcilingEvents)
|
||||
}
|
||||
|
||||
pub fn is_advancing(&self) -> bool {
|
||||
matches!(self.state, EpochState::AdvancingEpoch)
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of the current rewarding epoch.
|
||||
@@ -167,10 +175,10 @@ pub enum EpochState {
|
||||
#[serde(alias = "Rewarding")]
|
||||
Rewarding {
|
||||
/// The id of the last node that has already received its rewards.
|
||||
last_rewarded: MixId,
|
||||
last_rewarded: NodeId,
|
||||
|
||||
/// The id of the last node that's going to be rewarded before progressing into the next state.
|
||||
final_node_id: MixId,
|
||||
final_node_id: NodeId,
|
||||
// total_rewarded: u32,
|
||||
},
|
||||
|
||||
@@ -179,10 +187,9 @@ pub enum EpochState {
|
||||
#[serde(alias = "ReconcilingEvents")]
|
||||
ReconcilingEvents,
|
||||
|
||||
/// Represents the state of an epoch when all mixnodes have already been rewarded for their work in this epoch,
|
||||
/// all issued actions got resolved and the epoch should now be advanced whilst assigning new rewarded set.
|
||||
#[serde(alias = "AdvancingEpoch")]
|
||||
AdvancingEpoch,
|
||||
/// Represents the state of an epoch when all nodes have already been rewarded for their work in this epoch,
|
||||
/// all issued actions got resolved and node roles should now be assigned before advancing into the next epoch.
|
||||
RoleAssignment { next: Role },
|
||||
}
|
||||
|
||||
impl Display for EpochState {
|
||||
@@ -197,7 +204,9 @@ impl Display for EpochState {
|
||||
"mix rewarding (last rewarded: {last_rewarded}, final node: {final_node_id})"
|
||||
),
|
||||
EpochState::ReconcilingEvents => write!(f, "event reconciliation"),
|
||||
EpochState::AdvancingEpoch => write!(f, "advancing epoch"),
|
||||
EpochState::RoleAssignment { next } => {
|
||||
write!(f, "role assignment with next assignment for: {next}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,7 +215,7 @@ impl Display for EpochState {
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/Interval.ts")
|
||||
ts(export, export_to = "ts-packages/types/src/types/rust/Interval.ts")
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
@@ -359,6 +368,17 @@ impl Interval {
|
||||
self.current_epoch_end_unix_timestamp() <= env.block.time.seconds() as i64
|
||||
}
|
||||
|
||||
pub fn ensure_current_epoch_is_over(&self, env: &Env) -> Result<(), MixnetContractError> {
|
||||
if !self.is_current_epoch_over(env) {
|
||||
return Err(MixnetContractError::EpochInProgress {
|
||||
current_block_time: env.block.time.seconds(),
|
||||
epoch_start: self.current_epoch_start_unix_timestamp(),
|
||||
epoch_end: self.current_epoch_end_unix_timestamp(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn secs_until_current_epoch_end(&self, env: &Env) -> i64 {
|
||||
if self.is_current_epoch_over(env) {
|
||||
0
|
||||
|
||||
@@ -3,32 +3,30 @@
|
||||
|
||||
#![warn(clippy::expect_used)]
|
||||
#![warn(clippy::unwrap_used)]
|
||||
#![warn(clippy::todo)]
|
||||
|
||||
mod constants;
|
||||
pub mod constants;
|
||||
pub mod delegation;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
pub mod families;
|
||||
pub mod gateway;
|
||||
pub mod helpers;
|
||||
pub mod interval;
|
||||
pub mod mixnode;
|
||||
pub mod msg;
|
||||
pub mod nym_node;
|
||||
pub mod pending_events;
|
||||
pub mod reward_params;
|
||||
pub mod rewarding;
|
||||
pub mod signing_types;
|
||||
pub mod types;
|
||||
|
||||
pub use constants::*;
|
||||
pub use contracts_common::types::*;
|
||||
pub use cosmwasm_std::{Addr, Coin, Decimal, Fraction};
|
||||
pub use delegation::{
|
||||
Delegation, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
|
||||
PagedMixNodeDelegationsResponse,
|
||||
};
|
||||
pub use families::{
|
||||
Family, FamilyByHeadResponse, FamilyByLabelResponse, FamilyHead, FamilyMembersByHeadResponse,
|
||||
FamilyMembersByLabelResponse, PagedFamiliesResponse, PagedMembersResponse,
|
||||
PagedNodeDelegationsResponse,
|
||||
};
|
||||
pub use gateway::{
|
||||
Gateway, GatewayBond, GatewayBondResponse, GatewayConfigUpdate, GatewayOwnershipResponse,
|
||||
@@ -38,11 +36,12 @@ pub use interval::{
|
||||
CurrentIntervalResponse, EpochId, EpochState, EpochStatus, Interval, IntervalId,
|
||||
};
|
||||
pub use mixnode::{
|
||||
Layer, MixNode, MixNodeBond, MixNodeConfigUpdate, MixNodeCostParams, MixNodeDetails,
|
||||
MixNodeRewarding, MixOwnershipResponse, MixnodeDetailsByIdentityResponse,
|
||||
MixnodeDetailsResponse, PagedMixnodeBondsResponse, RewardedSetNodeStatus, UnbondedMixnode,
|
||||
LegacyMixLayer, MixNode, MixNodeBond, MixNodeConfigUpdate, MixNodeDetails,
|
||||
MixOwnershipResponse, MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse, NodeCostParams,
|
||||
NodeRewarding, PagedMixnodeBondsResponse, UnbondedMixnode,
|
||||
};
|
||||
pub use msg::*;
|
||||
pub use nym_node::{NymNode, NymNodeBond, NymNodeDetails, PendingNodeChanges};
|
||||
pub use pending_events::{
|
||||
EpochEventId, IntervalEventId, NumberOfPendingEventsResponse, PendingEpochEvent,
|
||||
PendingEpochEventData, PendingEpochEventKind, PendingEpochEventResponse,
|
||||
@@ -50,8 +49,6 @@ pub use pending_events::{
|
||||
PendingIntervalEventKind, PendingIntervalEventResponse, PendingIntervalEventsResponse,
|
||||
};
|
||||
pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams};
|
||||
pub use rewarding::{
|
||||
EstimatedCurrentEpochRewardResponse, PagedRewardedSetResponse, PendingRewardResponse,
|
||||
};
|
||||
pub use rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse};
|
||||
pub use signing_types::*;
|
||||
pub use types::*;
|
||||
|
||||
@@ -7,42 +7,18 @@
|
||||
use crate::constants::{TOKEN_SUPPLY, UNIT_DELEGATION_BASE};
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::helpers::IntoBaseDecimal;
|
||||
use crate::reward_params::{NodeRewardParams, RewardingParams};
|
||||
use crate::reward_params::{NodeRewardingParameters, RewardingParams};
|
||||
use crate::rewarding::helpers::truncate_reward;
|
||||
use crate::rewarding::RewardDistribution;
|
||||
use crate::{
|
||||
Delegation, EpochEventId, EpochId, IdentityKey, MixId, OperatingCostRange, Percent,
|
||||
ProfitMarginRange, SphinxKey,
|
||||
Delegation, EpochEventId, EpochId, IdentityKey, IntervalEventId, NodeId, OperatingCostRange,
|
||||
Percent, ProfitMarginRange, SphinxKey,
|
||||
};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128};
|
||||
use schemars::JsonSchema;
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
/// Current state of given node in the rewarded set.
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/RewardedSetNodeStatus.ts")
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub enum RewardedSetNodeStatus {
|
||||
/// Node that is currently active, i.e. is expected to be used by clients for mixing packets.
|
||||
#[serde(alias = "Active")]
|
||||
Active,
|
||||
|
||||
/// Node that is currently in standby, i.e. it's present in the rewarded set but is not active.
|
||||
#[serde(alias = "Standby")]
|
||||
Standby,
|
||||
}
|
||||
|
||||
impl RewardedSetNodeStatus {
|
||||
pub fn is_active(&self) -> bool {
|
||||
matches!(self, RewardedSetNodeStatus::Active)
|
||||
}
|
||||
}
|
||||
|
||||
/// Full details associated with given mixnode.
|
||||
#[cw_serde]
|
||||
pub struct MixNodeDetails {
|
||||
@@ -50,7 +26,7 @@ pub struct MixNodeDetails {
|
||||
pub bond_information: MixNodeBond,
|
||||
|
||||
/// Details used for computation of rewarding related data.
|
||||
pub rewarding_details: MixNodeRewarding,
|
||||
pub rewarding_details: NodeRewarding,
|
||||
|
||||
/// Adjustments to the mixnode that are ought to happen during future epoch transitions.
|
||||
#[serde(default)]
|
||||
@@ -60,7 +36,7 @@ pub struct MixNodeDetails {
|
||||
impl MixNodeDetails {
|
||||
pub fn new(
|
||||
bond_information: MixNodeBond,
|
||||
rewarding_details: MixNodeRewarding,
|
||||
rewarding_details: NodeRewarding,
|
||||
pending_changes: PendingMixNodeChanges,
|
||||
) -> Self {
|
||||
MixNodeDetails {
|
||||
@@ -70,14 +46,10 @@ impl MixNodeDetails {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mix_id(&self) -> MixId {
|
||||
pub fn mix_id(&self) -> NodeId {
|
||||
self.bond_information.mix_id
|
||||
}
|
||||
|
||||
pub fn layer(&self) -> Layer {
|
||||
self.bond_information.layer
|
||||
}
|
||||
|
||||
pub fn is_unbonding(&self) -> bool {
|
||||
self.bond_information.is_unbonding
|
||||
}
|
||||
@@ -106,10 +78,11 @@ impl MixNodeDetails {
|
||||
}
|
||||
}
|
||||
|
||||
// currently this struct is shared between mixnodes and nymnodes
|
||||
#[cw_serde]
|
||||
pub struct MixNodeRewarding {
|
||||
pub struct NodeRewarding {
|
||||
/// Information provided by the operator that influence the cost function.
|
||||
pub cost_params: MixNodeCostParams,
|
||||
pub cost_params: NodeCostParams,
|
||||
|
||||
/// Total pledge and compounded reward earned by the node operator.
|
||||
pub operator: Decimal,
|
||||
@@ -120,7 +93,7 @@ pub struct MixNodeRewarding {
|
||||
/// Cumulative reward earned by the "unit delegation" since the block 0.
|
||||
pub total_unit_reward: Decimal,
|
||||
|
||||
/// Value of the theoretical "unit delegation" that has delegated to this mixnode at block 0.
|
||||
/// Value of the theoretical "unit delegation" that has delegated to this node at block 0.
|
||||
pub unit_delegation: Decimal,
|
||||
|
||||
/// Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt
|
||||
@@ -133,9 +106,9 @@ pub struct MixNodeRewarding {
|
||||
pub unique_delegations: u32,
|
||||
}
|
||||
|
||||
impl MixNodeRewarding {
|
||||
impl NodeRewarding {
|
||||
pub fn initialise_new(
|
||||
cost_params: MixNodeCostParams,
|
||||
cost_params: NodeCostParams,
|
||||
initial_pledge: &Coin,
|
||||
current_epoch: EpochId,
|
||||
) -> Result<Self, MixnetContractError> {
|
||||
@@ -144,7 +117,7 @@ impl MixNodeRewarding {
|
||||
"pledge cannot be larger than the token supply"
|
||||
);
|
||||
|
||||
Ok(MixNodeRewarding {
|
||||
Ok(NodeRewarding {
|
||||
cost_params,
|
||||
operator: initial_pledge.amount.into_base_decimal()?,
|
||||
delegates: Decimal::zero(),
|
||||
@@ -155,6 +128,15 @@ impl MixNodeRewarding {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn normalise_cost_function(
|
||||
&mut self,
|
||||
allowed_profit_margin: ProfitMarginRange,
|
||||
allowed_operating_cost: OperatingCostRange,
|
||||
) {
|
||||
self.normalise_profit_margin(allowed_profit_margin);
|
||||
self.normalise_operating_cost(allowed_operating_cost)
|
||||
}
|
||||
|
||||
pub fn normalise_profit_margin(&mut self, allowed_range: ProfitMarginRange) {
|
||||
self.cost_params.profit_margin_percent =
|
||||
allowed_range.normalise(self.cost_params.profit_margin_percent)
|
||||
@@ -257,23 +239,18 @@ impl MixNodeRewarding {
|
||||
|
||||
pub fn node_reward(
|
||||
&self,
|
||||
reward_params: &RewardingParams,
|
||||
node_params: NodeRewardParams,
|
||||
global_params: &RewardingParams,
|
||||
node_params: NodeRewardingParameters,
|
||||
) -> Decimal {
|
||||
let work = if node_params.in_active_set {
|
||||
reward_params.active_node_work()
|
||||
} else {
|
||||
reward_params.standby_node_work()
|
||||
};
|
||||
let work = node_params.work_factor;
|
||||
let alpha = global_params.interval.sybil_resistance;
|
||||
|
||||
let alpha = reward_params.interval.sybil_resistance;
|
||||
|
||||
reward_params.interval.epoch_reward_budget
|
||||
* node_params.performance.value()
|
||||
* self.bond_saturation(reward_params)
|
||||
global_params.interval.epoch_reward_budget
|
||||
* node_params.performance
|
||||
* self.bond_saturation(global_params)
|
||||
* (work
|
||||
+ alpha.value() * self.pledge_saturation(reward_params)
|
||||
/ reward_params.dec_rewarded_set_size())
|
||||
+ alpha.value() * self.pledge_saturation(global_params)
|
||||
/ global_params.dec_rewarded_set_size())
|
||||
/ (Decimal::one() + alpha.value())
|
||||
}
|
||||
|
||||
@@ -285,7 +262,7 @@ impl MixNodeRewarding {
|
||||
epochs_in_interval: u32,
|
||||
) -> RewardDistribution {
|
||||
let node_cost =
|
||||
self.cost_params.epoch_operating_cost(epochs_in_interval) * node_performance.value();
|
||||
self.cost_params.epoch_operating_cost(epochs_in_interval) * node_performance;
|
||||
|
||||
// check if profit is positive
|
||||
if node_reward > node_cost {
|
||||
@@ -315,7 +292,7 @@ impl MixNodeRewarding {
|
||||
pub fn calculate_epoch_reward(
|
||||
&self,
|
||||
reward_params: &RewardingParams,
|
||||
node_params: NodeRewardParams,
|
||||
node_params: NodeRewardingParameters,
|
||||
epochs_in_interval: u32,
|
||||
) -> RewardDistribution {
|
||||
let node_reward = self.node_reward(reward_params, node_params);
|
||||
@@ -341,7 +318,7 @@ impl MixNodeRewarding {
|
||||
pub fn epoch_rewarding(
|
||||
&mut self,
|
||||
reward_params: &RewardingParams,
|
||||
node_params: NodeRewardParams,
|
||||
node_params: NodeRewardingParameters,
|
||||
epochs_in_interval: u32,
|
||||
absolute_epoch_id: EpochId,
|
||||
) {
|
||||
@@ -492,13 +469,30 @@ impl MixNodeRewarding {
|
||||
amount / self.delegates
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a copy of `Self` with zeroed operator value
|
||||
pub fn clear_operator(&self) -> NodeRewarding {
|
||||
let mut zeroed = self.clone();
|
||||
zeroed.operator = Decimal::zero();
|
||||
zeroed
|
||||
}
|
||||
}
|
||||
|
||||
/// Basic mixnode information provided by the node operator.
|
||||
#[cw_serde]
|
||||
// note: we had to remove `#[cw_serde]` as it enforces `#[serde(deny_unknown_fields)]` which we do not want
|
||||
// with the removal of explicit .layer field
|
||||
#[derive(
|
||||
::cosmwasm_schema::serde::Serialize,
|
||||
::cosmwasm_schema::serde::Deserialize,
|
||||
::std::clone::Clone,
|
||||
::std::fmt::Debug,
|
||||
::std::cmp::PartialEq,
|
||||
::cosmwasm_schema::schemars::JsonSchema,
|
||||
)]
|
||||
#[schemars(crate = "::cosmwasm_schema::schemars")]
|
||||
pub struct MixNodeBond {
|
||||
/// Unique id assigned to the bonded mixnode.
|
||||
pub mix_id: MixId,
|
||||
pub mix_id: NodeId,
|
||||
|
||||
/// Address of the owner of this mixnode.
|
||||
pub owner: Addr,
|
||||
@@ -506,9 +500,9 @@ pub struct MixNodeBond {
|
||||
/// Original amount pledged by the operator of this node.
|
||||
pub original_pledge: Coin,
|
||||
|
||||
/// Layer assigned to this mixnode.
|
||||
pub layer: Layer,
|
||||
|
||||
// REMOVED (but might be needed due to legacy things, idk yet)
|
||||
// /// Layer assigned to this mixnode.
|
||||
// pub layer: Layer,
|
||||
/// Information provided by the operator for the purposes of bonding.
|
||||
pub mix_node: MixNode,
|
||||
|
||||
@@ -525,26 +519,6 @@ pub struct MixNodeBond {
|
||||
}
|
||||
|
||||
impl MixNodeBond {
|
||||
pub fn new(
|
||||
mix_id: MixId,
|
||||
owner: Addr,
|
||||
original_pledge: Coin,
|
||||
layer: Layer,
|
||||
mix_node: MixNode,
|
||||
bonding_height: u64,
|
||||
) -> Self {
|
||||
MixNodeBond {
|
||||
mix_id,
|
||||
owner,
|
||||
original_pledge,
|
||||
layer,
|
||||
mix_node,
|
||||
proxy: None,
|
||||
bonding_height,
|
||||
is_unbonding: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn identity(&self) -> &str {
|
||||
&self.mix_node.identity_key
|
||||
}
|
||||
@@ -567,7 +541,7 @@ impl MixNodeBond {
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/Mixnode.ts")
|
||||
ts(export, export_to = "ts-packages/types/src/types/rust/Mixnode.ts")
|
||||
)]
|
||||
pub struct MixNode {
|
||||
/// Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com
|
||||
@@ -595,21 +569,21 @@ pub struct MixNode {
|
||||
/// The cost parameters, or the cost function, defined for the particular mixnode that influences
|
||||
/// how the rewards should be split between the node operator and its delegators.
|
||||
#[cw_serde]
|
||||
pub struct MixNodeCostParams {
|
||||
/// The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.
|
||||
pub struct NodeCostParams {
|
||||
/// The profit margin of the associated node, i.e. the desired percent of the reward to be distributed to the operator.
|
||||
pub profit_margin_percent: Percent,
|
||||
|
||||
/// Operating cost of the associated mixnode per the entire interval.
|
||||
/// Operating cost of the associated node per the entire interval.
|
||||
pub interval_operating_cost: Coin,
|
||||
}
|
||||
|
||||
impl MixNodeCostParams {
|
||||
impl NodeCostParams {
|
||||
pub fn to_inline_json(&self) -> String {
|
||||
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
}
|
||||
}
|
||||
|
||||
impl MixNodeCostParams {
|
||||
impl NodeCostParams {
|
||||
pub fn epoch_operating_cost(&self, epochs_in_interval: u32) -> Decimal {
|
||||
Decimal::from_ratio(self.interval_operating_cost.amount, epochs_in_interval)
|
||||
}
|
||||
@@ -628,38 +602,39 @@ impl MixNodeCostParams {
|
||||
Deserialize_repr,
|
||||
JsonSchema,
|
||||
)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[repr(u8)]
|
||||
pub enum Layer {
|
||||
pub enum LegacyMixLayer {
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Three = 3,
|
||||
}
|
||||
|
||||
impl From<Layer> for String {
|
||||
fn from(layer: Layer) -> Self {
|
||||
impl From<LegacyMixLayer> for String {
|
||||
fn from(layer: LegacyMixLayer) -> Self {
|
||||
(layer as u8).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Layer {
|
||||
impl TryFrom<u8> for LegacyMixLayer {
|
||||
type Error = MixnetContractError;
|
||||
|
||||
fn try_from(i: u8) -> Result<Layer, MixnetContractError> {
|
||||
fn try_from(i: u8) -> Result<LegacyMixLayer, MixnetContractError> {
|
||||
match i {
|
||||
1 => Ok(Layer::One),
|
||||
2 => Ok(Layer::Two),
|
||||
3 => Ok(Layer::Three),
|
||||
1 => Ok(LegacyMixLayer::One),
|
||||
2 => Ok(LegacyMixLayer::Two),
|
||||
3 => Ok(LegacyMixLayer::Three),
|
||||
_ => Err(MixnetContractError::InvalidLayer(i)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Layer> for u8 {
|
||||
fn from(layer: Layer) -> u8 {
|
||||
impl From<LegacyMixLayer> for u8 {
|
||||
fn from(layer: LegacyMixLayer) -> u8 {
|
||||
match layer {
|
||||
Layer::One => 1,
|
||||
Layer::Two => 2,
|
||||
Layer::Three => 3,
|
||||
LegacyMixLayer::One => 1,
|
||||
LegacyMixLayer::Two => 2,
|
||||
LegacyMixLayer::Three => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -667,19 +642,24 @@ impl From<Layer> for u8 {
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/PendingMixnodeChanges.ts")
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/PendingMixnodeChanges.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Default, Copy)]
|
||||
pub struct PendingMixNodeChanges {
|
||||
pub pledge_change: Option<EpochEventId>,
|
||||
// pub cost_params_change: Option<IntervalEventId>,
|
||||
#[serde(default)]
|
||||
pub cost_params_change: Option<IntervalEventId>,
|
||||
}
|
||||
|
||||
impl PendingMixNodeChanges {
|
||||
pub fn new_empty() -> PendingMixNodeChanges {
|
||||
PendingMixNodeChanges {
|
||||
pledge_change: None,
|
||||
cost_params_change: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -688,7 +668,10 @@ impl PendingMixNodeChanges {
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/UnbondedMixnode.ts")
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/UnbondedMixnode.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
pub struct UnbondedMixnode {
|
||||
@@ -712,7 +695,10 @@ pub struct UnbondedMixnode {
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/MixNodeConfigUpdate.ts")
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/MixNodeConfigUpdate.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
pub struct MixNodeConfigUpdate {
|
||||
@@ -740,11 +726,11 @@ pub struct PagedMixnodeBondsResponse {
|
||||
pub per_page: usize,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<MixId>,
|
||||
pub start_next_after: Option<NodeId>,
|
||||
}
|
||||
|
||||
impl PagedMixnodeBondsResponse {
|
||||
pub fn new(nodes: Vec<MixNodeBond>, per_page: usize, start_next_after: Option<MixId>) -> Self {
|
||||
pub fn new(nodes: Vec<MixNodeBond>, per_page: usize, start_next_after: Option<NodeId>) -> Self {
|
||||
PagedMixnodeBondsResponse {
|
||||
nodes,
|
||||
per_page,
|
||||
@@ -766,14 +752,14 @@ pub struct PagedMixnodesDetailsResponse {
|
||||
pub per_page: usize,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<MixId>,
|
||||
pub start_next_after: Option<NodeId>,
|
||||
}
|
||||
|
||||
impl PagedMixnodesDetailsResponse {
|
||||
pub fn new(
|
||||
nodes: Vec<MixNodeDetails>,
|
||||
per_page: usize,
|
||||
start_next_after: Option<MixId>,
|
||||
start_next_after: Option<NodeId>,
|
||||
) -> Self {
|
||||
PagedMixnodesDetailsResponse {
|
||||
nodes,
|
||||
@@ -787,21 +773,21 @@ impl PagedMixnodesDetailsResponse {
|
||||
#[cw_serde]
|
||||
pub struct PagedUnbondedMixnodesResponse {
|
||||
/// The past ids of unbonded mixnodes alongside their basic information such as the owner or the identity key.
|
||||
pub nodes: Vec<(MixId, UnbondedMixnode)>,
|
||||
pub nodes: Vec<(NodeId, UnbondedMixnode)>,
|
||||
|
||||
/// Maximum number of entries that could be included in a response. `per_page <= nodes.len()`
|
||||
// this field is rather redundant and should be deprecated.
|
||||
pub per_page: usize,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<MixId>,
|
||||
pub start_next_after: Option<NodeId>,
|
||||
}
|
||||
|
||||
impl PagedUnbondedMixnodesResponse {
|
||||
pub fn new(
|
||||
nodes: Vec<(MixId, UnbondedMixnode)>,
|
||||
nodes: Vec<(NodeId, UnbondedMixnode)>,
|
||||
per_page: usize,
|
||||
start_next_after: Option<MixId>,
|
||||
start_next_after: Option<NodeId>,
|
||||
) -> Self {
|
||||
PagedUnbondedMixnodesResponse {
|
||||
nodes,
|
||||
@@ -825,7 +811,7 @@ pub struct MixOwnershipResponse {
|
||||
#[cw_serde]
|
||||
pub struct MixnodeDetailsResponse {
|
||||
/// Id of the requested mixnode.
|
||||
pub mix_id: MixId,
|
||||
pub mix_id: NodeId,
|
||||
|
||||
/// If there exists a mixnode with the provided id, this field contains its detailed information.
|
||||
pub mixnode_details: Option<MixNodeDetails>,
|
||||
@@ -845,17 +831,17 @@ pub struct MixnodeDetailsByIdentityResponse {
|
||||
#[cw_serde]
|
||||
pub struct MixnodeRewardingDetailsResponse {
|
||||
/// Id of the requested mixnode.
|
||||
pub mix_id: MixId,
|
||||
pub mix_id: NodeId,
|
||||
|
||||
/// If there exists a mixnode with the provided id, this field contains its rewarding information.
|
||||
pub rewarding_details: Option<MixNodeRewarding>,
|
||||
pub rewarding_details: Option<NodeRewarding>,
|
||||
}
|
||||
|
||||
/// Response containing basic information of an unbonded mixnode with the provided id.
|
||||
#[cw_serde]
|
||||
pub struct UnbondedMixnodeResponse {
|
||||
/// Id of the requested mixnode.
|
||||
pub mix_id: MixId,
|
||||
pub mix_id: NodeId,
|
||||
|
||||
/// If there existed a mixnode with the provided id, this field contains its basic information.
|
||||
pub unbonded_info: Option<UnbondedMixnode>,
|
||||
@@ -863,9 +849,9 @@ pub struct UnbondedMixnodeResponse {
|
||||
|
||||
/// Response containing the current state of the stake saturation of a mixnode with the provided id.
|
||||
#[cw_serde]
|
||||
pub struct StakeSaturationResponse {
|
||||
pub struct MixStakeSaturationResponse {
|
||||
/// Id of the requested mixnode.
|
||||
pub mix_id: MixId,
|
||||
pub mix_id: NodeId,
|
||||
|
||||
/// The current stake saturation of this node that is indirectly used in reward calculation formulas.
|
||||
/// Note that it can't be larger than 1.
|
||||
|
||||
@@ -3,15 +3,17 @@
|
||||
|
||||
use crate::delegation::{self, OwnerProxySubKey};
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::families::FamilyHead;
|
||||
use crate::gateway::{Gateway, GatewayConfigUpdate};
|
||||
use crate::helpers::IntoBaseDecimal;
|
||||
use crate::mixnode::{Layer, MixNode, MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use crate::mixnode::{MixNode, MixNodeConfigUpdate, NodeCostParams};
|
||||
use crate::nym_node::{NodeConfigUpdate, Role};
|
||||
use crate::pending_events::{EpochEventId, IntervalEventId};
|
||||
use crate::reward_params::{
|
||||
IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams,
|
||||
ActiveSetUpdate, IntervalRewardParams, IntervalRewardingParamsUpdate, NodeRewardingParameters,
|
||||
Performance, RewardedSetParams, RewardingParams, WorkFactor,
|
||||
};
|
||||
use crate::types::{ContractStateParams, LayerAssignment, MixId};
|
||||
use crate::types::{ContractStateParams, NodeId};
|
||||
use crate::{NymNode, RoleAssignment};
|
||||
use crate::{OperatingCostRange, ProfitMarginRange};
|
||||
use contracts_common::{signing::MessageSignature, IdentityKey, Percent};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
@@ -21,28 +23,31 @@ use std::time::Duration;
|
||||
#[cfg(feature = "schema")]
|
||||
use crate::{
|
||||
delegation::{
|
||||
MixNodeDelegationResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
|
||||
PagedMixNodeDelegationsResponse,
|
||||
NodeDelegationResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
|
||||
PagedNodeDelegationsResponse,
|
||||
},
|
||||
families::{
|
||||
FamilyByHeadResponse, FamilyByLabelResponse, FamilyMembersByHeadResponse,
|
||||
FamilyMembersByLabelResponse, PagedFamiliesResponse, PagedMembersResponse,
|
||||
gateway::{
|
||||
GatewayBondResponse, GatewayOwnershipResponse, PagedGatewayResponse,
|
||||
PreassignedGatewayIdsResponse,
|
||||
},
|
||||
gateway::{GatewayBondResponse, GatewayOwnershipResponse, PagedGatewayResponse},
|
||||
interval::{CurrentIntervalResponse, EpochStatus},
|
||||
mixnode::{
|
||||
MixOwnershipResponse, MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse,
|
||||
MixnodeRewardingDetailsResponse, PagedMixnodeBondsResponse, PagedMixnodesDetailsResponse,
|
||||
PagedUnbondedMixnodesResponse, StakeSaturationResponse, UnbondedMixnodeResponse,
|
||||
MixOwnershipResponse, MixStakeSaturationResponse, MixnodeDetailsByIdentityResponse,
|
||||
MixnodeDetailsResponse, MixnodeRewardingDetailsResponse, PagedMixnodeBondsResponse,
|
||||
PagedMixnodesDetailsResponse, PagedUnbondedMixnodesResponse, UnbondedMixnodeResponse,
|
||||
},
|
||||
nym_node::{
|
||||
EpochAssignmentResponse, NodeDetailsByIdentityResponse, NodeDetailsResponse,
|
||||
NodeOwnershipResponse, NodeRewardingDetailsResponse, PagedNymNodeBondsResponse,
|
||||
PagedNymNodeDetailsResponse, PagedUnbondedNymNodesResponse, RolesMetadataResponse,
|
||||
StakeSaturationResponse, UnbondedNodeResponse,
|
||||
},
|
||||
pending_events::{
|
||||
NumberOfPendingEventsResponse, PendingEpochEventResponse, PendingEpochEventsResponse,
|
||||
PendingIntervalEventResponse, PendingIntervalEventsResponse,
|
||||
},
|
||||
rewarding::{
|
||||
EstimatedCurrentEpochRewardResponse, PagedRewardedSetResponse, PendingRewardResponse,
|
||||
},
|
||||
types::{ContractState, LayerDistribution},
|
||||
rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse},
|
||||
types::ContractState,
|
||||
};
|
||||
#[cfg(feature = "schema")]
|
||||
use contracts_common::{signing::Nonce, ContractBuildInformation};
|
||||
@@ -76,8 +81,7 @@ pub struct InitialRewardingParams {
|
||||
pub active_set_work_factor: Decimal,
|
||||
pub interval_pool_emission: Percent,
|
||||
|
||||
pub rewarded_set_size: u32,
|
||||
pub active_set_size: u32,
|
||||
pub rewarded_set_params: RewardedSetParams,
|
||||
}
|
||||
|
||||
impl InitialRewardingParams {
|
||||
@@ -88,8 +92,11 @@ impl InitialRewardingParams {
|
||||
let epoch_reward_budget = self.initial_reward_pool
|
||||
/ epochs_in_interval.into_base_decimal()?
|
||||
* self.interval_pool_emission;
|
||||
let stake_saturation_point =
|
||||
self.initial_staking_supply / self.rewarded_set_size.into_base_decimal()?;
|
||||
let stake_saturation_point = self.initial_staking_supply
|
||||
/ self
|
||||
.rewarded_set_params
|
||||
.rewarded_set_size()
|
||||
.into_base_decimal()?;
|
||||
|
||||
Ok(RewardingParams {
|
||||
interval: IntervalRewardParams {
|
||||
@@ -102,8 +109,7 @@ impl InitialRewardingParams {
|
||||
active_set_work_factor: self.active_set_work_factor,
|
||||
interval_pool_emission: self.interval_pool_emission,
|
||||
},
|
||||
rewarded_set_size: self.rewarded_set_size,
|
||||
active_set_size: self.active_set_size,
|
||||
rewarded_set: self.rewarded_set_params,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -115,45 +121,6 @@ pub enum ExecuteMsg {
|
||||
admin: String,
|
||||
},
|
||||
|
||||
AssignNodeLayer {
|
||||
mix_id: MixId,
|
||||
layer: Layer,
|
||||
},
|
||||
// Families
|
||||
/// Only owner of the node can crate the family with node as head
|
||||
CreateFamily {
|
||||
label: String,
|
||||
},
|
||||
/// Family head needs to sign the joining node IdentityKey
|
||||
JoinFamily {
|
||||
join_permit: MessageSignature,
|
||||
family_head: FamilyHead,
|
||||
},
|
||||
LeaveFamily {
|
||||
family_head: FamilyHead,
|
||||
},
|
||||
KickFamilyMember {
|
||||
member: IdentityKey,
|
||||
},
|
||||
CreateFamilyOnBehalf {
|
||||
owner_address: String,
|
||||
label: String,
|
||||
},
|
||||
/// Family head needs to sign the joining node IdentityKey, MixNode needs to provide its signature proving that it wants to join the family
|
||||
JoinFamilyOnBehalf {
|
||||
member_address: String,
|
||||
join_permit: MessageSignature,
|
||||
family_head: FamilyHead,
|
||||
},
|
||||
LeaveFamilyOnBehalf {
|
||||
member_address: String,
|
||||
family_head: FamilyHead,
|
||||
},
|
||||
KickFamilyMemberOnBehalf {
|
||||
head_address: String,
|
||||
member: IdentityKey,
|
||||
},
|
||||
|
||||
// state/sys-params-related
|
||||
UpdateRewardingValidatorAddress {
|
||||
address: String,
|
||||
@@ -161,8 +128,8 @@ pub enum ExecuteMsg {
|
||||
UpdateContractStateParams {
|
||||
updated_parameters: ContractStateParams,
|
||||
},
|
||||
UpdateActiveSetSize {
|
||||
active_set_size: u32,
|
||||
UpdateActiveSetDistribution {
|
||||
update: ActiveSetUpdate,
|
||||
force_immediately: bool,
|
||||
},
|
||||
UpdateRewardingParams {
|
||||
@@ -174,25 +141,24 @@ pub enum ExecuteMsg {
|
||||
epoch_duration_secs: u64,
|
||||
force_immediately: bool,
|
||||
},
|
||||
|
||||
BeginEpochTransition {},
|
||||
AdvanceCurrentEpoch {
|
||||
new_rewarded_set: Vec<LayerAssignment>,
|
||||
// families_in_layer: HashMap<String, Layer>,
|
||||
expected_active_set_size: u32,
|
||||
},
|
||||
ReconcileEpochEvents {
|
||||
limit: Option<u32>,
|
||||
},
|
||||
AssignRoles {
|
||||
assignment: RoleAssignment,
|
||||
},
|
||||
|
||||
// mixnode-related:
|
||||
BondMixnode {
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
cost_params: NodeCostParams,
|
||||
owner_signature: MessageSignature,
|
||||
},
|
||||
BondMixnodeOnBehalf {
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
cost_params: NodeCostParams,
|
||||
owner_signature: MessageSignature,
|
||||
owner: String,
|
||||
},
|
||||
@@ -211,11 +177,15 @@ pub enum ExecuteMsg {
|
||||
UnbondMixnodeOnBehalf {
|
||||
owner: String,
|
||||
},
|
||||
UpdateMixnodeCostParams {
|
||||
new_costs: MixNodeCostParams,
|
||||
#[serde(
|
||||
alias = "UpdateMixnodeCostParams",
|
||||
alias = "update_mixnode_cost_params"
|
||||
)]
|
||||
UpdateCostParams {
|
||||
new_costs: NodeCostParams,
|
||||
},
|
||||
UpdateMixnodeCostParamsOnBehalf {
|
||||
new_costs: MixNodeCostParams,
|
||||
new_costs: NodeCostParams,
|
||||
owner: String,
|
||||
},
|
||||
UpdateMixnodeConfig {
|
||||
@@ -225,6 +195,7 @@ pub enum ExecuteMsg {
|
||||
new_config: MixNodeConfigUpdate,
|
||||
owner: String,
|
||||
},
|
||||
MigrateMixnode {},
|
||||
|
||||
// gateway-related:
|
||||
BondGateway {
|
||||
@@ -247,44 +218,64 @@ pub enum ExecuteMsg {
|
||||
new_config: GatewayConfigUpdate,
|
||||
owner: String,
|
||||
},
|
||||
MigrateGateway {
|
||||
cost_params: Option<NodeCostParams>,
|
||||
},
|
||||
|
||||
// nym-node related:
|
||||
BondNymNode {
|
||||
node: NymNode,
|
||||
cost_params: NodeCostParams,
|
||||
owner_signature: MessageSignature,
|
||||
},
|
||||
UnbondNymNode {},
|
||||
UpdateNodeConfig {
|
||||
update: NodeConfigUpdate,
|
||||
},
|
||||
|
||||
// delegation-related:
|
||||
DelegateToMixnode {
|
||||
mix_id: MixId,
|
||||
#[serde(alias = "DelegateToMixnode", alias = "delegate_to_mixnode")]
|
||||
Delegate {
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
},
|
||||
DelegateToMixnodeOnBehalf {
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
delegate: String,
|
||||
},
|
||||
UndelegateFromMixnode {
|
||||
mix_id: MixId,
|
||||
#[serde(alias = "UndelegateFromMixnode", alias = "undelegate_from_mixnode")]
|
||||
Undelegate {
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
},
|
||||
UndelegateFromMixnodeOnBehalf {
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
delegate: String,
|
||||
},
|
||||
|
||||
// reward-related
|
||||
RewardMixnode {
|
||||
mix_id: MixId,
|
||||
performance: Performance,
|
||||
RewardNode {
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
params: NodeRewardingParameters,
|
||||
},
|
||||
WithdrawOperatorReward {},
|
||||
WithdrawOperatorRewardOnBehalf {
|
||||
owner: String,
|
||||
},
|
||||
WithdrawDelegatorReward {
|
||||
mix_id: MixId,
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
},
|
||||
WithdrawDelegatorRewardOnBehalf {
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
owner: String,
|
||||
},
|
||||
|
||||
// vesting migration:
|
||||
MigrateVestedMixNode {},
|
||||
MigrateVestedDelegation {
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
},
|
||||
|
||||
// testing-only
|
||||
@@ -298,41 +289,15 @@ impl ExecuteMsg {
|
||||
pub fn default_memo(&self) -> String {
|
||||
match self {
|
||||
ExecuteMsg::UpdateAdmin { admin } => format!("updating contract admin to {admin}"),
|
||||
ExecuteMsg::AssignNodeLayer { mix_id, layer } => {
|
||||
format!("assigning mix {mix_id} for layer {layer:?}")
|
||||
}
|
||||
ExecuteMsg::CreateFamily { .. } => "crating node family with".to_string(),
|
||||
ExecuteMsg::JoinFamily { family_head, .. } => {
|
||||
format!("joining family {family_head}")
|
||||
}
|
||||
ExecuteMsg::LeaveFamily { family_head, .. } => {
|
||||
format!("leaving family {family_head}")
|
||||
}
|
||||
ExecuteMsg::KickFamilyMember { member, .. } => {
|
||||
format!("kicking {member} from family")
|
||||
}
|
||||
ExecuteMsg::CreateFamilyOnBehalf { .. } => "crating node family with".to_string(),
|
||||
ExecuteMsg::JoinFamilyOnBehalf { family_head, .. } => {
|
||||
format!("joining family {family_head}")
|
||||
}
|
||||
ExecuteMsg::LeaveFamilyOnBehalf { family_head, .. } => {
|
||||
format!("leaving family {family_head}")
|
||||
}
|
||||
ExecuteMsg::KickFamilyMemberOnBehalf { member, .. } => {
|
||||
format!("kicking {member} from family")
|
||||
}
|
||||
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
|
||||
format!("updating rewarding validator to {address}")
|
||||
}
|
||||
ExecuteMsg::UpdateContractStateParams { .. } => {
|
||||
"updating mixnet state parameters".into()
|
||||
}
|
||||
ExecuteMsg::UpdateActiveSetSize {
|
||||
active_set_size,
|
||||
force_immediately,
|
||||
} => format!(
|
||||
"updating active set size to {active_set_size}. forced: {force_immediately}"
|
||||
),
|
||||
ExecuteMsg::UpdateActiveSetDistribution {
|
||||
force_immediately, ..
|
||||
} => format!("updating active set distribution. forced: {force_immediately}"),
|
||||
ExecuteMsg::UpdateRewardingParams {
|
||||
force_immediately, ..
|
||||
} => format!("updating mixnet rewarding parameters. forced: {force_immediately}"),
|
||||
@@ -340,7 +305,6 @@ impl ExecuteMsg {
|
||||
force_immediately, ..
|
||||
} => format!("updating mixnet interval configuration. forced: {force_immediately}"),
|
||||
ExecuteMsg::BeginEpochTransition {} => "beginning epoch transition".into(),
|
||||
ExecuteMsg::AdvanceCurrentEpoch { .. } => "advancing current epoch".into(),
|
||||
ExecuteMsg::ReconcileEpochEvents { .. } => "reconciling epoch events".into(),
|
||||
ExecuteMsg::BondMixnode { mix_node, .. } => {
|
||||
format!("bonding mixnode {}", mix_node.identity_key)
|
||||
@@ -356,7 +320,7 @@ impl ExecuteMsg {
|
||||
}
|
||||
ExecuteMsg::UnbondMixnode { .. } => "unbonding mixnode".into(),
|
||||
ExecuteMsg::UnbondMixnodeOnBehalf { .. } => "unbonding mixnode on behalf".into(),
|
||||
ExecuteMsg::UpdateMixnodeCostParams { .. } => "updating mixnode cost parameters".into(),
|
||||
ExecuteMsg::UpdateCostParams { .. } => "updating mixnode cost parameters".into(),
|
||||
ExecuteMsg::UpdateMixnodeCostParamsOnBehalf { .. } => {
|
||||
"updating mixnode cost parameters on behalf".into()
|
||||
}
|
||||
@@ -376,25 +340,22 @@ impl ExecuteMsg {
|
||||
ExecuteMsg::UpdateGatewayConfigOnBehalf { .. } => {
|
||||
"updating gateway configuration on behalf".into()
|
||||
}
|
||||
ExecuteMsg::DelegateToMixnode { mix_id } => format!("delegating to mixnode {mix_id}"),
|
||||
ExecuteMsg::Delegate { node_id: mix_id } => format!("delegating to mixnode {mix_id}"),
|
||||
ExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, .. } => {
|
||||
format!("delegating to mixnode {mix_id} on behalf")
|
||||
}
|
||||
ExecuteMsg::UndelegateFromMixnode { mix_id } => {
|
||||
ExecuteMsg::Undelegate { node_id: mix_id } => {
|
||||
format!("removing delegation from mixnode {mix_id}")
|
||||
}
|
||||
ExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, .. } => {
|
||||
format!("removing delegation from mixnode {mix_id} on behalf")
|
||||
}
|
||||
ExecuteMsg::RewardMixnode {
|
||||
mix_id,
|
||||
performance,
|
||||
} => format!("rewarding mixnode {mix_id} for performance {performance}"),
|
||||
ExecuteMsg::RewardNode { node_id, .. } => format!("rewarding node {node_id}"),
|
||||
ExecuteMsg::WithdrawOperatorReward { .. } => "withdrawing operator reward".into(),
|
||||
ExecuteMsg::WithdrawOperatorRewardOnBehalf { .. } => {
|
||||
"withdrawing operator reward on behalf".into()
|
||||
}
|
||||
ExecuteMsg::WithdrawDelegatorReward { mix_id } => {
|
||||
ExecuteMsg::WithdrawDelegatorReward { node_id: mix_id } => {
|
||||
format!("withdrawing delegator reward from mixnode {mix_id}")
|
||||
}
|
||||
ExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, .. } => {
|
||||
@@ -402,6 +363,12 @@ impl ExecuteMsg {
|
||||
}
|
||||
ExecuteMsg::MigrateVestedMixNode { .. } => "migrate vested mixnode".into(),
|
||||
ExecuteMsg::MigrateVestedDelegation { .. } => "migrate vested delegation".to_string(),
|
||||
ExecuteMsg::AssignRoles { .. } => "assigning epoch roles".into(),
|
||||
ExecuteMsg::MigrateMixnode { .. } => "migrating legacy mixnode".into(),
|
||||
ExecuteMsg::MigrateGateway { .. } => "migrating legacy gateway".into(),
|
||||
ExecuteMsg::BondNymNode { .. } => "bonding nym-node".into(),
|
||||
ExecuteMsg::UnbondNymNode { .. } => "unbonding nym-node".into(),
|
||||
ExecuteMsg::UpdateNodeConfig { .. } => "updating node config".into(),
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
ExecuteMsg::TestingResolveAllPendingEvents { .. } => {
|
||||
@@ -417,43 +384,6 @@ pub enum QueryMsg {
|
||||
#[cfg_attr(feature = "schema", returns(cw_controllers::AdminResponse))]
|
||||
Admin {},
|
||||
|
||||
// families
|
||||
/// Gets the list of families registered in this contract.
|
||||
#[cfg_attr(feature = "schema", returns(PagedFamiliesResponse))]
|
||||
GetAllFamiliesPaged {
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<String>,
|
||||
},
|
||||
|
||||
/// Gets the list of all family members registered in this contract.
|
||||
#[cfg_attr(feature = "schema", returns(PagedMembersResponse))]
|
||||
GetAllMembersPaged {
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<String>,
|
||||
},
|
||||
|
||||
/// Attempts to lookup family information given the family head.
|
||||
#[cfg_attr(feature = "schema", returns(FamilyByHeadResponse))]
|
||||
GetFamilyByHead { head: String },
|
||||
|
||||
/// Attempts to lookup family information given the family label.
|
||||
#[cfg_attr(feature = "schema", returns(FamilyByLabelResponse))]
|
||||
GetFamilyByLabel { label: String },
|
||||
|
||||
/// Attempts to retrieve family members given the family head.
|
||||
#[cfg_attr(feature = "schema", returns(FamilyMembersByHeadResponse))]
|
||||
GetFamilyMembersByHead { head: String },
|
||||
|
||||
/// Attempts to retrieve family members given the family label.
|
||||
#[cfg_attr(feature = "schema", returns(FamilyMembersByLabelResponse))]
|
||||
GetFamilyMembersByLabel { label: String },
|
||||
|
||||
// state/sys-params-related
|
||||
/// Gets build information of this contract, such as the commit hash used for the build or rustc version.
|
||||
#[cfg_attr(feature = "schema", returns(ContractBuildInformation))]
|
||||
@@ -488,16 +418,6 @@ pub enum QueryMsg {
|
||||
#[cfg_attr(feature = "schema", returns(CurrentIntervalResponse))]
|
||||
GetCurrentIntervalDetails {},
|
||||
|
||||
/// Gets the current list of mixnodes in the rewarded set.
|
||||
#[cfg_attr(feature = "schema", returns(PagedRewardedSetResponse))]
|
||||
GetRewardedSet {
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<MixId>,
|
||||
},
|
||||
|
||||
// mixnode-related:
|
||||
/// Gets the basic list of all currently bonded mixnodes.
|
||||
#[cfg_attr(feature = "schema", returns(PagedMixnodeBondsResponse))]
|
||||
@@ -506,7 +426,7 @@ pub enum QueryMsg {
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<MixId>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
/// Gets the detailed list of all currently bonded mixnodes.
|
||||
@@ -516,7 +436,7 @@ pub enum QueryMsg {
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<MixId>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
/// Gets the basic list of all unbonded mixnodes.
|
||||
@@ -526,20 +446,20 @@ pub enum QueryMsg {
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<MixId>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
/// Gets the basic list of all unbonded mixnodes that belonged to a particular owner.
|
||||
#[cfg_attr(feature = "schema", returns(PagedUnbondedMixnodesResponse))]
|
||||
GetUnbondedMixNodesByOwner {
|
||||
/// The address of the owner of the the mixnodes used for the query.
|
||||
/// The address of the owner of the mixnodes used for the query.
|
||||
owner: String,
|
||||
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<MixId>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
/// Gets the basic list of all unbonded mixnodes that used the particular identity key.
|
||||
@@ -552,7 +472,7 @@ pub enum QueryMsg {
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<MixId>,
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
/// Gets the detailed mixnode information belonging to the particular owner.
|
||||
@@ -566,28 +486,28 @@ pub enum QueryMsg {
|
||||
#[cfg_attr(feature = "schema", returns(MixnodeDetailsResponse))]
|
||||
GetMixnodeDetails {
|
||||
/// Id of the node to query.
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
},
|
||||
|
||||
/// Gets the rewarding information of a mixnode with the provided id.
|
||||
#[cfg_attr(feature = "schema", returns(MixnodeRewardingDetailsResponse))]
|
||||
GetMixnodeRewardingDetails {
|
||||
/// Id of the node to query.
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
},
|
||||
|
||||
/// Gets the stake saturation of a mixnode with the provided id.
|
||||
#[cfg_attr(feature = "schema", returns(StakeSaturationResponse))]
|
||||
#[cfg_attr(feature = "schema", returns(MixStakeSaturationResponse))]
|
||||
GetStakeSaturation {
|
||||
/// Id of the node to query.
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
},
|
||||
|
||||
/// Gets the basic information of an unbonded mixnode with the provided id.
|
||||
#[cfg_attr(feature = "schema", returns(UnbondedMixnodeResponse))]
|
||||
GetUnbondedMixNodeInformation {
|
||||
/// Id of the node to query.
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
},
|
||||
|
||||
/// Gets the detailed mixnode information of a node given its current identity key.
|
||||
@@ -597,10 +517,6 @@ pub enum QueryMsg {
|
||||
mix_identity: IdentityKey,
|
||||
},
|
||||
|
||||
/// Gets the current layer configuration of the mix network.
|
||||
#[cfg_attr(feature = "schema", returns(LayerDistribution))]
|
||||
GetLayerDistribution {},
|
||||
|
||||
// gateway-related:
|
||||
/// Gets the basic list of all currently bonded gateways.
|
||||
#[cfg_attr(feature = "schema", returns(PagedGatewayResponse))]
|
||||
@@ -626,12 +542,129 @@ pub enum QueryMsg {
|
||||
address: String,
|
||||
},
|
||||
|
||||
// delegation-related:
|
||||
/// Gets all delegations associated with particular mixnode
|
||||
#[cfg_attr(feature = "schema", returns(PagedMixNodeDelegationsResponse))]
|
||||
GetMixnodeDelegations {
|
||||
/// Get the `NodeId`s of all the legacy gateways that they will get assigned once migrated into NymNodes
|
||||
#[cfg_attr(feature = "schema", returns(PreassignedGatewayIdsResponse))]
|
||||
GetPreassignedGatewayIds {
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<IdentityKey>,
|
||||
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
},
|
||||
|
||||
// nym-node-related:
|
||||
/// Gets the basic list of all currently bonded nymnodes.
|
||||
#[cfg_attr(feature = "schema", returns(PagedNymNodeBondsResponse))]
|
||||
GetNymNodeBondsPaged {
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
/// Gets the detailed list of all currently bonded nymnodes.
|
||||
#[cfg_attr(feature = "schema", returns(PagedNymNodeDetailsResponse))]
|
||||
GetNymNodesDetailedPaged {
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
/// Gets the basic information of an unbonded nym-node with the provided id.
|
||||
#[cfg_attr(feature = "schema", returns(UnbondedNodeResponse))]
|
||||
GetUnbondedNymNode {
|
||||
/// Id of the node to query.
|
||||
mix_id: MixId,
|
||||
node_id: NodeId,
|
||||
},
|
||||
|
||||
/// Gets the basic list of all unbonded nymnodes.
|
||||
#[cfg_attr(feature = "schema", returns(PagedUnbondedNymNodesResponse))]
|
||||
GetUnbondedNymNodesPaged {
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
/// Gets the basic list of all unbonded nymnodes that belonged to a particular owner.
|
||||
#[cfg_attr(feature = "schema", returns(PagedUnbondedNymNodesResponse))]
|
||||
GetUnbondedNymNodesByOwnerPaged {
|
||||
/// The address of the owner of the nym-node used for the query
|
||||
owner: String,
|
||||
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
/// Gets the basic list of all unbonded nymnodes that used the particular identity key.
|
||||
#[cfg_attr(feature = "schema", returns(PagedUnbondedNymNodesResponse))]
|
||||
GetUnbondedNymNodesByIdentityKeyPaged {
|
||||
/// The identity key (base58-encoded ed25519 public key) of the node used for the query.
|
||||
identity_key: IdentityKey,
|
||||
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<NodeId>,
|
||||
},
|
||||
|
||||
/// Gets the detailed nymnode information belonging to the particular owner.
|
||||
#[cfg_attr(feature = "schema", returns(NodeOwnershipResponse))]
|
||||
GetOwnedNymNode {
|
||||
/// Address of the node owner to use for the query.
|
||||
address: String,
|
||||
},
|
||||
|
||||
/// Gets the detailed nymnode information of a node with the provided id.
|
||||
#[cfg_attr(feature = "schema", returns(NodeDetailsResponse))]
|
||||
GetNymNodeDetails {
|
||||
/// Id of the node to query.
|
||||
node_id: NodeId,
|
||||
},
|
||||
|
||||
/// Gets the detailed nym-node information given its current identity key.
|
||||
#[cfg_attr(feature = "schema", returns(NodeDetailsByIdentityResponse))]
|
||||
GetNymNodeDetailsByIdentityKey {
|
||||
/// The identity key (base58-encoded ed25519 public key) of the nym-node used for the query.
|
||||
node_identity: IdentityKey,
|
||||
},
|
||||
|
||||
/// Gets the rewarding information of a nym-node with the provided id.
|
||||
#[cfg_attr(feature = "schema", returns(NodeRewardingDetailsResponse))]
|
||||
GetNodeRewardingDetails {
|
||||
/// Id of the node to query.
|
||||
node_id: NodeId,
|
||||
},
|
||||
|
||||
/// Gets the stake saturation of a nym-node with the provided id.
|
||||
#[cfg_attr(feature = "schema", returns(StakeSaturationResponse))]
|
||||
GetNodeStakeSaturation {
|
||||
/// Id of the node to query.
|
||||
node_id: NodeId,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(EpochAssignmentResponse))]
|
||||
GetRoleAssignment { role: Role },
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(RolesMetadataResponse))]
|
||||
GetRewardedSetMetadata {},
|
||||
|
||||
// delegation-related:
|
||||
/// Gets all delegations associated with particular node
|
||||
#[cfg_attr(feature = "schema", returns(PagedNodeDelegationsResponse))]
|
||||
#[serde(alias = "GetMixnodeDelegations", alias = "get_mixnode_delegations")]
|
||||
GetNodeDelegations {
|
||||
/// Id of the node to query.
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<OwnerProxySubKey>,
|
||||
@@ -649,17 +682,18 @@ pub enum QueryMsg {
|
||||
delegator: String,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<(MixId, OwnerProxySubKey)>,
|
||||
start_after: Option<(NodeId, OwnerProxySubKey)>,
|
||||
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
},
|
||||
|
||||
/// Gets delegation information associated with particular mixnode - delegator pair
|
||||
#[cfg_attr(feature = "schema", returns(MixNodeDelegationResponse))]
|
||||
#[cfg_attr(feature = "schema", returns(NodeDelegationResponse))]
|
||||
GetDelegationDetails {
|
||||
/// Id of the node to query.
|
||||
mix_id: MixId,
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
|
||||
/// The address of the owner of the delegation.
|
||||
delegator: String,
|
||||
@@ -689,9 +723,14 @@ pub enum QueryMsg {
|
||||
|
||||
/// Gets the reward amount accrued by the particular mixnode that has not yet been claimed.
|
||||
#[cfg_attr(feature = "schema", returns(PendingRewardResponse))]
|
||||
GetPendingMixNodeOperatorReward {
|
||||
#[serde(
|
||||
alias = "GetPendingMixNodeOperatorReward",
|
||||
alias = "get_pending_mix_node_operator_reward"
|
||||
)]
|
||||
GetPendingNodeOperatorReward {
|
||||
/// Id of the node to query.
|
||||
mix_id: MixId,
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
},
|
||||
|
||||
/// Gets the reward amount accrued by the particular delegator that has not yet been claimed.
|
||||
@@ -701,7 +740,8 @@ pub enum QueryMsg {
|
||||
address: String,
|
||||
|
||||
/// Id of the node to query.
|
||||
mix_id: MixId,
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
|
||||
/// Entity who made the delegation on behalf of the owner.
|
||||
/// If present, it's most likely the address of the vesting contract.
|
||||
@@ -712,10 +752,14 @@ pub enum QueryMsg {
|
||||
#[cfg_attr(feature = "schema", returns(EstimatedCurrentEpochRewardResponse))]
|
||||
GetEstimatedCurrentEpochOperatorReward {
|
||||
/// Id of the node to query.
|
||||
mix_id: MixId,
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
|
||||
/// The estimated performance for the current epoch of the given node.
|
||||
estimated_performance: Performance,
|
||||
|
||||
/// The estimated work for the current epoch of the given node.
|
||||
estimated_work: Option<WorkFactor>,
|
||||
},
|
||||
|
||||
/// Given the provided node performance, attempt to estimate the delegator reward for the current epoch.
|
||||
@@ -725,14 +769,14 @@ pub enum QueryMsg {
|
||||
address: String,
|
||||
|
||||
/// Id of the node to query.
|
||||
mix_id: MixId,
|
||||
|
||||
/// Entity who made the delegation on behalf of the owner.
|
||||
/// If present, it's most likely the address of the vesting contract.
|
||||
proxy: Option<String>,
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
|
||||
/// The estimated performance for the current epoch of the given node.
|
||||
estimated_performance: Performance,
|
||||
|
||||
/// The estimated work for the current epoch of the given node.
|
||||
estimated_work: Option<WorkFactor>,
|
||||
},
|
||||
|
||||
// interval-related
|
||||
|
||||
@@ -0,0 +1,589 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::{EpochEventId, EpochId, Gateway, IntervalEventId, MixNode, NodeId, NodeRewarding};
|
||||
use contracts_common::IdentityKey;
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, StdError, StdResult};
|
||||
use cw_storage_plus::{IntKey, Key, KeyDeserialize, PrimaryKey};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(PartialOrd, Copy, Hash, Eq)]
|
||||
#[repr(u8)]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export, export_to = "ts-packages/types/src/types/rust/Role.ts")
|
||||
)]
|
||||
pub enum Role {
|
||||
#[serde(rename = "eg", alias = "entry", alias = "entry_gateway")]
|
||||
EntryGateway = 0,
|
||||
|
||||
#[serde(rename = "l1", alias = "layer1")]
|
||||
Layer1 = 1,
|
||||
|
||||
#[serde(rename = "l2", alias = "layer2")]
|
||||
Layer2 = 2,
|
||||
|
||||
#[serde(rename = "l3", alias = "layer3")]
|
||||
Layer3 = 3,
|
||||
|
||||
#[serde(rename = "xg", alias = "exit", alias = "exit_gateway")]
|
||||
ExitGateway = 4,
|
||||
|
||||
#[serde(rename = "stb", alias = "standby")]
|
||||
Standby = 128,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Role {
|
||||
type Error = MixnetContractError;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
n if n == Role::EntryGateway as u8 => Ok(Role::EntryGateway),
|
||||
n if n == Role::Layer1 as u8 => Ok(Role::Layer1),
|
||||
n if n == Role::Layer2 as u8 => Ok(Role::Layer2),
|
||||
n if n == Role::Layer3 as u8 => Ok(Role::Layer3),
|
||||
n if n == Role::ExitGateway as u8 => Ok(Role::ExitGateway),
|
||||
n if n == Role::Standby as u8 => Ok(Role::Standby),
|
||||
n => Err(MixnetContractError::UnknownRoleRepresentation { got: n }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PrimaryKey<'a> for Role {
|
||||
type Prefix = <u8 as PrimaryKey<'a>>::Prefix;
|
||||
type SubPrefix = <u8 as PrimaryKey<'a>>::SubPrefix;
|
||||
type Suffix = <u8 as PrimaryKey<'a>>::Suffix;
|
||||
type SuperSuffix = <u8 as PrimaryKey<'a>>::SuperSuffix;
|
||||
|
||||
fn key(&self) -> Vec<Key> {
|
||||
// I'm not sure why it wasn't possible to delegate the call to
|
||||
// `(*self as u8).key()` directly...
|
||||
// I guess because of the `Key::Ref(&'a [u8])` variant?
|
||||
vec![Key::Val8((*self as u8).to_cw_bytes())]
|
||||
}
|
||||
|
||||
fn joined_key(&self) -> Vec<u8> {
|
||||
(*self as u8).joined_key()
|
||||
}
|
||||
|
||||
fn joined_extra_key(&self, key: &[u8]) -> Vec<u8> {
|
||||
(*self as u8).joined_extra_key(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyDeserialize for Role {
|
||||
type Output = Role;
|
||||
|
||||
fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
|
||||
let u8_key: <u8 as KeyDeserialize>::Output = <u8 as KeyDeserialize>::from_vec(value)?;
|
||||
Role::try_from(u8_key).map_err(|err| StdError::generic_err(err.to_string()))
|
||||
}
|
||||
|
||||
fn from_slice(value: &[u8]) -> StdResult<Self::Output> {
|
||||
let u8_key: <u8 as KeyDeserialize>::Output = <u8 as KeyDeserialize>::from_slice(value)?;
|
||||
Role::try_from(u8_key).map_err(|err| StdError::generic_err(err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Role {
|
||||
pub fn first() -> Role {
|
||||
Role::ExitGateway
|
||||
}
|
||||
|
||||
pub fn next(&self) -> Option<Self> {
|
||||
// roles have to be assigned in the following order:
|
||||
// exit -> entry -> l1 -> l2 -> l3 -> standby
|
||||
match self {
|
||||
Role::ExitGateway => Some(Role::EntryGateway),
|
||||
Role::EntryGateway => Some(Role::Layer1),
|
||||
Role::Layer1 => Some(Role::Layer2),
|
||||
Role::Layer2 => Some(Role::Layer3),
|
||||
Role::Layer3 => Some(Role::Standby),
|
||||
Role::Standby => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_first(&self) -> bool {
|
||||
self == &Role::first()
|
||||
}
|
||||
|
||||
pub fn is_standby(&self) -> bool {
|
||||
matches!(self, Role::Standby)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Role {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Role::Layer1 => write!(f, "mix layer 1"),
|
||||
Role::Layer2 => write!(f, "mix layer 2"),
|
||||
Role::Layer3 => write!(f, "mix layer 3"),
|
||||
Role::EntryGateway => write!(f, "entry gateway"),
|
||||
Role::ExitGateway => write!(f, "exit gateway"),
|
||||
Role::Standby => write!(f, "standby"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata associated with the rewarded set.
|
||||
#[cw_serde]
|
||||
#[derive(Default, Copy)]
|
||||
pub struct RewardedSetMetadata {
|
||||
/// Epoch that this data corresponds to.
|
||||
pub epoch_id: EpochId,
|
||||
|
||||
/// Indicates whether all roles got assigned to the set for this epoch.
|
||||
pub fully_assigned: bool,
|
||||
|
||||
/// Metadata for the 'EntryGateway' role
|
||||
pub entry_gateway_metadata: RoleMetadata,
|
||||
|
||||
/// Metadata for the 'ExitGateway' role
|
||||
pub exit_gateway_metadata: RoleMetadata,
|
||||
|
||||
/// Metadata for the 'Layer1' role
|
||||
pub layer1_metadata: RoleMetadata,
|
||||
|
||||
/// Metadata for the 'Layer2' role
|
||||
pub layer2_metadata: RoleMetadata,
|
||||
|
||||
/// Metadata for the 'Layer3' role
|
||||
pub layer3_metadata: RoleMetadata,
|
||||
|
||||
/// Metadata for the 'Standby' role
|
||||
pub standby_metadata: RoleMetadata,
|
||||
}
|
||||
|
||||
impl RewardedSetMetadata {
|
||||
pub fn new(epoch_id: EpochId) -> Self {
|
||||
RewardedSetMetadata {
|
||||
epoch_id,
|
||||
fully_assigned: false,
|
||||
entry_gateway_metadata: Default::default(),
|
||||
exit_gateway_metadata: Default::default(),
|
||||
layer1_metadata: Default::default(),
|
||||
layer2_metadata: Default::default(),
|
||||
layer3_metadata: Default::default(),
|
||||
standby_metadata: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_role_count(&mut self, role: Role, num_nodes: u32) {
|
||||
match role {
|
||||
Role::EntryGateway => self.entry_gateway_metadata.num_nodes = num_nodes,
|
||||
Role::Layer1 => self.layer1_metadata.num_nodes = num_nodes,
|
||||
Role::Layer2 => self.layer2_metadata.num_nodes = num_nodes,
|
||||
Role::Layer3 => self.layer3_metadata.num_nodes = num_nodes,
|
||||
Role::ExitGateway => self.exit_gateway_metadata.num_nodes = num_nodes,
|
||||
Role::Standby => self.standby_metadata.num_nodes = num_nodes,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_highest_id(&mut self, highest_id: NodeId, role: Role) {
|
||||
match role {
|
||||
Role::EntryGateway => self.entry_gateway_metadata.highest_id = highest_id,
|
||||
Role::Layer1 => self.layer1_metadata.highest_id = highest_id,
|
||||
Role::Layer2 => self.layer2_metadata.highest_id = highest_id,
|
||||
Role::Layer3 => self.layer3_metadata.highest_id = highest_id,
|
||||
Role::ExitGateway => self.exit_gateway_metadata.highest_id = highest_id,
|
||||
Role::Standby => self.standby_metadata.highest_id = highest_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highest_rewarded_id(&self) -> NodeId {
|
||||
let mut highest = 0;
|
||||
if self.layer1_metadata.highest_id > highest {
|
||||
highest = self.layer1_metadata.highest_id;
|
||||
}
|
||||
if self.layer2_metadata.highest_id > highest {
|
||||
highest = self.layer2_metadata.highest_id;
|
||||
}
|
||||
if self.layer3_metadata.highest_id > highest {
|
||||
highest = self.layer3_metadata.highest_id;
|
||||
}
|
||||
if self.entry_gateway_metadata.highest_id > highest {
|
||||
highest = self.entry_gateway_metadata.highest_id;
|
||||
}
|
||||
if self.exit_gateway_metadata.highest_id > highest {
|
||||
highest = self.exit_gateway_metadata.highest_id;
|
||||
}
|
||||
if self.standby_metadata.highest_id > highest {
|
||||
highest = self.standby_metadata.highest_id;
|
||||
}
|
||||
|
||||
highest
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata associated with particular node role.
|
||||
#[cw_serde]
|
||||
#[derive(Default, Copy)]
|
||||
pub struct RoleMetadata {
|
||||
/// Highest, also latest, node-id of a node assigned this role.
|
||||
pub highest_id: NodeId,
|
||||
|
||||
/// Number of nodes assigned this particular role.
|
||||
pub num_nodes: u32,
|
||||
}
|
||||
|
||||
/// Full details associated with given node.
|
||||
#[cw_serde]
|
||||
pub struct NymNodeDetails {
|
||||
/// Basic bond information of this node, such as owner address, original pledge, etc.
|
||||
pub bond_information: NymNodeBond,
|
||||
|
||||
/// Details used for computation of rewarding related data.
|
||||
pub rewarding_details: NodeRewarding,
|
||||
|
||||
/// Adjustments to the node that are scheduled to happen during future epoch/interval transitions.
|
||||
pub pending_changes: PendingNodeChanges,
|
||||
}
|
||||
|
||||
impl NymNodeDetails {
|
||||
pub fn new(
|
||||
bond_information: NymNodeBond,
|
||||
rewarding_details: NodeRewarding,
|
||||
pending_changes: PendingNodeChanges,
|
||||
) -> Self {
|
||||
NymNodeDetails {
|
||||
bond_information,
|
||||
rewarding_details,
|
||||
pending_changes,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_id(&self) -> NodeId {
|
||||
self.bond_information.node_id
|
||||
}
|
||||
|
||||
pub fn is_unbonding(&self) -> bool {
|
||||
self.bond_information.is_unbonding
|
||||
}
|
||||
|
||||
pub fn original_pledge(&self) -> &Coin {
|
||||
&self.bond_information.original_pledge
|
||||
}
|
||||
|
||||
pub fn pending_operator_reward(&self) -> Coin {
|
||||
let pledge = self.original_pledge();
|
||||
self.rewarding_details.pending_operator_reward(pledge)
|
||||
}
|
||||
|
||||
pub fn pending_detailed_operator_reward(&self) -> StdResult<Decimal> {
|
||||
let pledge = self.original_pledge();
|
||||
self.rewarding_details
|
||||
.pending_detailed_operator_reward(pledge)
|
||||
}
|
||||
|
||||
pub fn total_stake(&self) -> Decimal {
|
||||
self.rewarding_details.node_bond()
|
||||
}
|
||||
|
||||
pub fn pending_pledge_change(&self) -> Option<EpochEventId> {
|
||||
self.pending_changes.pledge_change
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct NymNodeBond {
|
||||
/// Unique id assigned to the bonded node.
|
||||
pub node_id: NodeId,
|
||||
|
||||
/// Address of the owner of this nym-node.
|
||||
pub owner: Addr,
|
||||
|
||||
/// Original amount pledged by the operator of this node.
|
||||
pub original_pledge: Coin,
|
||||
|
||||
/// Block height at which this nym-node has been bonded.
|
||||
pub bonding_height: u64,
|
||||
|
||||
/// Flag to indicate whether this node is in the process of unbonding,
|
||||
/// that will conclude upon the epoch finishing.
|
||||
pub is_unbonding: bool,
|
||||
|
||||
/// Information provided by the operator for the purposes of bonding.
|
||||
pub node: NymNode,
|
||||
}
|
||||
|
||||
impl NymNodeBond {
|
||||
pub fn new(
|
||||
node_id: NodeId,
|
||||
owner: Addr,
|
||||
original_pledge: Coin,
|
||||
node: impl Into<NymNode>,
|
||||
bonding_height: u64,
|
||||
) -> NymNodeBond {
|
||||
Self {
|
||||
node_id,
|
||||
owner,
|
||||
original_pledge,
|
||||
bonding_height,
|
||||
is_unbonding: false,
|
||||
node: node.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn identity(&self) -> &str {
|
||||
&self.node.identity_key
|
||||
}
|
||||
|
||||
pub fn ensure_bonded(&self) -> Result<(), MixnetContractError> {
|
||||
if self.is_unbonding {
|
||||
return Err(MixnetContractError::NodeIsUnbonding {
|
||||
node_id: self.node_id,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.
|
||||
#[cw_serde]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export, export_to = "ts-packages/types/src/types/rust/NymNode.ts")
|
||||
)]
|
||||
pub struct NymNode {
|
||||
/// Network address of this nym-node, for example 1.1.1.1 or foo.mixnode.com
|
||||
/// that is used to discover other capabilities of this node.
|
||||
pub host: String,
|
||||
|
||||
/// Allow specifying custom port for accessing the http, and thus self-described, api
|
||||
/// of this node for the capabilities discovery.
|
||||
pub custom_http_port: Option<u16>,
|
||||
|
||||
/// Base58-encoded ed25519 EdDSA public key.
|
||||
pub identity_key: IdentityKey,
|
||||
// TODO: I don't think we want to include sphinx keys here,
|
||||
// given we want to rotate them and keeping that in sync with contract will be a PITA
|
||||
}
|
||||
|
||||
impl NymNode {
|
||||
/// Perform naive validation of the attached identity key - makes sure it's correctly encoded
|
||||
/// and has 32 bytes (as expected from ed25519). we're not, however, checking if it's a valid curve point
|
||||
pub fn naive_ensure_valid_pubkey(&self) -> Result<(), MixnetContractError> {
|
||||
let decoded = bs58::decode(&self.identity_key)
|
||||
.into_vec()
|
||||
.map_err(|_| MixnetContractError::InvalidPubKey)?;
|
||||
if decoded.len() != 32 {
|
||||
return Err(MixnetContractError::InvalidPubKey);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Makes sure the provided host's length is at most 255 characters to prevent abuse.
|
||||
pub fn ensure_host_in_range(&self) -> Result<(), MixnetContractError> {
|
||||
if self.host.len() > 255 {
|
||||
return Err(MixnetContractError::HostTooLong);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MixNode> for NymNode {
|
||||
fn from(value: MixNode) -> Self {
|
||||
NymNode {
|
||||
host: value.host,
|
||||
custom_http_port: Some(value.http_api_port),
|
||||
identity_key: value.identity_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Gateway> for NymNode {
|
||||
fn from(value: Gateway) -> Self {
|
||||
NymNode {
|
||||
host: value.host,
|
||||
custom_http_port: None,
|
||||
identity_key: value.identity_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/NodeConfigUpdate.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Default)]
|
||||
pub struct NodeConfigUpdate {
|
||||
pub host: Option<String>,
|
||||
// ideally this would have been `Option<Option<u16>>`, but not sure if json would have recognised it
|
||||
pub custom_http_port: Option<u16>,
|
||||
|
||||
// equivalent to setting `custom_http_port` to `None`
|
||||
#[serde(default)]
|
||||
pub restore_default_http_port: bool,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Default, Copy)]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/PendingNodeChanges.ts"
|
||||
)
|
||||
)]
|
||||
pub struct PendingNodeChanges {
|
||||
pub pledge_change: Option<EpochEventId>,
|
||||
pub cost_params_change: Option<IntervalEventId>,
|
||||
}
|
||||
|
||||
impl PendingNodeChanges {
|
||||
pub fn new_empty() -> PendingNodeChanges {
|
||||
PendingNodeChanges {
|
||||
pledge_change: None,
|
||||
cost_params_change: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_no_pending_pledge_changes(&self) -> Result<(), MixnetContractError> {
|
||||
if let Some(pending_event_id) = self.pledge_change {
|
||||
return Err(MixnetContractError::PendingPledgeChange { pending_event_id });
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ensure_no_pending_params_changes(&self) -> Result<(), MixnetContractError> {
|
||||
if let Some(pending_event_id) = self.cost_params_change {
|
||||
return Err(MixnetContractError::PendingParamsChange { pending_event_id });
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Basic information of a node that used to be part of the nym network but has already unbonded.
|
||||
#[cw_serde]
|
||||
pub struct UnbondedNymNode {
|
||||
/// Base58-encoded ed25519 EdDSA public key.
|
||||
pub identity_key: IdentityKey,
|
||||
|
||||
/// NodeId assigned to this node.
|
||||
pub node_id: NodeId,
|
||||
|
||||
/// Address of the owner of this nym node.
|
||||
pub owner: Addr,
|
||||
|
||||
/// Block height at which this nym node has unbonded.
|
||||
pub unbonding_height: u64,
|
||||
}
|
||||
|
||||
/// Response containing rewarding information of a node with the provided id.
|
||||
#[cw_serde]
|
||||
pub struct NodeRewardingDetailsResponse {
|
||||
/// Id of the requested node.
|
||||
pub node_id: NodeId,
|
||||
|
||||
/// If there exists a node with the provided id, this field contains its rewarding information.
|
||||
pub rewarding_details: Option<NodeRewarding>,
|
||||
}
|
||||
|
||||
/// Response containing details of a node belonging to the particular owner.
|
||||
#[cw_serde]
|
||||
pub struct NodeOwnershipResponse {
|
||||
/// Validated address of the node owner.
|
||||
pub address: Addr,
|
||||
|
||||
/// If the provided address owns a nym-node, this field contains its detailed information.
|
||||
pub details: Option<NymNodeDetails>,
|
||||
}
|
||||
|
||||
/// Response containing details of a node with the provided id.
|
||||
#[cw_serde]
|
||||
pub struct NodeDetailsResponse {
|
||||
/// Id of the requested node.
|
||||
pub node_id: NodeId,
|
||||
|
||||
/// If there exists a node with the provided id, this field contains its detailed information.
|
||||
pub details: Option<NymNodeDetails>,
|
||||
}
|
||||
|
||||
/// Response containing details of a bonded node with the provided identity key.
|
||||
#[cw_serde]
|
||||
pub struct NodeDetailsByIdentityResponse {
|
||||
/// The identity key (base58-encoded ed25519 public key) of the node.
|
||||
pub identity_key: IdentityKey,
|
||||
|
||||
/// If there exists a bonded node with the provided identity key, this field contains its detailed information.
|
||||
pub details: Option<NymNodeDetails>,
|
||||
}
|
||||
|
||||
/// Response containing the current state of the stake saturation of a node with the provided id.
|
||||
#[cw_serde]
|
||||
pub struct StakeSaturationResponse {
|
||||
/// Id of the requested node.
|
||||
pub node_id: NodeId,
|
||||
|
||||
/// The current stake saturation of this node that is indirectly used in reward calculation formulas.
|
||||
/// Note that it can't be larger than 1.
|
||||
pub current_saturation: Option<Decimal>,
|
||||
|
||||
/// The current, absolute, stake saturation of this node.
|
||||
/// Note that as the name suggests it can be larger than 1.
|
||||
/// However, anything beyond that value has no effect on the total node reward.
|
||||
pub uncapped_saturation: Option<Decimal>,
|
||||
}
|
||||
|
||||
/// Response containing paged list of all nym-nodes that have ever unbonded.
|
||||
#[cw_serde]
|
||||
pub struct PagedUnbondedNymNodesResponse {
|
||||
/// Basic information of the node such as the owner or the identity key.
|
||||
pub nodes: Vec<UnbondedNymNode>,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<NodeId>,
|
||||
}
|
||||
|
||||
/// Response containing basic information of an unbonded nym-node with the provided id.
|
||||
#[cw_serde]
|
||||
pub struct UnbondedNodeResponse {
|
||||
/// Id of the requested nym-node.
|
||||
pub node_id: NodeId,
|
||||
|
||||
/// If there existed a nym-node with the provided id, this field contains its basic information.
|
||||
pub details: Option<UnbondedNymNode>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct PagedNymNodeBondsResponse {
|
||||
/// The nym node bond information present in the contract.
|
||||
pub nodes: Vec<NymNodeBond>,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<NodeId>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct PagedNymNodeDetailsResponse {
|
||||
/// All nym-node details stored in the contract.
|
||||
/// Apart from the basic bond information it also contains details required for all future reward calculation
|
||||
/// as well as any pending changes requested by the operator.
|
||||
pub nodes: Vec<NymNodeDetails>,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<NodeId>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct EpochAssignmentResponse {
|
||||
/// Epoch that this data corresponds to.
|
||||
pub epoch_id: EpochId,
|
||||
|
||||
pub nodes: Vec<NodeId>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct RolesMetadataResponse {
|
||||
pub metadata: RewardedSetMetadata,
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::mixnode::MixNodeCostParams;
|
||||
use crate::reward_params::IntervalRewardingParamsUpdate;
|
||||
use crate::{BlockHeight, MixId};
|
||||
use crate::mixnode::NodeCostParams;
|
||||
use crate::reward_params::{ActiveSetUpdate, IntervalRewardingParamsUpdate};
|
||||
use crate::{BlockHeight, NodeId};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
|
||||
@@ -35,7 +35,7 @@ pub struct PendingEpochEventData {
|
||||
pub enum PendingEpochEventKind {
|
||||
// can't just pass the `Delegation` struct here as it's impossible to determine
|
||||
// `cumulative_reward_ratio` ahead of time
|
||||
/// Request to create a delegation towards particular mixnode.
|
||||
/// Request to create a delegation towards particular node.
|
||||
/// Note that if such delegation already exists, it will get updated with the provided token amount.
|
||||
#[serde(alias = "Delegate")]
|
||||
#[non_exhaustive]
|
||||
@@ -43,8 +43,9 @@ pub enum PendingEpochEventKind {
|
||||
/// The address of the owner of the delegation.
|
||||
owner: Addr,
|
||||
|
||||
/// The id of the mixnode used for the delegation.
|
||||
mix_id: MixId,
|
||||
/// The id of the node used for the delegation.
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
|
||||
/// The amount of tokens to use for the delegation.
|
||||
amount: Coin,
|
||||
@@ -54,15 +55,16 @@ pub enum PendingEpochEventKind {
|
||||
proxy: Option<Addr>,
|
||||
},
|
||||
|
||||
/// Request to remove delegation from particular mixnode.
|
||||
/// Request to remove delegation from particular node.
|
||||
#[serde(alias = "Undelegate")]
|
||||
#[non_exhaustive]
|
||||
Undelegate {
|
||||
/// The address of the owner of the delegation.
|
||||
owner: Addr,
|
||||
|
||||
/// The id of the mixnode used for the delegation.
|
||||
mix_id: MixId,
|
||||
/// The id of the node used for the delegation.
|
||||
#[serde(alias = "mix_id")]
|
||||
node_id: NodeId,
|
||||
|
||||
/// Entity who made the delegation on behalf of the owner.
|
||||
/// If present, it's most likely the address of the vesting contract.
|
||||
@@ -70,20 +72,38 @@ pub enum PendingEpochEventKind {
|
||||
},
|
||||
|
||||
/// Request to pledge more tokens (by the node operator) towards its node.
|
||||
#[serde(alias = "PledgeMore")]
|
||||
PledgeMore {
|
||||
/// The id of the mixnode that will have its pledge updated.
|
||||
mix_id: MixId,
|
||||
NymNodePledgeMore {
|
||||
/// The id of the nym node that will have its pledge updated.
|
||||
node_id: NodeId,
|
||||
|
||||
/// The amount of additional tokens to use by the pledge.
|
||||
/// The amount of additional tokens to use in the pledge.
|
||||
amount: Coin,
|
||||
},
|
||||
|
||||
/// Request to pledge more tokens (by the node operator) towards its node.
|
||||
#[serde(alias = "PledgeMore")]
|
||||
MixnodePledgeMore {
|
||||
/// The id of the mixnode that will have its pledge updated.
|
||||
mix_id: NodeId,
|
||||
|
||||
/// The amount of additional tokens to use in the pledge.
|
||||
amount: Coin,
|
||||
},
|
||||
|
||||
/// Request to decrease amount of pledged tokens (by the node operator) from its node.
|
||||
NymNodeDecreasePledge {
|
||||
/// The id of the nym node that will have its pledge updated.
|
||||
node_id: NodeId,
|
||||
|
||||
/// The amount of tokens that should be removed from the pledge.
|
||||
decrease_by: Coin,
|
||||
},
|
||||
|
||||
/// Request to decrease amount of pledged tokens (by the node operator) from its node.
|
||||
#[serde(alias = "DecreasePledge")]
|
||||
DecreasePledge {
|
||||
MixnodeDecreasePledge {
|
||||
/// The id of the mixnode that will have its pledge updated.
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
|
||||
/// The amount of tokens that should be removed from the pledge.
|
||||
decrease_by: Coin,
|
||||
@@ -93,15 +113,17 @@ pub enum PendingEpochEventKind {
|
||||
#[serde(alias = "UnbondMixnode")]
|
||||
UnbondMixnode {
|
||||
/// The id of the mixnode that will get unbonded.
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
},
|
||||
|
||||
/// Request to update the current size of the active set.
|
||||
#[serde(alias = "UpdateActiveSetSize")]
|
||||
UpdateActiveSetSize {
|
||||
/// The new desired size of the active set.
|
||||
new_size: u32,
|
||||
/// Request to unbond a nym node and completely remove it from the network.
|
||||
UnbondNymNode {
|
||||
/// The id of the node that will get unbonded.
|
||||
node_id: NodeId,
|
||||
},
|
||||
|
||||
/// Request to update the current active set.
|
||||
UpdateActiveSet { update: ActiveSetUpdate },
|
||||
}
|
||||
|
||||
impl PendingEpochEventKind {
|
||||
@@ -112,19 +134,19 @@ impl PendingEpochEventKind {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_delegate(owner: Addr, mix_id: MixId, amount: Coin) -> Self {
|
||||
pub fn new_delegate(owner: Addr, node_id: NodeId, amount: Coin) -> Self {
|
||||
PendingEpochEventKind::Delegate {
|
||||
owner,
|
||||
mix_id,
|
||||
node_id,
|
||||
amount,
|
||||
proxy: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_undelegate(owner: Addr, mix_id: MixId) -> Self {
|
||||
pub fn new_undelegate(owner: Addr, node_id: NodeId) -> Self {
|
||||
PendingEpochEventKind::Undelegate {
|
||||
owner,
|
||||
mix_id,
|
||||
node_id,
|
||||
proxy: None,
|
||||
}
|
||||
}
|
||||
@@ -166,10 +188,19 @@ pub enum PendingIntervalEventKind {
|
||||
#[serde(alias = "ChangeMixCostParams")]
|
||||
ChangeMixCostParams {
|
||||
/// The id of the mixnode that will have its cost parameters updated.
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
|
||||
/// The new updated cost function of this mixnode.
|
||||
new_costs: MixNodeCostParams,
|
||||
new_costs: NodeCostParams,
|
||||
},
|
||||
|
||||
/// Request to update cost parameters of given nym node.
|
||||
ChangeNymNodeCostParams {
|
||||
/// The id of the nym node that will have its cost parameters updated.
|
||||
node_id: NodeId,
|
||||
|
||||
/// The new updated cost function of this nym node.
|
||||
new_costs: NodeCostParams,
|
||||
},
|
||||
|
||||
/// Request to update the underlying rewarding parameters used by the system
|
||||
|
||||
@@ -2,17 +2,22 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::helpers::IntoBaseDecimal;
|
||||
use crate::nym_node::Role;
|
||||
use crate::{error::MixnetContractError, Percent};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Decimal;
|
||||
|
||||
pub type Performance = Percent;
|
||||
pub type WorkFactor = Decimal;
|
||||
|
||||
/// Parameters required by the mix-mining reward distribution that do not change during an interval.
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/IntervalRewardParams.ts")
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/IntervalRewardParams.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
@@ -78,7 +83,10 @@ impl IntervalRewardParams {
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/RewardingParams.ts")
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/RewardingParams.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
@@ -86,23 +94,15 @@ pub struct RewardingParams {
|
||||
/// Parameters that should remain unchanged throughout an interval.
|
||||
pub interval: IntervalRewardParams,
|
||||
|
||||
// while the rewarded set size can change between epochs to accommodate for bandwidth demands,
|
||||
// the active set size should be unchanged between epochs and should only be adjusted between
|
||||
// intervals. However, it makes more sense to keep both of those values together as they're
|
||||
// very strongly related to each other.
|
||||
/// The expected number of mixnodes in the rewarded set (i.e. active + standby).
|
||||
pub rewarded_set_size: u32,
|
||||
|
||||
/// The expected number of mixnodes in the active set.
|
||||
pub active_set_size: u32,
|
||||
pub rewarded_set: RewardedSetParams,
|
||||
}
|
||||
|
||||
impl RewardingParams {
|
||||
pub fn active_node_work(&self) -> Decimal {
|
||||
pub fn active_node_work(&self) -> WorkFactor {
|
||||
self.interval.active_set_work_factor * self.standby_node_work()
|
||||
}
|
||||
|
||||
pub fn standby_node_work(&self) -> Decimal {
|
||||
pub fn standby_node_work(&self) -> WorkFactor {
|
||||
let f = self.interval.active_set_work_factor;
|
||||
let k = self.dec_rewarded_set_size();
|
||||
let one = Decimal::one();
|
||||
@@ -113,27 +113,33 @@ impl RewardingParams {
|
||||
one / (f * k - (f - one) * k_r)
|
||||
}
|
||||
|
||||
pub fn rewarded_set_size(&self) -> u32 {
|
||||
self.rewarded_set.rewarded_set_size()
|
||||
}
|
||||
|
||||
pub fn active_set_size(&self) -> u32 {
|
||||
self.rewarded_set.active_set_size()
|
||||
}
|
||||
|
||||
pub fn dec_rewarded_set_size(&self) -> Decimal {
|
||||
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
||||
// with 0 decimal places
|
||||
#[allow(clippy::unwrap_used)]
|
||||
self.rewarded_set_size.into_base_decimal().unwrap()
|
||||
self.rewarded_set_size().into_base_decimal().unwrap()
|
||||
}
|
||||
|
||||
pub fn dec_active_set_size(&self) -> Decimal {
|
||||
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
||||
// with 0 decimal places
|
||||
#[allow(clippy::unwrap_used)]
|
||||
self.active_set_size.into_base_decimal().unwrap()
|
||||
self.active_set_size().into_base_decimal().unwrap()
|
||||
}
|
||||
|
||||
fn dec_standby_set_size(&self) -> Decimal {
|
||||
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
||||
// with 0 decimal places
|
||||
#[allow(clippy::unwrap_used)]
|
||||
(self.rewarded_set_size - self.active_set_size)
|
||||
.into_base_decimal()
|
||||
.unwrap()
|
||||
self.rewarded_set.standby.into_base_decimal().unwrap()
|
||||
}
|
||||
|
||||
pub fn apply_epochs_in_interval_change(&mut self, new_epochs_in_interval: u32) {
|
||||
@@ -146,19 +152,35 @@ impl RewardingParams {
|
||||
* self.interval.interval_pool_emission;
|
||||
}
|
||||
|
||||
pub fn try_change_active_set_size(
|
||||
&mut self,
|
||||
new_active_set_size: u32,
|
||||
pub fn validate_active_set_update(
|
||||
&self,
|
||||
update: ActiveSetUpdate,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
if new_active_set_size == 0 {
|
||||
return Err(MixnetContractError::ZeroActiveSet);
|
||||
}
|
||||
update.ensure_non_empty()?;
|
||||
let active_set_size = update.active_set_size();
|
||||
|
||||
if new_active_set_size > self.rewarded_set_size {
|
||||
if active_set_size > self.rewarded_set_size() {
|
||||
return Err(MixnetContractError::InvalidActiveSetSize);
|
||||
}
|
||||
|
||||
self.active_set_size = new_active_set_size;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn try_change_active_set(
|
||||
&mut self,
|
||||
update: ActiveSetUpdate,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
self.validate_active_set_update(update)?;
|
||||
let active_set_size = update.active_set_size();
|
||||
let rewarded_set_size = self.rewarded_set_size();
|
||||
|
||||
// safety: due to validation we know that the active_set_size <= rewarded_set_size
|
||||
let new_standby = rewarded_set_size - active_set_size;
|
||||
|
||||
self.rewarded_set.exit_gateways = update.exit_gateways;
|
||||
self.rewarded_set.entry_gateways = update.entry_gateways;
|
||||
self.rewarded_set.mixnodes = update.mixnodes;
|
||||
self.rewarded_set.standby = new_standby;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -201,16 +223,10 @@ impl RewardingParams {
|
||||
self.interval.interval_pool_emission = interval_pool_emission;
|
||||
}
|
||||
|
||||
if let Some(rewarded_set_size) = updates.rewarded_set_size {
|
||||
if rewarded_set_size == 0 {
|
||||
return Err(MixnetContractError::ZeroRewardedSet);
|
||||
}
|
||||
if rewarded_set_size < self.active_set_size {
|
||||
return Err(MixnetContractError::InvalidRewardedSetSize);
|
||||
}
|
||||
|
||||
if let Some(rewarded_set_update) = updates.rewarded_set_params {
|
||||
rewarded_set_update.ensure_valid()?;
|
||||
recompute_saturation_point = true;
|
||||
self.rewarded_set_size = rewarded_set_size;
|
||||
self.rewarded_set = rewarded_set_update;
|
||||
}
|
||||
|
||||
if recompute_epoch_budget {
|
||||
@@ -221,31 +237,112 @@ impl RewardingParams {
|
||||
|
||||
if recompute_saturation_point {
|
||||
self.interval.stake_saturation_point =
|
||||
self.interval.staking_supply / self.rewarded_set_size.into_base_decimal()?
|
||||
self.interval.staking_supply / self.rewarded_set_size().into_base_decimal()?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: possibly refactor this
|
||||
/// Parameters used for rewarding particular mixnode.
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/RewardedSetParams.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct NodeRewardParams {
|
||||
/// Performance of the particular node in the current epoch.
|
||||
pub performance: Percent,
|
||||
pub struct RewardedSetParams {
|
||||
/// The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`])
|
||||
pub entry_gateways: u32,
|
||||
|
||||
/// Flag indicating whether the node has been in the active set during the epoch.
|
||||
pub in_active_set: bool,
|
||||
/// The expected number of nodes assigned exit gateway role (i.e. [`Role::ExitGateway`])
|
||||
pub exit_gateways: u32,
|
||||
|
||||
/// The expected number of nodes assigned the 'mixnode' role, i.e. total of [`Role::Layer1`], [`Role::Layer2`] and [`Role::Layer3`].
|
||||
pub mixnodes: u32,
|
||||
|
||||
/// Number of nodes in the 'standby' set. (i.e. [`Role::Standby`])
|
||||
pub standby: u32,
|
||||
}
|
||||
|
||||
impl NodeRewardParams {
|
||||
pub fn new(performance: Percent, in_active_set: bool) -> Self {
|
||||
NodeRewardParams {
|
||||
performance,
|
||||
in_active_set,
|
||||
impl RewardedSetParams {
|
||||
pub fn active_set_size(&self) -> u32 {
|
||||
self.entry_gateways + self.exit_gateways + self.mixnodes
|
||||
}
|
||||
|
||||
pub fn rewarded_set_size(&self) -> u32 {
|
||||
self.active_set_size() + self.standby
|
||||
}
|
||||
|
||||
pub fn ensure_valid(&self) -> Result<(), MixnetContractError> {
|
||||
if self.entry_gateways == 0 || self.exit_gateways == 0 || self.mixnodes == 0 {
|
||||
return Err(MixnetContractError::EmptyRoleAssignment);
|
||||
}
|
||||
if self.mixnodes % 3 != 0 {
|
||||
return Err(MixnetContractError::UnevenLayerAssignment);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn maximum_role_count(&self, role: Role) -> u32 {
|
||||
match role {
|
||||
Role::EntryGateway => self.entry_gateways,
|
||||
Role::Layer1 | Role::Layer2 | Role::Layer3 => self.mixnodes / 3,
|
||||
Role::ExitGateway => self.exit_gateways,
|
||||
Role::Standby => self.standby,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_role_count(&self, role: Role, assigned: u32) -> Result<(), MixnetContractError> {
|
||||
let allowed = self.maximum_role_count(role);
|
||||
|
||||
if assigned > allowed {
|
||||
return Err(MixnetContractError::IllegalRoleCount {
|
||||
role,
|
||||
assigned,
|
||||
allowed,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters used for rewarding particular node.
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/NodeRewardingParameters.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct NodeRewardingParameters {
|
||||
/// Performance of the particular node in the current epoch.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
pub performance: Performance,
|
||||
|
||||
/// Amount of work performed by this node in the current epoch
|
||||
/// also known as 'omega' in the paper
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
pub work_factor: WorkFactor,
|
||||
}
|
||||
|
||||
impl NodeRewardingParameters {
|
||||
pub fn new(performance: Performance, work_factor: WorkFactor) -> Self {
|
||||
NodeRewardingParameters {
|
||||
performance,
|
||||
work_factor,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.performance.is_zero() || self.work_factor.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +350,10 @@ impl NodeRewardParams {
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/IntervalRewardingParamsUpdate.ts")
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/IntervalRewardingParamsUpdate.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy, Default)]
|
||||
@@ -282,8 +382,8 @@ pub struct IntervalRewardingParamsUpdate {
|
||||
/// Defines the new value of the interval pool emission rate.
|
||||
pub interval_pool_emission: Option<Percent>,
|
||||
|
||||
/// Defines the new size of the rewarded set.
|
||||
pub rewarded_set_size: Option<u32>,
|
||||
/// Defines the parameters of the rewarded set.
|
||||
pub rewarded_set_params: Option<RewardedSetParams>,
|
||||
}
|
||||
|
||||
impl IntervalRewardingParamsUpdate {
|
||||
@@ -295,10 +395,45 @@ impl IntervalRewardingParamsUpdate {
|
||||
|| self.sybil_resistance_percent.is_some()
|
||||
|| self.active_set_work_factor.is_some()
|
||||
|| self.interval_pool_emission.is_some()
|
||||
|| self.rewarded_set_size.is_some()
|
||||
|| self.rewarded_set_params.is_some()
|
||||
}
|
||||
|
||||
pub fn to_inline_json(&self) -> String {
|
||||
serde_json_wasm::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Specification on how the active set should be updated.
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/ActiveSetUpdate.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy, Default)]
|
||||
pub struct ActiveSetUpdate {
|
||||
/// The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`])
|
||||
pub entry_gateways: u32,
|
||||
|
||||
/// The expected number of nodes assigned exit gateway role (i.e. [`Role::ExitGateway`])
|
||||
pub exit_gateways: u32,
|
||||
|
||||
/// The expected number of nodes assigned the 'mixnode' role, i.e. total of [`Role::Layer1`], [`Role::Layer2`] and [`Role::Layer3`].
|
||||
pub mixnodes: u32,
|
||||
}
|
||||
|
||||
impl ActiveSetUpdate {
|
||||
pub fn active_set_size(&self) -> u32 {
|
||||
self.entry_gateways + self.exit_gateways + self.mixnodes
|
||||
}
|
||||
|
||||
pub fn ensure_non_empty(&self) -> Result<(), MixnetContractError> {
|
||||
if self.entry_gateways == 0 || self.exit_gateways == 0 || self.mixnodes == 0 {
|
||||
return Err(MixnetContractError::EmptyRoleAssignment);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{MixId, RewardedSetNodeStatus};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Coin, Decimal};
|
||||
|
||||
@@ -11,7 +10,10 @@ pub mod simulator;
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export_to = "ts-packages/types/src/types/rust/RewardEstimate.ts")
|
||||
ts(
|
||||
export,
|
||||
export_to = "ts-packages/types/src/types/rust/RewardEstimate.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy, Default)]
|
||||
@@ -63,7 +65,10 @@ pub struct PendingRewardResponse {
|
||||
|
||||
/// The associated mixnode is still fully bonded, meaning it is neither unbonded
|
||||
/// nor in the process of unbonding that would have finished at the epoch transition.
|
||||
#[deprecated(note = "this field will be removed. use .node_still_fully_bonded instead")]
|
||||
pub mixnode_still_fully_bonded: bool,
|
||||
|
||||
pub node_still_fully_bonded: bool,
|
||||
}
|
||||
|
||||
/// Response containing estimation of node rewards for the current epoch.
|
||||
@@ -99,13 +104,3 @@ impl EstimatedCurrentEpochRewardResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Response containing paged list of all mixnodes in the rewarded set.
|
||||
#[cw_serde]
|
||||
pub struct PagedRewardedSetResponse {
|
||||
/// Nodes in the current rewarded set.
|
||||
pub nodes: Vec<(MixId, RewardedSetNodeStatus)>,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<MixId>,
|
||||
}
|
||||
|
||||
@@ -3,23 +3,21 @@
|
||||
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::helpers::IntoBaseDecimal;
|
||||
use crate::reward_params::NodeRewardParams;
|
||||
use crate::reward_params::{NodeRewardingParameters, WorkFactor};
|
||||
use crate::rewarding::simulator::simulated_node::SimulatedNode;
|
||||
use crate::rewarding::RewardDistribution;
|
||||
use crate::{
|
||||
Delegation, Interval, IntervalRewardParams, MixId, MixNodeCostParams, RewardingParams,
|
||||
};
|
||||
use crate::{Delegation, Interval, IntervalRewardParams, NodeCostParams, NodeId, RewardingParams};
|
||||
use cosmwasm_std::{Coin, Decimal};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub mod simulated_node;
|
||||
|
||||
pub struct Simulator {
|
||||
pub nodes: BTreeMap<MixId, SimulatedNode>,
|
||||
pub nodes: BTreeMap<NodeId, SimulatedNode>,
|
||||
pub system_rewarding_params: RewardingParams,
|
||||
pub interval: Interval,
|
||||
|
||||
next_mix_id: MixId,
|
||||
next_mix_id: NodeId,
|
||||
pending_reward_pool_emission: Decimal,
|
||||
}
|
||||
|
||||
@@ -34,6 +32,14 @@ impl Simulator {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn legacy_standby_work_factor(&self) -> WorkFactor {
|
||||
self.system_rewarding_params.standby_node_work()
|
||||
}
|
||||
|
||||
pub fn legacy_active_work_factor(&self) -> WorkFactor {
|
||||
self.system_rewarding_params.active_node_work()
|
||||
}
|
||||
|
||||
fn advance_epoch(&mut self) -> Result<(), MixnetContractError> {
|
||||
let updated = self.interval.advance_epoch();
|
||||
|
||||
@@ -53,7 +59,7 @@ impl Simulator {
|
||||
let stake_saturation_point = staking_supply
|
||||
/ self
|
||||
.system_rewarding_params
|
||||
.rewarded_set_size
|
||||
.rewarded_set_size()
|
||||
.into_base_decimal()?;
|
||||
|
||||
let updated_params = RewardingParams {
|
||||
@@ -67,8 +73,7 @@ impl Simulator {
|
||||
active_set_work_factor: old.active_set_work_factor,
|
||||
interval_pool_emission: old.interval_pool_emission,
|
||||
},
|
||||
rewarded_set_size: self.system_rewarding_params.rewarded_set_size,
|
||||
active_set_size: self.system_rewarding_params.active_set_size,
|
||||
rewarded_set: self.system_rewarding_params.rewarded_set,
|
||||
};
|
||||
|
||||
self.system_rewarding_params = updated_params;
|
||||
@@ -82,8 +87,8 @@ impl Simulator {
|
||||
pub fn bond(
|
||||
&mut self,
|
||||
pledge: Coin,
|
||||
cost_params: MixNodeCostParams,
|
||||
) -> Result<MixId, MixnetContractError> {
|
||||
cost_params: NodeCostParams,
|
||||
) -> Result<NodeId, MixnetContractError> {
|
||||
let mix_id = self.next_mix_id;
|
||||
|
||||
self.nodes.insert(
|
||||
@@ -105,7 +110,7 @@ impl Simulator {
|
||||
&mut self,
|
||||
delegator: S,
|
||||
delegation: Coin,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
let node = self
|
||||
.nodes
|
||||
@@ -119,7 +124,7 @@ impl Simulator {
|
||||
pub fn undelegate<S: Into<String>>(
|
||||
&mut self,
|
||||
delegator: S,
|
||||
mix_id: MixId,
|
||||
mix_id: NodeId,
|
||||
) -> Result<(Coin, Coin), MixnetContractError> {
|
||||
let node = self
|
||||
.nodes
|
||||
@@ -130,7 +135,7 @@ impl Simulator {
|
||||
|
||||
pub fn simulate_epoch_single_node(
|
||||
&mut self,
|
||||
params: NodeRewardParams,
|
||||
params: NodeRewardingParameters,
|
||||
) -> Result<RewardDistribution, MixnetContractError> {
|
||||
assert_eq!(self.nodes.len(), 1);
|
||||
|
||||
@@ -148,8 +153,8 @@ impl Simulator {
|
||||
|
||||
pub fn simulate_epoch(
|
||||
&mut self,
|
||||
node_params: &BTreeMap<MixId, NodeRewardParams>,
|
||||
) -> Result<BTreeMap<MixId, RewardDistribution>, MixnetContractError> {
|
||||
node_params: &BTreeMap<NodeId, NodeRewardingParameters>,
|
||||
) -> Result<BTreeMap<NodeId, RewardDistribution>, MixnetContractError> {
|
||||
let mut params_keys = node_params.keys().copied().collect::<Vec<_>>();
|
||||
params_keys.sort_unstable();
|
||||
let mut node_keys = self.nodes.keys().copied().collect::<Vec<_>>();
|
||||
@@ -185,7 +190,7 @@ impl Simulator {
|
||||
&self,
|
||||
delegation: &Delegation,
|
||||
) -> Result<Decimal, MixnetContractError> {
|
||||
Ok(self.nodes[&delegation.mix_id]
|
||||
Ok(self.nodes[&delegation.node_id]
|
||||
.rewarding_details
|
||||
.determine_delegation_reward(delegation)?)
|
||||
}
|
||||
@@ -206,7 +211,7 @@ impl Simulator {
|
||||
// assume node state doesn't change in the interval (kinda unrealistic)
|
||||
pub fn simulate_full_interval(
|
||||
&mut self,
|
||||
node_params: &BTreeMap<MixId, NodeRewardParams>,
|
||||
node_params: &BTreeMap<NodeId, NodeRewardingParameters>,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
for _ in 0..self.interval.epochs_in_interval() {
|
||||
self.simulate_epoch(node_params)?;
|
||||
@@ -219,6 +224,7 @@ impl Simulator {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::helpers::compare_decimals;
|
||||
use crate::reward_params::RewardedSetParams;
|
||||
use crate::Percent;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use std::time::Duration;
|
||||
@@ -226,6 +232,7 @@ mod tests {
|
||||
#[cfg(test)]
|
||||
mod single_node_case {
|
||||
use super::*;
|
||||
use crate::reward_params::RewardedSetParams;
|
||||
use crate::rewarding::helpers::truncate_reward_amount;
|
||||
use cosmwasm_std::coin;
|
||||
|
||||
@@ -237,15 +244,23 @@ mod tests {
|
||||
let profit_margin = Percent::from_percentage_value(10).unwrap();
|
||||
let interval_operating_cost = Coin::new(40_000_000, "unym");
|
||||
let epochs_in_interval = 720u32;
|
||||
let rewarded_set_size = 240;
|
||||
let active_set_size = 100;
|
||||
let interval_pool_emission = Percent::from_percentage_value(2).unwrap();
|
||||
|
||||
// the import values here are active set being 100 and rewarded set being 240
|
||||
// since those are the values we were using in the past
|
||||
let rewarded_set = RewardedSetParams {
|
||||
entry_gateways: 20,
|
||||
exit_gateways: 50,
|
||||
mixnodes: 30,
|
||||
standby: 140,
|
||||
};
|
||||
|
||||
let reward_pool = 250_000_000_000_000u128;
|
||||
let staking_supply = 100_000_000_000_000u128;
|
||||
let epoch_reward_budget =
|
||||
interval_pool_emission * Decimal::from_ratio(reward_pool, epochs_in_interval);
|
||||
let stake_saturation_point = Decimal::from_ratio(staking_supply, rewarded_set_size);
|
||||
let stake_saturation_point =
|
||||
Decimal::from_ratio(staking_supply, rewarded_set.rewarded_set_size());
|
||||
|
||||
let rewarding_params = RewardingParams {
|
||||
interval: IntervalRewardParams {
|
||||
@@ -258,8 +273,7 @@ mod tests {
|
||||
active_set_work_factor: Decimal::percent(1000), // value '10'
|
||||
interval_pool_emission,
|
||||
},
|
||||
rewarded_set_size,
|
||||
active_set_size,
|
||||
rewarded_set,
|
||||
};
|
||||
|
||||
let interval = Interval::init_interval(
|
||||
@@ -270,7 +284,7 @@ mod tests {
|
||||
let initial_pledge = Coin::new(initial_pledge, "unym");
|
||||
let mut simulator = Simulator::new(rewarding_params, interval);
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
let cost_params = NodeCostParams {
|
||||
profit_margin_percent: profit_margin,
|
||||
interval_operating_cost,
|
||||
};
|
||||
@@ -315,8 +329,10 @@ mod tests {
|
||||
fn simulator_returns_expected_values_for_base_case() {
|
||||
let mut simulator = base_simulator(10000_000000);
|
||||
|
||||
let epoch_params =
|
||||
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
||||
let epoch_params = NodeRewardingParameters::new(
|
||||
Percent::from_percentage_value(100).unwrap(),
|
||||
simulator.legacy_active_work_factor(),
|
||||
);
|
||||
let rewards = simulator.simulate_epoch_single_node(epoch_params).unwrap();
|
||||
|
||||
assert_eq!(rewards.delegates, Decimal::zero());
|
||||
@@ -334,8 +350,10 @@ mod tests {
|
||||
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
|
||||
.unwrap();
|
||||
|
||||
let node_params =
|
||||
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
||||
let node_params = NodeRewardingParameters::new(
|
||||
Percent::from_percentage_value(100).unwrap(),
|
||||
simulator.legacy_active_work_factor(),
|
||||
);
|
||||
let rewards = simulator.simulate_epoch_single_node(node_params).unwrap();
|
||||
|
||||
compare_decimals(
|
||||
@@ -364,8 +382,10 @@ mod tests {
|
||||
#[test]
|
||||
fn delegation_and_undelegation() {
|
||||
let mut simulator = base_simulator(10000_000000);
|
||||
let node_params =
|
||||
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
||||
let node_params = NodeRewardingParameters::new(
|
||||
Percent::from_percentage_value(100).unwrap(),
|
||||
simulator.legacy_active_work_factor(),
|
||||
);
|
||||
|
||||
let rewards1 = simulator.simulate_epoch_single_node(node_params).unwrap();
|
||||
let expected_operator1 = "1128452.5416104363".parse().unwrap();
|
||||
@@ -411,8 +431,10 @@ mod tests {
|
||||
// essentially all delegators' rewards (and the operator itself) are still correctly computed
|
||||
let original_pledge = coin(10000_000000, "unym");
|
||||
let mut simulator = base_simulator(original_pledge.amount.u128());
|
||||
let node_params =
|
||||
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
||||
let node_params = NodeRewardingParameters::new(
|
||||
Percent::from_percentage_value(100).unwrap(),
|
||||
simulator.legacy_active_work_factor(),
|
||||
);
|
||||
|
||||
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
|
||||
// delegating at different times still work)
|
||||
@@ -454,8 +476,10 @@ mod tests {
|
||||
fn withdrawing_delegator_reward() {
|
||||
// essentially all delegators' rewards (and the operator itself) are still correctly computed
|
||||
let mut simulator = base_simulator(10000_000000);
|
||||
let node_params =
|
||||
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
||||
let node_params = NodeRewardingParameters::new(
|
||||
Percent::from_percentage_value(100).unwrap(),
|
||||
simulator.legacy_active_work_factor(),
|
||||
);
|
||||
|
||||
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
|
||||
// delegating at different times still work)
|
||||
@@ -524,7 +548,7 @@ mod tests {
|
||||
fn simulating_multiple_epochs() {
|
||||
let mut simulator = base_simulator(10000_000000);
|
||||
|
||||
let mut is_active = true;
|
||||
let mut work_factor = simulator.legacy_active_work_factor();
|
||||
let mut performance = Percent::from_percentage_value(100).unwrap();
|
||||
for epoch in 0..720 {
|
||||
if epoch == 0 {
|
||||
@@ -538,7 +562,7 @@ mod tests {
|
||||
.unwrap()
|
||||
}
|
||||
if epoch == 89 {
|
||||
is_active = false;
|
||||
work_factor = simulator.legacy_standby_work_factor();
|
||||
}
|
||||
if epoch == 123 {
|
||||
simulator
|
||||
@@ -560,7 +584,7 @@ mod tests {
|
||||
// TODO: figure out if there's a good way to verify whether `reward` is what we expect it to be
|
||||
}
|
||||
if epoch == 345 {
|
||||
is_active = true;
|
||||
work_factor = simulator.legacy_active_work_factor();
|
||||
}
|
||||
if epoch == 358 {
|
||||
performance = Percent::from_percentage_value(100).unwrap();
|
||||
@@ -579,7 +603,7 @@ mod tests {
|
||||
|
||||
// this has to always hold
|
||||
check_rewarding_invariant(&simulator);
|
||||
let node_params = NodeRewardParams::new(performance, is_active);
|
||||
let node_params = NodeRewardingParameters::new(performance, work_factor);
|
||||
simulator.simulate_epoch_single_node(node_params).unwrap();
|
||||
}
|
||||
|
||||
@@ -600,15 +624,23 @@ mod tests {
|
||||
// rather than just checking the final results
|
||||
|
||||
let epochs_in_interval = 1u32;
|
||||
let rewarded_set_size = 10;
|
||||
let active_set_size = 6;
|
||||
let interval_pool_emission = Percent::from_percentage_value(2).unwrap();
|
||||
|
||||
// the import values here are active set being 6 and rewarded set being 10
|
||||
// since those are the values we were using in the past
|
||||
let rewarded_set = RewardedSetParams {
|
||||
entry_gateways: 1,
|
||||
exit_gateways: 2,
|
||||
mixnodes: 3,
|
||||
standby: 4,
|
||||
};
|
||||
|
||||
let reward_pool = 250_000_000_000_000u128;
|
||||
let staking_supply = 100_000_000_000_000u128;
|
||||
let epoch_reward_budget =
|
||||
interval_pool_emission * Decimal::from_ratio(reward_pool, epochs_in_interval);
|
||||
let stake_saturation_point = Decimal::from_ratio(staking_supply, rewarded_set_size);
|
||||
let stake_saturation_point =
|
||||
Decimal::from_ratio(staking_supply, rewarded_set.rewarded_set_size());
|
||||
|
||||
let rewarding_params = RewardingParams {
|
||||
interval: IntervalRewardParams {
|
||||
@@ -621,8 +653,7 @@ mod tests {
|
||||
active_set_work_factor: Decimal::percent(1000), // value '10'
|
||||
interval_pool_emission,
|
||||
},
|
||||
rewarded_set_size,
|
||||
active_set_size,
|
||||
rewarded_set,
|
||||
};
|
||||
|
||||
let interval = Interval::init_interval(
|
||||
@@ -636,7 +667,7 @@ mod tests {
|
||||
let n0 = simulator
|
||||
.bond(
|
||||
Coin::new(11_000_000_000000, "unym"),
|
||||
MixNodeCostParams {
|
||||
NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||
},
|
||||
@@ -649,7 +680,7 @@ mod tests {
|
||||
let n1 = simulator
|
||||
.bond(
|
||||
Coin::new(1_000_000_000000, "unym"),
|
||||
MixNodeCostParams {
|
||||
NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||
},
|
||||
@@ -662,7 +693,7 @@ mod tests {
|
||||
let n2 = simulator
|
||||
.bond(
|
||||
Coin::new(1_000_000_000000, "unym"),
|
||||
MixNodeCostParams {
|
||||
NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||
},
|
||||
@@ -675,7 +706,7 @@ mod tests {
|
||||
let n3 = simulator
|
||||
.bond(
|
||||
Coin::new(1_000_000_000000, "unym"),
|
||||
MixNodeCostParams {
|
||||
NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
|
||||
interval_operating_cost: Coin::new(500_000_000, "unym"),
|
||||
},
|
||||
@@ -688,7 +719,7 @@ mod tests {
|
||||
let n4 = simulator
|
||||
.bond(
|
||||
Coin::new(1000_000000, "unym"),
|
||||
MixNodeCostParams {
|
||||
NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||
},
|
||||
@@ -701,7 +732,7 @@ mod tests {
|
||||
let n5 = simulator
|
||||
.bond(
|
||||
Coin::new(1_000_000_000000, "unym"),
|
||||
MixNodeCostParams {
|
||||
NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||
},
|
||||
@@ -714,7 +745,7 @@ mod tests {
|
||||
let n6 = simulator
|
||||
.bond(
|
||||
Coin::new(11_000_000_000000, "unym"),
|
||||
MixNodeCostParams {
|
||||
NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||
},
|
||||
@@ -727,7 +758,7 @@ mod tests {
|
||||
let n7 = simulator
|
||||
.bond(
|
||||
Coin::new(1_000_000_000000, "unym"),
|
||||
MixNodeCostParams {
|
||||
NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||
},
|
||||
@@ -740,7 +771,7 @@ mod tests {
|
||||
let n8 = simulator
|
||||
.bond(
|
||||
Coin::new(1_000_000_000000, "unym"),
|
||||
MixNodeCostParams {
|
||||
NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
|
||||
interval_operating_cost: Coin::new(500_000_000, "unym"),
|
||||
},
|
||||
@@ -753,7 +784,7 @@ mod tests {
|
||||
let n9 = simulator
|
||||
.bond(
|
||||
Coin::new(1_000_000_000000, "unym"),
|
||||
MixNodeCostParams {
|
||||
NodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||
},
|
||||
@@ -767,17 +798,20 @@ mod tests {
|
||||
let uptime_09 = Percent::from_percentage_value(90).unwrap();
|
||||
let uptime_0 = Percent::from_percentage_value(0).unwrap();
|
||||
|
||||
let active_work = simulator.legacy_active_work_factor();
|
||||
let standby_work = simulator.legacy_standby_work_factor();
|
||||
|
||||
let node_params = [
|
||||
(n0, NodeRewardParams::new(uptime_1, true)),
|
||||
(n1, NodeRewardParams::new(uptime_1, true)),
|
||||
(n2, NodeRewardParams::new(uptime_1, true)),
|
||||
(n3, NodeRewardParams::new(uptime_09, true)),
|
||||
(n4, NodeRewardParams::new(uptime_09, true)),
|
||||
(n5, NodeRewardParams::new(uptime_0, true)),
|
||||
(n6, NodeRewardParams::new(uptime_1, false)),
|
||||
(n7, NodeRewardParams::new(uptime_1, false)),
|
||||
(n8, NodeRewardParams::new(uptime_09, false)),
|
||||
(n9, NodeRewardParams::new(uptime_0, false)),
|
||||
(n0, NodeRewardingParameters::new(uptime_1, active_work)),
|
||||
(n1, NodeRewardingParameters::new(uptime_1, active_work)),
|
||||
(n2, NodeRewardingParameters::new(uptime_1, active_work)),
|
||||
(n3, NodeRewardingParameters::new(uptime_09, active_work)),
|
||||
(n4, NodeRewardingParameters::new(uptime_09, active_work)),
|
||||
(n5, NodeRewardingParameters::new(uptime_0, active_work)),
|
||||
(n6, NodeRewardingParameters::new(uptime_1, standby_work)),
|
||||
(n7, NodeRewardingParameters::new(uptime_1, standby_work)),
|
||||
(n8, NodeRewardingParameters::new(uptime_09, standby_work)),
|
||||
(n9, NodeRewardingParameters::new(uptime_0, standby_work)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
+8
-8
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{Delegation, EpochId, MixId, MixNodeCostParams, MixNodeRewarding};
|
||||
use crate::{Delegation, EpochId, NodeCostParams, NodeId, NodeRewarding};
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -9,21 +9,21 @@ use crate::error::MixnetContractError;
|
||||
use crate::rewarding::helpers::truncate_reward;
|
||||
|
||||
pub struct SimulatedNode {
|
||||
pub mix_id: MixId,
|
||||
pub rewarding_details: MixNodeRewarding,
|
||||
pub mix_id: NodeId,
|
||||
pub rewarding_details: NodeRewarding,
|
||||
pub delegations: HashMap<String, Delegation>,
|
||||
}
|
||||
|
||||
impl SimulatedNode {
|
||||
pub fn new(
|
||||
mix_id: MixId,
|
||||
cost_params: MixNodeCostParams,
|
||||
mix_id: NodeId,
|
||||
cost_params: NodeCostParams,
|
||||
initial_pledge: &Coin,
|
||||
current_epoch: EpochId,
|
||||
) -> Result<Self, MixnetContractError> {
|
||||
Ok(SimulatedNode {
|
||||
mix_id,
|
||||
rewarding_details: MixNodeRewarding::initialise_new(
|
||||
rewarding_details: NodeRewarding::initialise_new(
|
||||
cost_params,
|
||||
initial_pledge,
|
||||
current_epoch,
|
||||
@@ -59,8 +59,8 @@ impl SimulatedNode {
|
||||
) -> Result<(Coin, Coin), MixnetContractError> {
|
||||
let delegator = delegator.into();
|
||||
let delegation = self.delegations.remove(&delegator).ok_or(
|
||||
MixnetContractError::NoMixnodeDelegationFound {
|
||||
mix_id: MixId::MAX,
|
||||
MixnetContractError::NodeDelegationNotFound {
|
||||
node_id: NodeId::MAX,
|
||||
address: delegator,
|
||||
proxy: None,
|
||||
},
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::families::FamilyHead;
|
||||
use crate::{Gateway, IdentityKey, MixNode, MixNodeCostParams};
|
||||
use crate::nym_node::NymNode;
|
||||
use crate::{Gateway, MixNode, NodeCostParams};
|
||||
use contracts_common::signing::{
|
||||
ContractMessageContent, LegacyContractMessageContent, MessageType, Nonce, SignableMessage,
|
||||
SigningPurpose,
|
||||
@@ -12,20 +12,20 @@ use serde::Serialize;
|
||||
|
||||
pub type SignableMixNodeBondingMsg = SignableMessage<ContractMessageContent<MixnodeBondingPayload>>;
|
||||
pub type SignableGatewayBondingMsg = SignableMessage<ContractMessageContent<GatewayBondingPayload>>;
|
||||
pub type SignableNymNodeBondingMsg = SignableMessage<ContractMessageContent<NymNodeBondingPayload>>;
|
||||
pub type SignableLegacyMixNodeBondingMsg =
|
||||
SignableMessage<LegacyContractMessageContent<MixnodeBondingPayload>>;
|
||||
pub type SignableLegacyGatewayBondingMsg =
|
||||
SignableMessage<LegacyContractMessageContent<GatewayBondingPayload>>;
|
||||
pub type SignableFamilyJoinPermitMsg = SignableMessage<FamilyJoinPermit>;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MixnodeBondingPayload {
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
cost_params: NodeCostParams,
|
||||
}
|
||||
|
||||
impl MixnodeBondingPayload {
|
||||
pub fn new(mix_node: MixNode, cost_params: MixNodeCostParams) -> Self {
|
||||
pub fn new(mix_node: MixNode, cost_params: NodeCostParams) -> Self {
|
||||
Self {
|
||||
mix_node,
|
||||
cost_params,
|
||||
@@ -44,7 +44,7 @@ pub fn construct_mixnode_bonding_sign_payload(
|
||||
sender: Addr,
|
||||
pledge: Coin,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
cost_params: NodeCostParams,
|
||||
) -> SignableMixNodeBondingMsg {
|
||||
let payload = MixnodeBondingPayload::new(mix_node, cost_params);
|
||||
let content = ContractMessageContent::new(sender, vec![pledge], payload);
|
||||
@@ -57,7 +57,7 @@ pub fn construct_legacy_mixnode_bonding_sign_payload(
|
||||
sender: Addr,
|
||||
pledge: Coin,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
cost_params: NodeCostParams,
|
||||
) -> SignableLegacyMixNodeBondingMsg {
|
||||
let payload = MixnodeBondingPayload::new(mix_node, cost_params);
|
||||
let content: LegacyContractMessageContent<_> =
|
||||
@@ -109,39 +109,48 @@ pub fn construct_legacy_gateway_bonding_sign_payload(
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct FamilyJoinPermit {
|
||||
// the granter of this permit
|
||||
family_head: FamilyHead,
|
||||
// the actual member we want to permit to join
|
||||
member_node: IdentityKey,
|
||||
pub struct NymNodeBondingPayload {
|
||||
nym_node: NymNode,
|
||||
cost_params: NodeCostParams,
|
||||
}
|
||||
|
||||
impl FamilyJoinPermit {
|
||||
pub fn new(family_head: FamilyHead, member_node: IdentityKey) -> Self {
|
||||
Self {
|
||||
family_head,
|
||||
member_node,
|
||||
impl NymNodeBondingPayload {
|
||||
pub fn new(nym_node: NymNode, cost_params: NodeCostParams) -> Self {
|
||||
NymNodeBondingPayload {
|
||||
nym_node,
|
||||
cost_params,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningPurpose for FamilyJoinPermit {
|
||||
impl SigningPurpose for NymNodeBondingPayload {
|
||||
fn message_type() -> MessageType {
|
||||
MessageType::new("family-join-permit")
|
||||
MessageType::new("nym-node-bonding")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn construct_family_join_permit(
|
||||
pub fn construct_nym_node_bonding_sign_payload(
|
||||
nonce: Nonce,
|
||||
family_head: FamilyHead,
|
||||
member_node: IdentityKey,
|
||||
) -> SignableFamilyJoinPermitMsg {
|
||||
let payload = FamilyJoinPermit::new(family_head, member_node);
|
||||
sender: Addr,
|
||||
pledge: Coin,
|
||||
nym_node: NymNode,
|
||||
cost_params: NodeCostParams,
|
||||
) -> SignableNymNodeBondingMsg {
|
||||
let payload = NymNodeBondingPayload::new(nym_node, cost_params);
|
||||
let content = ContractMessageContent::new(sender, vec![pledge], payload);
|
||||
|
||||
// note: we're NOT wrapping it in `ContractMessageContent` because the family head is not going to be the one
|
||||
// sending the message to the contract
|
||||
SignableMessage::new(nonce, payload)
|
||||
SignableMessage::new(nonce, content)
|
||||
}
|
||||
|
||||
// TODO: depending on our threat model, we should perhaps extend it to include all _on_behalf methods
|
||||
// (update: but we trust our vesting contract since its compromise would be even more devastating so there's no need)
|
||||
pub fn construct_generic_node_bonding_payload<T>(
|
||||
nonce: Nonce,
|
||||
sender: Addr,
|
||||
pledge: Coin,
|
||||
payload: T,
|
||||
) -> SignableMessage<ContractMessageContent<T>>
|
||||
where
|
||||
T: SigningPurpose,
|
||||
{
|
||||
let content = ContractMessageContent::new(sender, vec![pledge], payload);
|
||||
SignableMessage::new(nonce, content)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user