Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7aa84cd5a | |||
| bfbd509e4b | |||
| 09b9601c7e | |||
| 2a1dd138e0 | |||
| 9874daa061 | |||
| baa61c07d5 | |||
| 62e9c8236a | |||
| cf268ffcd5 |
+14
-27
@@ -1,49 +1,36 @@
|
|||||||
name: Daily security audit
|
name: Daily security audit
|
||||||
|
|
||||||
on:
|
on: workflow_dispatch
|
||||||
schedule:
|
|
||||||
- cron: '5 9 * * *'
|
|
||||||
jobs:
|
jobs:
|
||||||
cargo-deny:
|
security_audit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository code
|
- uses: actions/checkout@v1
|
||||||
uses: actions/checkout@v2
|
- uses: actions-rs/audit-check@v1
|
||||||
- name: Install rust toolchain
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Install cargo deny
|
|
||||||
run: cargo install --locked cargo-deny
|
|
||||||
- name: Run cargo deny
|
|
||||||
run: cargo deny check advisories --hide-inclusion-graph &> .github/workflows/support-files/notifications/deny.message
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: report
|
|
||||||
path: .github/workflows/support-files/notifications/deny.message
|
|
||||||
notification:
|
notification:
|
||||||
needs: cargo-deny
|
if: ${{ failure() }}
|
||||||
|
needs: security_audit
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Download report from previous job
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: report
|
|
||||||
path: .github/workflows/support-files/notifications
|
|
||||||
- name: Keybase - Node Install
|
- name: Keybase - Node Install
|
||||||
run: npm install
|
run: npm install
|
||||||
working-directory: .github/workflows/support-files
|
working-directory: .github/workflows/support-files
|
||||||
- name: Keybase - Send Notification
|
- name: Keybase - Send Notification
|
||||||
env:
|
env:
|
||||||
NYM_NOTIFICATION_KIND: security
|
NYM_NOTIFICATION_KIND: nightly
|
||||||
NYM_PROJECT_NAME: "Daily security report"
|
NYM_PROJECT_NAME: "Nym daily audit"
|
||||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||||
|
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||||
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||||
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBTECH_TEAM }}"
|
||||||
KEYBASE_NYM_CHANNEL: "security"
|
KEYBASE_NYM_CHANNEL: "test"
|
||||||
uses: docker://keybaseio/client:stable-node
|
uses: docker://keybaseio/client:stable-node
|
||||||
with:
|
with:
|
||||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Dependencies (Linux)
|
||||||
|
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
|
||||||
|
|
||||||
- name: Check the release tag starts with `nym-binaries-`
|
- name: Check the release tag starts with `nym-binaries-`
|
||||||
if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false && github.event_name != 'workflow_dispatch'
|
if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false && github.event_name != 'workflow_dispatch'
|
||||||
uses: actions/github-script@v3
|
uses: actions/github-script@v3
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ require('dotenv').config();
|
|||||||
const Bot = require('keybase-bot');
|
const Bot = require('keybase-bot');
|
||||||
|
|
||||||
let context = {
|
let context = {
|
||||||
kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly', 'nym-connect','security'],
|
kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly', 'nym-connect'],
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
const Handlebars = require('handlebars');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const { Octokit, App } = require('octokit');
|
|
||||||
|
|
||||||
async function addToContextAndValidate(context) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getMessageBody(context) {
|
|
||||||
try {
|
|
||||||
const source = fs
|
|
||||||
.readFileSync("deny.message").toString();
|
|
||||||
return source;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
addToContextAndValidate,
|
|
||||||
getMessageBody,
|
|
||||||
};
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
name: Tests for validator API
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- "validator-api/tests/**"
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: validator-api/tests
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: validator api tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Node v18
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18.1.0
|
|
||||||
|
|
||||||
- name: Install yarn
|
|
||||||
run: yarn install
|
|
||||||
|
|
||||||
- name: Run yarn
|
|
||||||
run: yarn
|
|
||||||
|
|
||||||
- name: Launch tests
|
|
||||||
run: yarn test
|
|
||||||
working-directory: validator-api/tests
|
|
||||||
+22
-22
@@ -2,39 +2,40 @@
|
|||||||
|
|
||||||
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## Unreleased
|
## [v1.1.0](https://github.com/nymtech/nym/tree/v1.1.0) (2022-11-09)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- nym-cli: added CLI tool for interacting with the Nyx blockchain and Nym mixnet smart contracts ([#1577])
|
|
||||||
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#1558])
|
|
||||||
- network-requester: added additional Blockstream Green wallet endpoint to `example.allowed.list` ([#1611](https://github.com/nymtech/nym/pull/1611))
|
|
||||||
- common/ledger: new library for communicating with a Ledger device ([#1640])
|
|
||||||
- native-client/socks5-client: `disable_loop_cover_traffic_stream` Debug config option to disable the separate loop cover traffic stream ([#1666])
|
|
||||||
- native-client/socks5-client: `disable_main_poisson_packet_distribution` Debug config option to make the client ignore poisson distribution in the main packet stream and ONLY send real message (and as fast as they come) ([#1664])
|
|
||||||
- native-client/socks5-client: `use_extended_packet_size` Debug config option to make the client use 'ExtendedPacketSize' for its traffic (32kB as opposed to 2kB in 1.0.2) ([#1671])
|
|
||||||
- wasm-client: uses updated wasm-compatible `client-core` so that it's now capable of packet retransmission, cover traffic and poisson delay (among other things!) ([#1673])
|
|
||||||
- validator-api: add `interval_operating_cost` and `profit_margin_percent` to cmpute reward estimation endpoint
|
|
||||||
- native-client/socks5-client/network-requester: improve handling error cases ([#1713])
|
|
||||||
- vesting-contract: optional locked token pledge cap per account ([#1687]), defaults to 100_000 NYM
|
|
||||||
- clients: add testing-only support for two more extended packet sizes (8kb and 16kb).
|
- clients: add testing-only support for two more extended packet sizes (8kb and 16kb).
|
||||||
|
- common/ledger: new library for communicating with a Ledger device ([#1640])
|
||||||
|
- native-client/socks5-client/wasm-client: `disable_loop_cover_traffic_stream` Debug config option to disable the separate loop cover traffic stream ([#1666])
|
||||||
|
- native-client/socks5-client/wasm-client: `disable_main_poisson_packet_distribution` Debug config option to make the client ignore poisson distribution in the main packet stream and ONLY send real message (and as fast as they come) ([#1664])
|
||||||
|
- native-client/socks5-client/wasm-client: `use_extended_packet_size` Debug config option to make the client use 'ExtendedPacketSize' for its traffic (32kB as opposed to 2kB in 1.0.2) ([#1671])
|
||||||
|
- network-requester: added additional Blockstream Green wallet endpoint to `example.allowed.list` ([#1611])
|
||||||
|
- validator-api: add `interval_operating_cost` and `profit_margin_percent` to compute reward estimation endpoint
|
||||||
|
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#1558])
|
||||||
|
- wasm-client: uses updated wasm-compatible `client-core` so that it's now capable of packet retransmission, cover traffic and poisson delay (among other things!) ([#1673])
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- validator-api, mixnode, gateway should now prefer values in config.toml over mainnet defaults ([#1645])
|
|
||||||
- validator-api should now correctly update historical uptimes for all mixnodes and gateways every 24h ([#1721])
|
|
||||||
- socks5-client: fix bug where in some cases packet reordering could trigger a connection being closed too early ([#1702],[#1724])
|
- socks5-client: fix bug where in some cases packet reordering could trigger a connection being closed too early ([#1702],[#1724])
|
||||||
|
- validator-api: mixnode, gateway should now prefer values in config.toml over mainnet defaults ([#1645])
|
||||||
|
- validator-api: should now correctly update historical uptimes for all mixnodes and gateways every 24h ([#1721])
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- validator-client: made `fee` argument optional for `execute` and `execute_multiple` ([#1541])
|
|
||||||
- socks5 client: graceful shutdown should fix error on disconnect in nym-connect ([#1591])
|
|
||||||
- wasm-client: fixed build errors on MacOS and changed example JS code to use mainnet ([#1585])
|
|
||||||
- gateway-client: will attempt to read now as many as 8 websocket messages at once, assuming they're already available on the socket ([#1669])
|
|
||||||
- validator-api: changed error serialization on `inclusion_probability`, `stake-saturation` and `reward-estimation` endpoints to provide more accurate information ([#1681])
|
|
||||||
- moved `Percent` struct to to `contracts-common`, change affects explorer-api
|
|
||||||
- clients: bound the sphinx packet channel and reduce sending rate if gateway can't keep up ([#1703],[#1725])
|
- clients: bound the sphinx packet channel and reduce sending rate if gateway can't keep up ([#1703],[#1725])
|
||||||
|
- gateway-client: will attempt to read now as many as 8 websocket messages at once, assuming they're already available on the socket ([#1669])
|
||||||
|
- moved `Percent` struct to `contracts-common`, change affects explorer-api
|
||||||
|
- socks5 client: graceful shutdown should fix error on disconnect in nym-connect ([#1591])
|
||||||
|
- validator-api: changed error serialization on `inclusion_probability`, `stake-saturation` and `reward-estimation` endpoints to provide more accurate information ([#1681])
|
||||||
|
- validator-client: made `fee` argument optional for `execute` and `execute_multiple` ([#1541])
|
||||||
|
- wasm-client: fixed build errors on MacOS and changed example JS code to use mainnet ([#1585])
|
||||||
|
- validator-api: changes to internal SQL schema due to the mixnet contract revamp ([#1472])
|
||||||
|
- validator-api: changes to internal data structures due to the mixnet contract revamp ([#1472])
|
||||||
|
- validator-api: split epoch-operations into multiple separate transactions ([#1472])
|
||||||
|
|
||||||
|
[#1472]: https://github.com/nymtech/nym/pull/1472
|
||||||
[#1541]: https://github.com/nymtech/nym/pull/1541
|
[#1541]: https://github.com/nymtech/nym/pull/1541
|
||||||
[#1558]: https://github.com/nymtech/nym/pull/1558
|
[#1558]: https://github.com/nymtech/nym/pull/1558
|
||||||
[#1577]: https://github.com/nymtech/nym/pull/1577
|
[#1577]: https://github.com/nymtech/nym/pull/1577
|
||||||
@@ -42,16 +43,15 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
|||||||
[#1591]: https://github.com/nymtech/nym/pull/1591
|
[#1591]: https://github.com/nymtech/nym/pull/1591
|
||||||
[#1640]: https://github.com/nymtech/nym/pull/1640
|
[#1640]: https://github.com/nymtech/nym/pull/1640
|
||||||
[#1645]: https://github.com/nymtech/nym/pull/1645
|
[#1645]: https://github.com/nymtech/nym/pull/1645
|
||||||
|
[#1611]: https://github.com/nymtech/nym/pull/1611
|
||||||
[#1664]: https://github.com/nymtech/nym/pull/1664
|
[#1664]: https://github.com/nymtech/nym/pull/1664
|
||||||
[#1666]: https://github.com/nymtech/nym/pull/1645
|
[#1666]: https://github.com/nymtech/nym/pull/1645
|
||||||
[#1669]: https://github.com/nymtech/nym/pull/1669
|
[#1669]: https://github.com/nymtech/nym/pull/1669
|
||||||
[#1671]: https://github.com/nymtech/nym/pull/1671
|
[#1671]: https://github.com/nymtech/nym/pull/1671
|
||||||
[#1673]: https://github.com/nymtech/nym/pull/1673
|
[#1673]: https://github.com/nymtech/nym/pull/1673
|
||||||
[#1681]: https://github.com/nymtech/nym/pull/1681
|
[#1681]: https://github.com/nymtech/nym/pull/1681
|
||||||
[#1687]: https://github.com/nymtech/nym/pull/1687
|
|
||||||
[#1702]: https://github.com/nymtech/nym/pull/1702
|
[#1702]: https://github.com/nymtech/nym/pull/1702
|
||||||
[#1703]: https://github.com/nymtech/nym/pull/1703
|
[#1703]: https://github.com/nymtech/nym/pull/1703
|
||||||
[#1713]: https://github.com/nymtech/nym/pull/1713
|
|
||||||
[#1721]: https://github.com/nymtech/nym/pull/1721
|
[#1721]: https://github.com/nymtech/nym/pull/1721
|
||||||
[#1724]: https://github.com/nymtech/nym/pull/1724
|
[#1724]: https://github.com/nymtech/nym/pull/1724
|
||||||
[#1725]: https://github.com/nymtech/nym/pull/1725
|
[#1725]: https://github.com/nymtech/nym/pull/1725
|
||||||
|
|||||||
Generated
+9
-12
@@ -578,7 +578,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "client-core"
|
name = "client-core"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"config",
|
"config",
|
||||||
"crypto",
|
"crypto",
|
||||||
@@ -1582,7 +1582,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "explorer-api"
|
name = "explorer-api"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap 3.2.8",
|
"clap 3.2.8",
|
||||||
@@ -3070,7 +3070,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-cli"
|
name = "nym-cli"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@@ -3122,7 +3122,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 3.2.8",
|
"clap 3.2.8",
|
||||||
"client-core",
|
"client-core",
|
||||||
@@ -3147,7 +3147,6 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"sled",
|
"sled",
|
||||||
"task",
|
"task",
|
||||||
"thiserror",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite 0.14.0",
|
"tokio-tungstenite 0.14.0",
|
||||||
"topology",
|
"topology",
|
||||||
@@ -3160,7 +3159,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-gateway"
|
name = "nym-gateway"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3207,7 +3206,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-mixnode"
|
name = "nym-mixnode"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bs58",
|
"bs58",
|
||||||
@@ -3249,7 +3248,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-network-requester"
|
name = "nym-network-requester"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"clap 3.2.8",
|
"clap 3.2.8",
|
||||||
@@ -3296,7 +3295,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 3.2.8",
|
"clap 3.2.8",
|
||||||
"client-core",
|
"client-core",
|
||||||
@@ -3324,7 +3323,6 @@ dependencies = [
|
|||||||
"snafu 0.6.10",
|
"snafu 0.6.10",
|
||||||
"socks5-requests",
|
"socks5-requests",
|
||||||
"task",
|
"task",
|
||||||
"thiserror",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"topology",
|
"topology",
|
||||||
"url",
|
"url",
|
||||||
@@ -3361,7 +3359,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-validator-api"
|
name = "nym-validator-api"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -6516,7 +6514,6 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"contracts-common",
|
"contracts-common",
|
||||||
"cosmwasm-std",
|
"cosmwasm-std",
|
||||||
"log",
|
|
||||||
"mixnet-contract-common",
|
"mixnet-contract-common",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -122,6 +122,3 @@ mixnet-opt: wasm
|
|||||||
generate-typescript:
|
generate-typescript:
|
||||||
cd tools/ts-rs-cli && cargo run && cd ../..
|
cd tools/ts-rs-cli && cargo run && cd ../..
|
||||||
yarn types:lint:fix
|
yarn types:lint:fix
|
||||||
|
|
||||||
run-validator-tests:
|
|
||||||
cd validator-api/tests/functional_test && yarn test
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "client-core"
|
name = "client-core"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ where
|
|||||||
async fn on_message(&mut self, next_message: StreamMessage) {
|
async fn on_message(&mut self, next_message: StreamMessage) {
|
||||||
trace!("created new message");
|
trace!("created new message");
|
||||||
|
|
||||||
let (next_message, fragment_id) = match next_message {
|
let next_message = match next_message {
|
||||||
StreamMessage::Cover => {
|
StreamMessage::Cover => {
|
||||||
// TODO for way down the line: in very rare cases (during topology update) we might have
|
// TODO for way down the line: in very rare cases (during topology update) we might have
|
||||||
// to wait a really tiny bit before actually obtaining the permit hence messing with our
|
// to wait a really tiny bit before actually obtaining the permit hence messing with our
|
||||||
@@ -315,24 +315,20 @@ where
|
|||||||
}
|
}
|
||||||
let topology_ref = topology_ref_option.unwrap();
|
let topology_ref = topology_ref_option.unwrap();
|
||||||
|
|
||||||
(
|
generate_loop_cover_packet(
|
||||||
generate_loop_cover_packet(
|
&mut self.rng,
|
||||||
&mut self.rng,
|
topology_ref,
|
||||||
topology_ref,
|
&self.ack_key,
|
||||||
&self.ack_key,
|
&self.our_full_destination,
|
||||||
&self.our_full_destination,
|
self.config.average_ack_delay,
|
||||||
self.config.average_ack_delay,
|
self.config.average_packet_delay,
|
||||||
self.config.average_packet_delay,
|
self.config.cover_packet_size,
|
||||||
self.config.cover_packet_size,
|
|
||||||
)
|
|
||||||
.expect(
|
|
||||||
"Somehow failed to generate a loop cover message with a valid topology",
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
|
.expect("Somehow failed to generate a loop cover message with a valid topology")
|
||||||
}
|
}
|
||||||
StreamMessage::Real(real_message) => {
|
StreamMessage::Real(real_message) => {
|
||||||
(real_message.mix_packet, Some(real_message.fragment_id))
|
self.sent_notify(real_message.fragment_id);
|
||||||
|
real_message.mix_packet
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -340,12 +336,6 @@ where
|
|||||||
log::error!("Failed to send - channel closed: {}", err);
|
log::error!("Failed to send - channel closed: {}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// notify ack controller about sending our message only after we actually managed to push it
|
|
||||||
// through the channel
|
|
||||||
if let Some(fragment_id) = fragment_id {
|
|
||||||
self.sent_notify(fragment_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// JS: Not entirely sure why or how it fixes stuff, but without the yield call,
|
// JS: Not entirely sure why or how it fixes stuff, but without the yield call,
|
||||||
// the UnboundedReceiver [of mix_rx] will not get a chance to read anything
|
// the UnboundedReceiver [of mix_rx] will not get a chance to read anything
|
||||||
// JS2: Basically it was the case that with high enough rate, the stream had already a next value
|
// JS2: Basically it was the case that with high enough rate, the stream had already a next value
|
||||||
|
|||||||
@@ -57,15 +57,24 @@ impl<'a> TopologyReadPermit<'a> {
|
|||||||
) -> Option<&'a NymTopology> {
|
) -> Option<&'a NymTopology> {
|
||||||
// Note: implicit deref with Deref for TopologyReadPermit is happening here
|
// Note: implicit deref with Deref for TopologyReadPermit is happening here
|
||||||
let topology_ref_option = self.permit.as_ref();
|
let topology_ref_option = self.permit.as_ref();
|
||||||
topology_ref_option.as_ref().filter(|topology_ref| {
|
match topology_ref_option {
|
||||||
!(!topology_ref.can_construct_path_through(DEFAULT_NUM_MIX_HOPS)
|
None => None,
|
||||||
|| !topology_ref.gateway_exists(ack_recipient.gateway())
|
Some(topology_ref) => {
|
||||||
|| if let Some(packet_recipient) = packet_recipient {
|
// see if it's possible to route the packet to both gateways
|
||||||
!topology_ref.gateway_exists(packet_recipient.gateway())
|
if !topology_ref.can_construct_path_through(DEFAULT_NUM_MIX_HOPS)
|
||||||
|
|| !topology_ref.gateway_exists(ack_recipient.gateway())
|
||||||
|
|| if let Some(packet_recipient) = packet_recipient {
|
||||||
|
!topology_ref.gateway_exists(packet_recipient.gateway())
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
{
|
||||||
|
None
|
||||||
} else {
|
} else {
|
||||||
false
|
Some(topology_ref)
|
||||||
})
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,4 @@ pub enum ClientCoreError {
|
|||||||
ListOfValidatorApisIsEmpty,
|
ListOfValidatorApisIsEmpty,
|
||||||
#[error("Could not load existing gateway configuration: {0}")]
|
#[error("Could not load existing gateway configuration: {0}")]
|
||||||
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
|
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
|
||||||
#[error("The current network topology seem to be insufficient to route any packets through")]
|
|
||||||
InsufficientNetworkTopology,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||||
description = "Implementation of the Nym Client"
|
description = "Implementation of the Nym Client"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -27,7 +27,6 @@ pretty_env_logger = "0.4" # for formatting log messages
|
|||||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
|
rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
|
||||||
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
|
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
|
||||||
sled = "0.34" # for storage of replySURB decryption keys
|
sled = "0.34" # for storage of replySURB decryption keys
|
||||||
thiserror = "1.0.34"
|
|
||||||
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||||
tokio-tungstenite = "0.14" # websocket
|
tokio-tungstenite = "0.14" # websocket
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ use client_core::client::topology_control::{
|
|||||||
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
|
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
|
||||||
};
|
};
|
||||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||||
use client_core::error::ClientCoreError;
|
|
||||||
use crypto::asymmetric::identity;
|
use crypto::asymmetric::identity;
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use gateway_client::bandwidth::BandwidthController;
|
use gateway_client::bandwidth::BandwidthController;
|
||||||
@@ -34,7 +33,6 @@ use nymsphinx::receiver::ReconstructedMessage;
|
|||||||
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
|
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
|
||||||
|
|
||||||
use crate::client::config::{Config, SocketType};
|
use crate::client::config::{Config, SocketType};
|
||||||
use crate::error::ClientError;
|
|
||||||
use crate::websocket;
|
use crate::websocket;
|
||||||
|
|
||||||
pub(crate) mod config;
|
pub(crate) mod config;
|
||||||
@@ -234,7 +232,7 @@ impl NymClient {
|
|||||||
&mut self,
|
&mut self,
|
||||||
topology_accessor: TopologyAccessor,
|
topology_accessor: TopologyAccessor,
|
||||||
shutdown: ShutdownListener,
|
shutdown: ShutdownListener,
|
||||||
) -> Result<(), ClientError> {
|
) {
|
||||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||||
self.config.get_base().get_validator_api_endpoints(),
|
self.config.get_base().get_validator_api_endpoints(),
|
||||||
self.config.get_base().get_topology_refresh_rate(),
|
self.config.get_base().get_topology_refresh_rate(),
|
||||||
@@ -249,16 +247,14 @@ impl NymClient {
|
|||||||
|
|
||||||
// TODO: a slightly more graceful termination here
|
// TODO: a slightly more graceful termination here
|
||||||
if !topology_refresher.is_topology_routable().await {
|
if !topology_refresher.is_topology_routable().await {
|
||||||
log::error!(
|
panic!(
|
||||||
"The current network topology seem to be insufficient to route any packets through \
|
"The current network topology seem to be insufficient to route any packets through\
|
||||||
- check if enough nodes and a gateway are online"
|
- check if enough nodes and a gateway are online"
|
||||||
);
|
);
|
||||||
return Err(ClientCoreError::InsufficientNetworkTopology.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Starting topology refresher...");
|
info!("Starting topology refresher...");
|
||||||
topology_refresher.start_with_shutdown(shutdown);
|
topology_refresher.start_with_shutdown(shutdown);
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
|
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
|
||||||
@@ -332,8 +328,8 @@ impl NymClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
||||||
pub async fn run_forever(&mut self) -> Result<(), ClientError> {
|
pub async fn run_forever(&mut self) {
|
||||||
let shutdown = self.start().await?;
|
let shutdown = self.start().await;
|
||||||
wait_for_signal().await;
|
wait_for_signal().await;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
@@ -350,10 +346,9 @@ impl NymClient {
|
|||||||
//shutdown.wait_for_shutdown().await;
|
//shutdown.wait_for_shutdown().await;
|
||||||
|
|
||||||
log::info!("Stopping nym-client");
|
log::info!("Stopping nym-client");
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(&mut self) -> Result<ShutdownNotifier, ClientError> {
|
pub async fn start(&mut self) -> ShutdownNotifier {
|
||||||
info!("Starting nym client");
|
info!("Starting nym client");
|
||||||
// channels for inter-component communication
|
// channels for inter-component communication
|
||||||
// TODO: make the channels be internally created by the relevant components
|
// TODO: make the channels be internally created by the relevant components
|
||||||
@@ -384,7 +379,7 @@ impl NymClient {
|
|||||||
// the components are started in very specific order. Unless you know what you are doing,
|
// the components are started in very specific order. Unless you know what you are doing,
|
||||||
// do not change that.
|
// do not change that.
|
||||||
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
|
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
|
||||||
.await?;
|
.await;
|
||||||
self.start_received_messages_buffer_controller(
|
self.start_received_messages_buffer_controller(
|
||||||
received_buffer_request_receiver,
|
received_buffer_request_receiver,
|
||||||
mixnet_messages_receiver,
|
mixnet_messages_receiver,
|
||||||
@@ -448,6 +443,6 @@ impl NymClient {
|
|||||||
info!("Client startup finished!");
|
info!("Client startup finished!");
|
||||||
info!("The address of this client is: {}", self.as_mix_recipient());
|
info!("The address of this client is: {}", self.as_mix_recipient());
|
||||||
|
|
||||||
Ok(shutdown)
|
shutdown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::config::{Config, SocketType};
|
use crate::client::config::{Config, SocketType};
|
||||||
use crate::error::ClientError;
|
|
||||||
use clap::CommandFactory;
|
use clap::CommandFactory;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use completions::{fig_generate, ArgShell};
|
use completions::{fig_generate, ArgShell};
|
||||||
@@ -84,17 +83,16 @@ pub(crate) struct OverrideConfig {
|
|||||||
enabled_credentials_mode: bool,
|
enabled_credentials_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn execute(args: &Cli) -> Result<(), ClientError> {
|
pub(crate) async fn execute(args: &Cli) {
|
||||||
let bin_name = "nym-native-client";
|
let bin_name = "nym-native-client";
|
||||||
|
|
||||||
match &args.command {
|
match &args.command {
|
||||||
Commands::Init(m) => init::execute(m).await,
|
Commands::Init(m) => init::execute(m).await,
|
||||||
Commands::Run(m) => run::execute(m).await?,
|
Commands::Run(m) => run::execute(m).await,
|
||||||
Commands::Upgrade(m) => upgrade::execute(m),
|
Commands::Upgrade(m) => upgrade::execute(m),
|
||||||
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
|
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
|
||||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
|
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
client::{config::Config, NymClient},
|
client::{config::Config, NymClient},
|
||||||
commands::{override_config, OverrideConfig},
|
commands::{override_config, OverrideConfig},
|
||||||
error::ClientError,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
@@ -74,14 +73,14 @@ fn version_check(cfg: &Config) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn execute(args: &Run) -> Result<(), ClientError> {
|
pub(crate) async fn execute(args: &Run) {
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
let mut config = match Config::load_from_file(Some(id)) {
|
let mut config = match Config::load_from_file(Some(id)) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
|
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
|
||||||
return Err(ClientError::FailedToLoadConfig(id.to_string()));
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,8 +89,8 @@ pub(crate) async fn execute(args: &Run) -> Result<(), ClientError> {
|
|||||||
|
|
||||||
if !version_check(&config) {
|
if !version_check(&config) {
|
||||||
error!("failed the local version check");
|
error!("failed the local version check");
|
||||||
return Err(ClientError::FailedLocalVersionCheck);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NymClient::new(config).run_forever().await
|
NymClient::new(config).run_forever().await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
use client_core::error::ClientCoreError;
|
|
||||||
use crypto::asymmetric::identity::Ed25519RecoveryError;
|
|
||||||
use gateway_client::error::GatewayClientError;
|
|
||||||
use validator_client::ValidatorClientError;
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
|
||||||
pub enum ClientError {
|
|
||||||
#[error("I/O error: {0}")]
|
|
||||||
IoError(#[from] std::io::Error),
|
|
||||||
#[error("Gateway client error: {0}")]
|
|
||||||
GatewayClientError(#[from] GatewayClientError),
|
|
||||||
#[error("Ed25519 error: {0}")]
|
|
||||||
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
|
|
||||||
#[error("Validator client error: {0}")]
|
|
||||||
ValidatorClientError(#[from] ValidatorClientError),
|
|
||||||
#[error("client-core error: {0}")]
|
|
||||||
ClientCoreError(#[from] ClientCoreError),
|
|
||||||
|
|
||||||
#[error("Failed to load config for: {0}")]
|
|
||||||
FailedToLoadConfig(String),
|
|
||||||
#[error("Failed local version check, client and config mismatch")]
|
|
||||||
FailedLocalVersionCheck,
|
|
||||||
}
|
|
||||||
@@ -2,5 +2,4 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod error;
|
|
||||||
pub mod websocket;
|
pub mod websocket;
|
||||||
|
|||||||
@@ -2,23 +2,21 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use clap::{crate_version, Parser};
|
use clap::{crate_version, Parser};
|
||||||
use error::ClientError;
|
|
||||||
use logging::setup_logging;
|
use logging::setup_logging;
|
||||||
use network_defaults::setup_env;
|
use network_defaults::setup_env;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod error;
|
|
||||||
pub mod websocket;
|
pub mod websocket;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), ClientError> {
|
async fn main() {
|
||||||
setup_logging();
|
setup_logging();
|
||||||
println!("{}", banner());
|
println!("{}", banner());
|
||||||
|
|
||||||
let args = commands::Cli::parse();
|
let args = commands::Cli::parse();
|
||||||
setup_env(args.config_env_file.clone());
|
setup_env(args.config_env_file.clone());
|
||||||
commands::execute(&args).await
|
commands::execute(&args).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn banner() -> String {
|
fn banner() -> String {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -20,7 +20,6 @@ pretty_env_logger = "0.4"
|
|||||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||||
serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization
|
serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization
|
||||||
snafu = "0.6"
|
snafu = "0.6"
|
||||||
thiserror = "1.0.34"
|
|
||||||
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] }
|
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] }
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::client::config::Config;
|
use crate::client::config::Config;
|
||||||
use crate::error::Socks5ClientError;
|
|
||||||
use crate::socks::{
|
use crate::socks::{
|
||||||
authentication::{AuthenticationMethods, Authenticator, User},
|
authentication::{AuthenticationMethods, Authenticator, User},
|
||||||
server::SphinxSocksServer,
|
server::SphinxSocksServer,
|
||||||
@@ -24,7 +23,6 @@ use client_core::client::topology_control::{
|
|||||||
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
|
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
|
||||||
};
|
};
|
||||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||||
use client_core::error::ClientCoreError;
|
|
||||||
use crypto::asymmetric::identity;
|
use crypto::asymmetric::identity;
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
@@ -234,7 +232,7 @@ impl NymClient {
|
|||||||
&mut self,
|
&mut self,
|
||||||
topology_accessor: TopologyAccessor,
|
topology_accessor: TopologyAccessor,
|
||||||
shutdown: ShutdownListener,
|
shutdown: ShutdownListener,
|
||||||
) -> Result<(), Socks5ClientError> {
|
) {
|
||||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||||
self.config.get_base().get_validator_api_endpoints(),
|
self.config.get_base().get_validator_api_endpoints(),
|
||||||
self.config.get_base().get_topology_refresh_rate(),
|
self.config.get_base().get_topology_refresh_rate(),
|
||||||
@@ -249,16 +247,14 @@ impl NymClient {
|
|||||||
|
|
||||||
// TODO: a slightly more graceful termination here
|
// TODO: a slightly more graceful termination here
|
||||||
if !topology_refresher.is_topology_routable().await {
|
if !topology_refresher.is_topology_routable().await {
|
||||||
log::error!(
|
panic!(
|
||||||
"The current network topology seem to be insufficient to route any packets through \
|
"The current network topology seem to be insufficient to route any packets through\
|
||||||
- check if enough nodes and a gateway are online"
|
- check if enough nodes and a gateway are online"
|
||||||
);
|
);
|
||||||
return Err(ClientCoreError::InsufficientNetworkTopology.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Starting topology refresher...");
|
info!("Starting topology refresher...");
|
||||||
topology_refresher.start_with_shutdown(shutdown);
|
topology_refresher.start_with_shutdown(shutdown);
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
|
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
|
||||||
@@ -297,8 +293,8 @@ impl NymClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
||||||
pub async fn run_forever(&mut self) -> Result<(), Socks5ClientError> {
|
pub async fn run_forever(&mut self) {
|
||||||
let mut shutdown = self.start().await?;
|
let mut shutdown = self.start().await;
|
||||||
wait_for_signal().await;
|
wait_for_signal().await;
|
||||||
|
|
||||||
log::info!("Sending shutdown");
|
log::info!("Sending shutdown");
|
||||||
@@ -309,15 +305,11 @@ impl NymClient {
|
|||||||
shutdown.wait_for_shutdown().await;
|
shutdown.wait_for_shutdown().await;
|
||||||
|
|
||||||
log::info!("Stopping nym-socks5-client");
|
log::info!("Stopping nym-socks5-client");
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variant of `run_forever` that listends for remote control messages
|
// Variant of `run_forever` that listends for remote control messages
|
||||||
pub async fn run_and_listen(
|
pub async fn run_and_listen(&mut self, mut receiver: Socks5ControlMessageReceiver) {
|
||||||
&mut self,
|
let mut shutdown = self.start().await;
|
||||||
mut receiver: Socks5ControlMessageReceiver,
|
|
||||||
) -> Result<(), Socks5ClientError> {
|
|
||||||
let mut shutdown = self.start().await?;
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
message = receiver.next() => {
|
message = receiver.next() => {
|
||||||
log::debug!("Received message: {:?}", message);
|
log::debug!("Received message: {:?}", message);
|
||||||
@@ -343,10 +335,9 @@ impl NymClient {
|
|||||||
shutdown.wait_for_shutdown().await;
|
shutdown.wait_for_shutdown().await;
|
||||||
|
|
||||||
log::info!("Stopping nym-socks5-client");
|
log::info!("Stopping nym-socks5-client");
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(&mut self) -> Result<ShutdownNotifier, Socks5ClientError> {
|
pub async fn start(&mut self) -> ShutdownNotifier {
|
||||||
info!("Starting nym client");
|
info!("Starting nym client");
|
||||||
// channels for inter-component communication
|
// channels for inter-component communication
|
||||||
// TODO: make the channels be internally created by the relevant components
|
// TODO: make the channels be internally created by the relevant components
|
||||||
@@ -377,7 +368,7 @@ impl NymClient {
|
|||||||
// the components are started in very specific order. Unless you know what you are doing,
|
// the components are started in very specific order. Unless you know what you are doing,
|
||||||
// do not change that.
|
// do not change that.
|
||||||
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
|
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
|
||||||
.await?;
|
.await;
|
||||||
self.start_received_messages_buffer_controller(
|
self.start_received_messages_buffer_controller(
|
||||||
received_buffer_request_receiver,
|
received_buffer_request_receiver,
|
||||||
mixnet_messages_receiver,
|
mixnet_messages_receiver,
|
||||||
@@ -426,6 +417,6 @@ impl NymClient {
|
|||||||
info!("Client startup finished!");
|
info!("Client startup finished!");
|
||||||
info!("The address of this client is: {}", self.as_mix_recipient());
|
info!("The address of this client is: {}", self.as_mix_recipient());
|
||||||
|
|
||||||
Ok(shutdown)
|
shutdown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::config::Config;
|
use crate::client::config::Config;
|
||||||
use crate::error::Socks5ClientError;
|
|
||||||
use clap::CommandFactory;
|
use clap::CommandFactory;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use completions::{fig_generate, ArgShell};
|
use completions::{fig_generate, ArgShell};
|
||||||
@@ -84,17 +83,16 @@ pub(crate) struct OverrideConfig {
|
|||||||
enabled_credentials_mode: bool,
|
enabled_credentials_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn execute(args: &Cli) -> Result<(), Socks5ClientError> {
|
pub(crate) async fn execute(args: &Cli) {
|
||||||
let bin_name = "nym-socks5-client";
|
let bin_name = "nym-socks5-client";
|
||||||
|
|
||||||
match &args.command {
|
match &args.command {
|
||||||
Commands::Init(m) => init::execute(m).await,
|
Commands::Init(m) => init::execute(m).await,
|
||||||
Commands::Run(m) => run::execute(m).await?,
|
Commands::Run(m) => run::execute(m).await,
|
||||||
Commands::Upgrade(m) => upgrade::execute(m),
|
Commands::Upgrade(m) => upgrade::execute(m),
|
||||||
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
|
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
|
||||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
|
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
client::{config::Config, NymClient},
|
client::{config::Config, NymClient},
|
||||||
commands::{override_config, OverrideConfig},
|
commands::{override_config, OverrideConfig},
|
||||||
error::Socks5ClientError,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
@@ -81,14 +80,14 @@ fn version_check(cfg: &Config) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn execute(args: &Run) -> Result<(), Socks5ClientError> {
|
pub(crate) async fn execute(args: &Run) {
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
let mut config = match Config::load_from_file(Some(id)) {
|
let mut config = match Config::load_from_file(Some(id)) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
|
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
|
||||||
return Err(Socks5ClientError::FailedToLoadConfig(id.to_string()));
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,8 +96,8 @@ pub(crate) async fn execute(args: &Run) -> Result<(), Socks5ClientError> {
|
|||||||
|
|
||||||
if !version_check(&config) {
|
if !version_check(&config) {
|
||||||
error!("failed the local version check");
|
error!("failed the local version check");
|
||||||
return Err(Socks5ClientError::FailedLocalVersionCheck);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NymClient::new(config).run_forever().await
|
NymClient::new(config).run_forever().await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
use client_core::error::ClientCoreError;
|
|
||||||
use crypto::asymmetric::identity::Ed25519RecoveryError;
|
|
||||||
use gateway_client::error::GatewayClientError;
|
|
||||||
use validator_client::ValidatorClientError;
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
|
||||||
pub enum Socks5ClientError {
|
|
||||||
#[error("I/O error: {0}")]
|
|
||||||
IoError(#[from] std::io::Error),
|
|
||||||
#[error("Gateway client error: {0}")]
|
|
||||||
GatewayClientError(#[from] GatewayClientError),
|
|
||||||
#[error("Ed25519 error: {0}")]
|
|
||||||
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
|
|
||||||
#[error("Validator client error: {0}")]
|
|
||||||
ValidatorClientError(#[from] ValidatorClientError),
|
|
||||||
#[error("client-core error: {0}")]
|
|
||||||
ClientCoreError(#[from] ClientCoreError),
|
|
||||||
|
|
||||||
#[error("Failed to load config for: {0}")]
|
|
||||||
FailedToLoadConfig(String),
|
|
||||||
#[error("Failed local version check, client and config mismatch")]
|
|
||||||
FailedLocalVersionCheck,
|
|
||||||
}
|
|
||||||
@@ -2,5 +2,4 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod error;
|
|
||||||
pub mod socks;
|
pub mod socks;
|
||||||
|
|||||||
@@ -2,23 +2,21 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use clap::{crate_version, Parser};
|
use clap::{crate_version, Parser};
|
||||||
use error::Socks5ClientError;
|
|
||||||
use logging::setup_logging;
|
use logging::setup_logging;
|
||||||
use network_defaults::setup_env;
|
use network_defaults::setup_env;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
mod commands;
|
mod commands;
|
||||||
pub mod error;
|
|
||||||
pub mod socks;
|
pub mod socks;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Socks5ClientError> {
|
async fn main() {
|
||||||
setup_logging();
|
setup_logging();
|
||||||
println!("{}", banner());
|
println!("{}", banner());
|
||||||
|
|
||||||
let args = commands::Cli::parse();
|
let args = commands::Cli::parse();
|
||||||
setup_env(args.config_env_file.clone());
|
setup_env(args.config_env_file.clone());
|
||||||
commands::execute(&args).await
|
commands::execute(&args).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn banner() -> String {
|
fn banner() -> String {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client-wasm"
|
name = "nym-client-wasm"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||||
version = "1.0.1"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
@@ -55,8 +55,6 @@ wee_alloc = { version = "0.4", optional = true }
|
|||||||
wasm-bindgen-test = "0.3"
|
wasm-bindgen-test = "0.3"
|
||||||
|
|
||||||
[package.metadata.wasm-pack.profile.release]
|
[package.metadata.wasm-pack.profile.release]
|
||||||
wasm-opt = true
|
wasm-opt = false
|
||||||
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = true
|
|
||||||
opt-level = 'z'
|
|
||||||
@@ -2,11 +2,6 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
performance: {
|
|
||||||
hints: false,
|
|
||||||
maxEntrypointSize: 512000,
|
|
||||||
maxAssetSize: 512000
|
|
||||||
},
|
|
||||||
entry: {
|
entry: {
|
||||||
bootstrap: './bootstrap.js',
|
bootstrap: './bootstrap.js',
|
||||||
worker: './worker.js',
|
worker: './worker.js',
|
||||||
@@ -27,7 +22,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
],
|
],
|
||||||
experiments: { syncWebAssembly: true },
|
experiments: { syncWebAssembly: true },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ async function main() {
|
|||||||
// const preferredGateway = 'CgQrYP8etksSBf4nALNqp93SHPpgFwEUyTsjBNNLj5WM';
|
// const preferredGateway = 'CgQrYP8etksSBf4nALNqp93SHPpgFwEUyTsjBNNLj5WM';
|
||||||
|
|
||||||
const gatewayEndpoint = await get_gateway(validator, preferredGateway);
|
const gatewayEndpoint = await get_gateway(validator, preferredGateway);
|
||||||
gatewayEndpoint.gateway_listener = "wss://gateway1.nymtech.net:443"; // this is needed if we want it to work on the web. However this gateway is a v1 gateway, we will need to change for v2 once we get there
|
|
||||||
|
|
||||||
// only really useful if you want to adjust some settings like traffic rate
|
// only really useful if you want to adjust some settings like traffic rate
|
||||||
// (if not needed you can just pass a null)
|
// (if not needed you can just pass a null)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
|||||||
allow-unwrap-in-tests = true
|
|
||||||
allow-expect-in-tests = true
|
|
||||||
@@ -11,8 +11,6 @@ use cosmwasm_std::Event;
|
|||||||
/// * `event`: event to search through.
|
/// * `event`: event to search through.
|
||||||
/// * `key`: key associated with the particular attribute
|
/// * `key`: key associated with the particular attribute
|
||||||
pub fn must_find_attribute(event: &Event, key: &str) -> String {
|
pub fn must_find_attribute(event: &Event, key: &str) -> String {
|
||||||
// due to how the function is supposed to work, the unwrap is fine in this instance
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
may_find_attribute(event, key).unwrap()
|
may_find_attribute(event, key).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
#![warn(clippy::expect_used)]
|
|
||||||
#![warn(clippy::unwrap_used)]
|
|
||||||
|
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ impl Percent {
|
|||||||
|
|
||||||
impl Display for Percent {
|
impl Display for Percent {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
let adjusted = Decimal::from_ratio(100u32, 1u32) * self.0;
|
let adjusted = Decimal::from_atomics(100u32, 0).unwrap() * self.0;
|
||||||
write!(f, "{}%", adjusted)
|
write!(f, "{}%", adjusted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use cosmwasm_std::{Decimal, Uint128};
|
use cosmwasm_std::Decimal;
|
||||||
|
|
||||||
pub const TOKEN_SUPPLY: Uint128 = Uint128::new(1_000_000_000_000_000);
|
|
||||||
|
|
||||||
// I'm still not 100% sure how to feel about existence of this file
|
// 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.
|
// This is equivalent of representing our display coin with 6 decimal places.
|
||||||
|
|||||||
@@ -4,10 +4,8 @@
|
|||||||
// due to code generated by JsonSchema
|
// due to code generated by JsonSchema
|
||||||
#![allow(clippy::field_reassign_with_default)]
|
#![allow(clippy::field_reassign_with_default)]
|
||||||
|
|
||||||
use crate::constants::TOKEN_SUPPLY;
|
|
||||||
use crate::helpers::IntoBaseDecimal;
|
|
||||||
use crate::{Addr, MixId};
|
use crate::{Addr, MixId};
|
||||||
use cosmwasm_std::{Coin, Decimal, StdResult};
|
use cosmwasm_std::{Coin, Decimal};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -62,11 +60,6 @@ impl Delegation {
|
|||||||
height: u64,
|
height: u64,
|
||||||
proxy: Option<Addr>,
|
proxy: Option<Addr>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(
|
|
||||||
amount.amount <= TOKEN_SUPPLY,
|
|
||||||
"delegation cannot be larger than the token supply"
|
|
||||||
);
|
|
||||||
|
|
||||||
Delegation {
|
Delegation {
|
||||||
owner,
|
owner,
|
||||||
mix_id,
|
mix_id,
|
||||||
@@ -94,8 +87,10 @@ impl Delegation {
|
|||||||
(mix_id, owner_proxy_subkey)
|
(mix_id, owner_proxy_subkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dec_amount(&self) -> StdResult<Decimal> {
|
pub fn dec_amount(&self) -> Decimal {
|
||||||
self.amount.amount.into_base_decimal()
|
// the unwrap here is fine as we're guaranteed our base coin amount is going to fit in a Decimal
|
||||||
|
// with 0 decimal places
|
||||||
|
Decimal::from_atomics(self.amount.amount, 0).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn proxy_storage_key(&self) -> OwnerProxySubKey {
|
pub fn proxy_storage_key(&self) -> OwnerProxySubKey {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use cosmwasm_std::{Decimal, StdError, StdResult, Uint128};
|
use cosmwasm_std::Decimal;
|
||||||
|
|
||||||
pub fn compare_decimals(a: Decimal, b: Decimal, epsilon: Option<Decimal>) {
|
pub fn compare_decimals(a: Decimal, b: Decimal, epsilon: Option<Decimal>) {
|
||||||
let epsilon = epsilon.unwrap_or_else(|| Decimal::from_ratio(1u128, 100_000_000u128));
|
let epsilon = epsilon.unwrap_or_else(|| Decimal::from_ratio(1u128, 100_000_000u128));
|
||||||
@@ -11,23 +11,3 @@ pub fn compare_decimals(a: Decimal, b: Decimal, epsilon: Option<Decimal>) {
|
|||||||
assert!(b - a < epsilon, "{} != {}", a, b)
|
assert!(b - a < epsilon, "{} != {}", a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_base_decimal(val: impl Into<Uint128>) -> StdResult<Decimal> {
|
|
||||||
val.into_base_decimal()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait IntoBaseDecimal {
|
|
||||||
fn into_base_decimal(self) -> StdResult<Decimal>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IntoBaseDecimal for T
|
|
||||||
where
|
|
||||||
T: Into<Uint128>,
|
|
||||||
{
|
|
||||||
fn into_base_decimal(self) -> StdResult<Decimal> {
|
|
||||||
let atomics = self.into();
|
|
||||||
Decimal::from_atomics(atomics, 0).map_err(|_| StdError::GenericErr {
|
|
||||||
msg: format!("Decimal range exceeded for {atomics} with 0 decimal places."),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -145,17 +145,14 @@ impl JsonSchema for Interval {
|
|||||||
impl Interval {
|
impl Interval {
|
||||||
/// Initialize epoch in the contract with default values.
|
/// Initialize epoch in the contract with default values.
|
||||||
pub fn init_interval(epochs_in_interval: u32, epoch_length: Duration, env: &Env) -> Self {
|
pub fn init_interval(epochs_in_interval: u32, epoch_length: Duration, env: &Env) -> Self {
|
||||||
// if this fails it means the value provided from the chain itself (via cosmwasm) is invalid,
|
|
||||||
// so we really have to panic here as anything beyond that point would be invalid anyway
|
|
||||||
#[allow(clippy::expect_used)]
|
|
||||||
let current_epoch_start =
|
|
||||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64)
|
|
||||||
.expect("The timestamp provided via env.block.time is invalid");
|
|
||||||
|
|
||||||
Interval {
|
Interval {
|
||||||
id: 0,
|
id: 0,
|
||||||
epochs_in_interval,
|
epochs_in_interval,
|
||||||
current_epoch_start,
|
// I really don't see a way for this to fail, unless the blockchain is lying to us
|
||||||
|
current_epoch_start: OffsetDateTime::from_unix_timestamp(
|
||||||
|
env.block.time.seconds() as i64
|
||||||
|
)
|
||||||
|
.expect("Invalid timestamp from env.block.time"),
|
||||||
current_epoch_id: 0,
|
current_epoch_id: 0,
|
||||||
epoch_length,
|
epoch_length,
|
||||||
total_elapsed_epochs: 0,
|
total_elapsed_epochs: 0,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
#![warn(clippy::expect_used)]
|
|
||||||
#![warn(clippy::unwrap_used)]
|
|
||||||
|
|
||||||
mod constants;
|
mod constants;
|
||||||
pub mod delegation;
|
pub mod delegation;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
// due to code generated by JsonSchema
|
// due to code generated by JsonSchema
|
||||||
#![allow(clippy::field_reassign_with_default)]
|
#![allow(clippy::field_reassign_with_default)]
|
||||||
|
|
||||||
use crate::constants::{TOKEN_SUPPLY, UNIT_DELEGATION_BASE};
|
use crate::constants::UNIT_DELEGATION_BASE;
|
||||||
use crate::error::MixnetContractError;
|
use crate::error::MixnetContractError;
|
||||||
use crate::helpers::IntoBaseDecimal;
|
|
||||||
use crate::reward_params::{NodeRewardParams, RewardingParams};
|
use crate::reward_params::{NodeRewardParams, RewardingParams};
|
||||||
use crate::rewarding::helpers::truncate_reward;
|
use crate::rewarding::helpers::truncate_reward;
|
||||||
use crate::rewarding::RewardDistribution;
|
use crate::rewarding::RewardDistribution;
|
||||||
use crate::{Delegation, EpochId, IdentityKey, MixId, Percent, SphinxKey};
|
use crate::{Delegation, EpochId, IdentityKey, MixId, Percent, SphinxKey};
|
||||||
use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128};
|
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
@@ -65,7 +64,7 @@ impl MixNodeDetails {
|
|||||||
self.rewarding_details.pending_operator_reward(pledge)
|
self.rewarding_details.pending_operator_reward(pledge)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pending_detailed_operator_reward(&self) -> StdResult<Decimal> {
|
pub fn pending_detailed_operator_reward(&self) -> Decimal {
|
||||||
let pledge = self.original_pledge();
|
let pledge = self.original_pledge();
|
||||||
self.rewarding_details
|
self.rewarding_details
|
||||||
.pending_detailed_operator_reward(pledge)
|
.pending_detailed_operator_reward(pledge)
|
||||||
@@ -108,21 +107,16 @@ impl MixNodeRewarding {
|
|||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
initial_pledge: &Coin,
|
initial_pledge: &Coin,
|
||||||
current_epoch: EpochId,
|
current_epoch: EpochId,
|
||||||
) -> Result<Self, MixnetContractError> {
|
) -> Self {
|
||||||
assert!(
|
MixNodeRewarding {
|
||||||
initial_pledge.amount <= TOKEN_SUPPLY,
|
|
||||||
"pledge cannot be larger than the token supply"
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(MixNodeRewarding {
|
|
||||||
cost_params,
|
cost_params,
|
||||||
operator: initial_pledge.amount.into_base_decimal()?,
|
operator: Decimal::from_atomics(initial_pledge.amount, 0).unwrap(),
|
||||||
delegates: Decimal::zero(),
|
delegates: Decimal::zero(),
|
||||||
total_unit_reward: Decimal::zero(),
|
total_unit_reward: Decimal::zero(),
|
||||||
unit_delegation: UNIT_DELEGATION_BASE,
|
unit_delegation: UNIT_DELEGATION_BASE,
|
||||||
last_rewarded_epoch: current_epoch,
|
last_rewarded_epoch: current_epoch,
|
||||||
unique_delegations: 0,
|
unique_delegations: 0,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether this node is still bonded. This is performed via a simple check,
|
/// Determines whether this node is still bonded. This is performed via a simple check,
|
||||||
@@ -141,30 +135,27 @@ impl MixNodeRewarding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pending_detailed_operator_reward(&self, original_pledge: &Coin) -> StdResult<Decimal> {
|
pub fn pending_detailed_operator_reward(&self, original_pledge: &Coin) -> Decimal {
|
||||||
let initial_dec = original_pledge.amount.into_base_decimal()?;
|
let initial_dec = Decimal::from_atomics(original_pledge.amount, 0).unwrap();
|
||||||
if initial_dec > self.operator {
|
if initial_dec > self.operator {
|
||||||
panic!(
|
panic!(
|
||||||
"seems slashing has occurred while it has not been implemented nor accounted for!"
|
"seems slashing has occurred while it has not been implemented nor accounted for!"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Ok(self.operator - initial_dec)
|
self.operator - initial_dec
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn operator_pledge_with_reward(&self, denom: impl Into<String>) -> Coin {
|
pub fn operator_pledge_with_reward(&self, denom: impl Into<String>) -> Coin {
|
||||||
truncate_reward(self.operator, denom)
|
truncate_reward(self.operator, denom)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pending_delegator_reward(&self, delegation: &Delegation) -> StdResult<Coin> {
|
pub fn pending_delegator_reward(&self, delegation: &Delegation) -> Coin {
|
||||||
let delegator_reward = self.determine_delegation_reward(delegation)?;
|
let delegator_reward = self.determine_delegation_reward(delegation);
|
||||||
Ok(truncate_reward(delegator_reward, &delegation.amount.denom))
|
truncate_reward(delegator_reward, &delegation.amount.denom)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn withdraw_operator_reward(
|
pub fn withdraw_operator_reward(&mut self, original_pledge: &Coin) -> Coin {
|
||||||
&mut self,
|
let initial_dec = Decimal::from_atomics(original_pledge.amount, 0).unwrap();
|
||||||
original_pledge: &Coin,
|
|
||||||
) -> Result<Coin, MixnetContractError> {
|
|
||||||
let initial_dec = original_pledge.amount.into_base_decimal()?;
|
|
||||||
if initial_dec > self.operator {
|
if initial_dec > self.operator {
|
||||||
panic!(
|
panic!(
|
||||||
"seems slashing has occurred while it has not been implemented nor accounted for!"
|
"seems slashing has occurred while it has not been implemented nor accounted for!"
|
||||||
@@ -173,14 +164,14 @@ impl MixNodeRewarding {
|
|||||||
let diff = self.operator - initial_dec;
|
let diff = self.operator - initial_dec;
|
||||||
self.operator = initial_dec;
|
self.operator = initial_dec;
|
||||||
|
|
||||||
Ok(truncate_reward(diff, &original_pledge.denom))
|
truncate_reward(diff, &original_pledge.denom)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn withdraw_delegator_reward(
|
pub fn withdraw_delegator_reward(
|
||||||
&mut self,
|
&mut self,
|
||||||
delegation: &mut Delegation,
|
delegation: &mut Delegation,
|
||||||
) -> Result<Coin, MixnetContractError> {
|
) -> Result<Coin, MixnetContractError> {
|
||||||
let reward = self.determine_delegation_reward(delegation)?;
|
let reward = self.determine_delegation_reward(delegation);
|
||||||
self.decrease_delegates_decimal(reward)?;
|
self.decrease_delegates_decimal(reward)?;
|
||||||
|
|
||||||
delegation.cumulative_reward_ratio = self.full_reward_ratio();
|
delegation.cumulative_reward_ratio = self.full_reward_ratio();
|
||||||
@@ -310,27 +301,23 @@ impl MixNodeRewarding {
|
|||||||
self.distribute_rewards(reward_distribution, absolute_epoch_id)
|
self.distribute_rewards(reward_distribution, absolute_epoch_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn determine_delegation_reward(&self, delegation: &Delegation) -> StdResult<Decimal> {
|
pub fn determine_delegation_reward(&self, delegation: &Delegation) -> Decimal {
|
||||||
let starting_ratio = delegation.cumulative_reward_ratio;
|
let starting_ratio = delegation.cumulative_reward_ratio;
|
||||||
let ending_ratio = self.full_reward_ratio();
|
let ending_ratio = self.full_reward_ratio();
|
||||||
let adjust = starting_ratio + self.unit_delegation;
|
let adjust = starting_ratio + self.unit_delegation;
|
||||||
|
|
||||||
Ok((ending_ratio - starting_ratio) * delegation.dec_amount()? / adjust)
|
(ending_ratio - starting_ratio) * delegation.dec_amount() / adjust
|
||||||
}
|
}
|
||||||
|
|
||||||
// this updates `unique_delegations` field
|
// this updates `unique_delegations` field
|
||||||
pub fn add_base_delegation(&mut self, amount: Uint128) -> Result<(), MixnetContractError> {
|
pub fn add_base_delegation(&mut self, amount: Uint128) {
|
||||||
self.increase_delegates_uint128(amount)?;
|
self.increase_delegates_uint128(amount);
|
||||||
self.unique_delegations += 1;
|
self.unique_delegations += 1;
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn increase_delegates_uint128(
|
pub fn increase_delegates_uint128(&mut self, amount: Uint128) {
|
||||||
&mut self,
|
// the unwrap here is fine as the value is guaranteed to fit under provided constraints
|
||||||
amount: Uint128,
|
self.delegates += Decimal::from_atomics(amount, 0).unwrap()
|
||||||
) -> Result<(), MixnetContractError> {
|
|
||||||
self.delegates += amount.into_base_decimal()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this updates `unique_delegations` field
|
// this updates `unique_delegations` field
|
||||||
@@ -348,7 +335,7 @@ impl MixNodeRewarding {
|
|||||||
&mut self,
|
&mut self,
|
||||||
amount: Uint128,
|
amount: Uint128,
|
||||||
) -> Result<(), MixnetContractError> {
|
) -> Result<(), MixnetContractError> {
|
||||||
let amount_dec = amount.into_base_decimal()?;
|
let amount_dec = Decimal::from_atomics(amount, 0).unwrap();
|
||||||
self.decrease_delegates_decimal(amount_dec)
|
self.decrease_delegates_decimal(amount_dec)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,8 +368,8 @@ impl MixNodeRewarding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn undelegate(&mut self, delegation: &Delegation) -> Result<Coin, MixnetContractError> {
|
pub fn undelegate(&mut self, delegation: &Delegation) -> Result<Coin, MixnetContractError> {
|
||||||
let reward = self.determine_delegation_reward(delegation)?;
|
let reward = self.determine_delegation_reward(delegation);
|
||||||
let full_amount = reward + delegation.dec_amount()?;
|
let full_amount = reward + delegation.dec_amount();
|
||||||
self.remove_delegation_decimal(full_amount)?;
|
self.remove_delegation_decimal(full_amount)?;
|
||||||
Ok(truncate_reward(full_amount, &delegation.amount.denom))
|
Ok(truncate_reward(full_amount, &delegation.amount.denom))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::delegation::OwnerProxySubKey;
|
use crate::delegation::OwnerProxySubKey;
|
||||||
use crate::error::MixnetContractError;
|
|
||||||
use crate::helpers::IntoBaseDecimal;
|
|
||||||
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||||
use crate::reward_params::{
|
use crate::reward_params::{
|
||||||
IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams,
|
IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams,
|
||||||
@@ -43,17 +41,14 @@ pub struct InitialRewardingParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InitialRewardingParams {
|
impl InitialRewardingParams {
|
||||||
pub fn into_rewarding_params(
|
pub fn into_rewarding_params(self, epochs_in_interval: u32) -> RewardingParams {
|
||||||
self,
|
|
||||||
epochs_in_interval: u32,
|
|
||||||
) -> Result<RewardingParams, MixnetContractError> {
|
|
||||||
let epoch_reward_budget = self.initial_reward_pool
|
let epoch_reward_budget = self.initial_reward_pool
|
||||||
/ epochs_in_interval.into_base_decimal()?
|
/ Decimal::from_atomics(epochs_in_interval, 0).unwrap()
|
||||||
* self.interval_pool_emission;
|
* self.interval_pool_emission;
|
||||||
let stake_saturation_point =
|
let stake_saturation_point =
|
||||||
self.initial_staking_supply / self.rewarded_set_size.into_base_decimal()?;
|
self.initial_staking_supply / Decimal::from_atomics(self.rewarded_set_size, 0).unwrap();
|
||||||
|
|
||||||
Ok(RewardingParams {
|
RewardingParams {
|
||||||
interval: IntervalRewardParams {
|
interval: IntervalRewardParams {
|
||||||
reward_pool: self.initial_reward_pool,
|
reward_pool: self.initial_reward_pool,
|
||||||
staking_supply: self.initial_staking_supply,
|
staking_supply: self.initial_staking_supply,
|
||||||
@@ -66,7 +61,7 @@ impl InitialRewardingParams {
|
|||||||
},
|
},
|
||||||
rewarded_set_size: self.rewarded_set_size,
|
rewarded_set_size: self.rewarded_set_size,
|
||||||
active_set_size: self.active_set_size,
|
active_set_size: self.active_set_size,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::helpers::IntoBaseDecimal;
|
|
||||||
use crate::{error::MixnetContractError, Percent};
|
use crate::{error::MixnetContractError, Percent};
|
||||||
use cosmwasm_std::Decimal;
|
use cosmwasm_std::Decimal;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
@@ -111,33 +110,24 @@ impl RewardingParams {
|
|||||||
pub fn dec_rewarded_set_size(&self) -> Decimal {
|
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
|
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
||||||
// with 0 decimal places
|
// with 0 decimal places
|
||||||
#[allow(clippy::unwrap_used)]
|
Decimal::from_atomics(self.rewarded_set_size, 0).unwrap()
|
||||||
self.rewarded_set_size.into_base_decimal().unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dec_active_set_size(&self) -> Decimal {
|
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
|
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
||||||
// with 0 decimal places
|
// with 0 decimal places
|
||||||
#[allow(clippy::unwrap_used)]
|
Decimal::from_atomics(self.active_set_size, 0).unwrap()
|
||||||
self.active_set_size.into_base_decimal().unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dec_standby_set_size(&self) -> Decimal {
|
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
|
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
||||||
// with 0 decimal places
|
// with 0 decimal places
|
||||||
#[allow(clippy::unwrap_used)]
|
Decimal::from_atomics(self.rewarded_set_size - self.active_set_size, 0).unwrap()
|
||||||
(self.rewarded_set_size - self.active_set_size)
|
|
||||||
.into_base_decimal()
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_epochs_in_interval_change(&mut self, new_epochs_in_interval: u32) {
|
pub fn apply_epochs_in_interval_change(&mut self, new_epochs_in_interval: u32) {
|
||||||
// the unwrap here is fine as we're guaranteed an `u32` is going to fit in a Decimal
|
self.interval.epoch_reward_budget = self.interval.reward_pool
|
||||||
// with 0 decimal places
|
/ Decimal::from_atomics(new_epochs_in_interval, 0).unwrap()
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
let new_epochs_in_interval = new_epochs_in_interval.into_base_decimal().unwrap();
|
|
||||||
|
|
||||||
self.interval.epoch_reward_budget = self.interval.reward_pool / new_epochs_in_interval
|
|
||||||
* self.interval.interval_pool_emission;
|
* self.interval.interval_pool_emission;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,13 +200,13 @@ impl RewardingParams {
|
|||||||
|
|
||||||
if recompute_epoch_budget {
|
if recompute_epoch_budget {
|
||||||
self.interval.epoch_reward_budget = self.interval.reward_pool
|
self.interval.epoch_reward_budget = self.interval.reward_pool
|
||||||
/ epochs_in_interval.into_base_decimal()?
|
/ Decimal::from_atomics(epochs_in_interval, 0).unwrap()
|
||||||
* self.interval.interval_pool_emission;
|
* self.interval.interval_pool_emission;
|
||||||
}
|
}
|
||||||
|
|
||||||
if recompute_saturation_point {
|
if recompute_saturation_point {
|
||||||
self.interval.stake_saturation_point =
|
self.interval.stake_saturation_point = self.interval.staking_supply
|
||||||
self.interval.staking_supply / self.rewarded_set_size.into_base_decimal()?
|
/ Decimal::from_atomics(self.rewarded_set_size, 0).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ pub struct RewardEstimate {
|
|||||||
pub operating_cost: Decimal,
|
pub operating_cost: Decimal,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)]
|
||||||
pub struct RewardDistribution {
|
pub struct RewardDistribution {
|
||||||
pub operator: Decimal,
|
pub operator: Decimal,
|
||||||
pub delegates: Decimal,
|
pub delegates: Decimal,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::error::MixnetContractError;
|
use crate::error::MixnetContractError;
|
||||||
use crate::helpers::IntoBaseDecimal;
|
|
||||||
use crate::reward_params::NodeRewardParams;
|
use crate::reward_params::NodeRewardParams;
|
||||||
use crate::rewarding::simulator::simulated_node::SimulatedNode;
|
use crate::rewarding::simulator::simulated_node::SimulatedNode;
|
||||||
use crate::rewarding::RewardDistribution;
|
use crate::rewarding::RewardDistribution;
|
||||||
@@ -34,7 +33,7 @@ impl Simulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance_epoch(&mut self) -> Result<(), MixnetContractError> {
|
fn advance_epoch(&mut self) {
|
||||||
let updated = self.interval.advance_epoch();
|
let updated = self.interval.advance_epoch();
|
||||||
|
|
||||||
// we rolled over an interval
|
// we rolled over an interval
|
||||||
@@ -48,13 +47,10 @@ impl Simulator {
|
|||||||
.staking_supply_scale_factor
|
.staking_supply_scale_factor
|
||||||
* self.pending_reward_pool_emission;
|
* self.pending_reward_pool_emission;
|
||||||
let epoch_reward_budget = reward_pool
|
let epoch_reward_budget = reward_pool
|
||||||
/ self.interval.epochs_in_interval().into_base_decimal()?
|
/ Decimal::from_atomics(self.interval.epochs_in_interval(), 0).unwrap()
|
||||||
* old.interval_pool_emission.value();
|
* old.interval_pool_emission.value();
|
||||||
let stake_saturation_point = staking_supply
|
let stake_saturation_point = staking_supply
|
||||||
/ self
|
/ Decimal::from_atomics(self.system_rewarding_params.rewarded_set_size, 0).unwrap();
|
||||||
.system_rewarding_params
|
|
||||||
.rewarded_set_size
|
|
||||||
.into_base_decimal()?;
|
|
||||||
|
|
||||||
let updated_params = RewardingParams {
|
let updated_params = RewardingParams {
|
||||||
interval: IntervalRewardParams {
|
interval: IntervalRewardParams {
|
||||||
@@ -75,15 +71,9 @@ impl Simulator {
|
|||||||
self.pending_reward_pool_emission = Decimal::zero();
|
self.pending_reward_pool_emission = Decimal::zero();
|
||||||
}
|
}
|
||||||
self.interval = updated;
|
self.interval = updated;
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bond(
|
pub fn bond(&mut self, pledge: Coin, cost_params: MixNodeCostParams) -> MixId {
|
||||||
&mut self,
|
|
||||||
pledge: Coin,
|
|
||||||
cost_params: MixNodeCostParams,
|
|
||||||
) -> Result<MixId, MixnetContractError> {
|
|
||||||
let mix_id = self.next_mix_id;
|
let mix_id = self.next_mix_id;
|
||||||
|
|
||||||
self.nodes.insert(
|
self.nodes.insert(
|
||||||
@@ -93,24 +83,16 @@ impl Simulator {
|
|||||||
cost_params,
|
cost_params,
|
||||||
&pledge,
|
&pledge,
|
||||||
self.interval.current_epoch_absolute_id(),
|
self.interval.current_epoch_absolute_id(),
|
||||||
)?,
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.next_mix_id += 1;
|
self.next_mix_id += 1;
|
||||||
|
|
||||||
Ok(mix_id)
|
mix_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delegate<S: Into<String>>(
|
pub fn delegate<S: Into<String>>(&mut self, delegator: S, delegation: Coin, mix_id: MixId) {
|
||||||
&mut self,
|
let node = self.nodes.get_mut(&mix_id).expect("node doesn't exist");
|
||||||
delegator: S,
|
|
||||||
delegation: Coin,
|
|
||||||
mix_id: MixId,
|
|
||||||
) -> Result<(), MixnetContractError> {
|
|
||||||
let node = self
|
|
||||||
.nodes
|
|
||||||
.get_mut(&mix_id)
|
|
||||||
.ok_or(MixnetContractError::MixNodeBondNotFound { mix_id })?;
|
|
||||||
node.delegate(delegator, delegation)
|
node.delegate(delegator, delegation)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,35 +103,23 @@ impl Simulator {
|
|||||||
delegator: S,
|
delegator: S,
|
||||||
mix_id: MixId,
|
mix_id: MixId,
|
||||||
) -> Result<(Coin, Coin), MixnetContractError> {
|
) -> Result<(Coin, Coin), MixnetContractError> {
|
||||||
let node = self
|
let node = self.nodes.get_mut(&mix_id).expect("node not found");
|
||||||
.nodes
|
|
||||||
.get_mut(&mix_id)
|
|
||||||
.ok_or(MixnetContractError::MixNodeBondNotFound { mix_id })?;
|
|
||||||
node.undelegate(delegator)
|
node.undelegate(delegator)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simulate_epoch_single_node(
|
pub fn simulate_epoch_single_node(&mut self, params: NodeRewardParams) -> RewardDistribution {
|
||||||
&mut self,
|
|
||||||
params: NodeRewardParams,
|
|
||||||
) -> Result<RewardDistribution, MixnetContractError> {
|
|
||||||
assert_eq!(self.nodes.len(), 1);
|
assert_eq!(self.nodes.len(), 1);
|
||||||
|
|
||||||
if let Some(&id) = self.nodes.keys().next() {
|
let id = *self.nodes.keys().next().unwrap();
|
||||||
let mut params_map = BTreeMap::new();
|
let mut params_map = BTreeMap::new();
|
||||||
params_map.insert(id, params);
|
params_map.insert(id, params);
|
||||||
Ok(self
|
self.simulate_epoch(¶ms_map).remove(&id).unwrap()
|
||||||
.simulate_epoch(¶ms_map)?
|
|
||||||
.remove(&id)
|
|
||||||
.unwrap_or_default())
|
|
||||||
} else {
|
|
||||||
Ok(RewardDistribution::default())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simulate_epoch(
|
pub fn simulate_epoch(
|
||||||
&mut self,
|
&mut self,
|
||||||
node_params: &BTreeMap<MixId, NodeRewardParams>,
|
node_params: &BTreeMap<MixId, NodeRewardParams>,
|
||||||
) -> Result<BTreeMap<MixId, RewardDistribution>, MixnetContractError> {
|
) -> BTreeMap<MixId, RewardDistribution> {
|
||||||
let mut params_keys = node_params.keys().copied().collect::<Vec<_>>();
|
let mut params_keys = node_params.keys().copied().collect::<Vec<_>>();
|
||||||
params_keys.sort_unstable();
|
params_keys.sort_unstable();
|
||||||
let mut node_keys = self.nodes.keys().copied().collect::<Vec<_>>();
|
let mut node_keys = self.nodes.keys().copied().collect::<Vec<_>>();
|
||||||
@@ -177,41 +147,34 @@ impl Simulator {
|
|||||||
dist.insert(*mix_id, reward_distribution);
|
dist.insert(*mix_id, reward_distribution);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.advance_epoch()?;
|
self.advance_epoch();
|
||||||
Ok(dist)
|
dist
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn determine_delegation_reward(
|
pub fn determine_delegation_reward(&self, delegation: &Delegation) -> Decimal {
|
||||||
&self,
|
self.nodes[&delegation.mix_id]
|
||||||
delegation: &Delegation,
|
|
||||||
) -> Result<Decimal, MixnetContractError> {
|
|
||||||
Ok(self.nodes[&delegation.mix_id]
|
|
||||||
.rewarding_details
|
.rewarding_details
|
||||||
.determine_delegation_reward(delegation)?)
|
.determine_delegation_reward(delegation)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn determine_total_delegation_reward(&self) -> Result<Decimal, MixnetContractError> {
|
pub fn determine_total_delegation_reward(&self) -> Decimal {
|
||||||
let mut total = Decimal::zero();
|
let mut total = Decimal::zero();
|
||||||
|
|
||||||
for node in self.nodes.values() {
|
for node in self.nodes.values() {
|
||||||
for delegation in node.delegations.values() {
|
for delegation in node.delegations.values() {
|
||||||
total += node
|
total += node
|
||||||
.rewarding_details
|
.rewarding_details
|
||||||
.determine_delegation_reward(delegation)?
|
.determine_delegation_reward(delegation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(total)
|
total
|
||||||
}
|
}
|
||||||
|
|
||||||
// assume node state doesn't change in the interval (kinda unrealistic)
|
// assume node state doesn't change in the interval (kinda unrealistic)
|
||||||
pub fn simulate_full_interval(
|
pub fn simulate_full_interval(&mut self, node_params: &BTreeMap<MixId, NodeRewardParams>) {
|
||||||
&mut self,
|
|
||||||
node_params: &BTreeMap<MixId, NodeRewardParams>,
|
|
||||||
) -> Result<(), MixnetContractError> {
|
|
||||||
for _ in 0..self.interval.epochs_in_interval() {
|
for _ in 0..self.interval.epochs_in_interval() {
|
||||||
self.simulate_epoch(node_params)?;
|
self.simulate_epoch(node_params);
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,10 +197,6 @@ mod tests {
|
|||||||
use cosmwasm_std::testing::mock_env;
|
use cosmwasm_std::testing::mock_env;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
// explicitly marking this as part of #[allow(clippy::unwrap_used)] until
|
|
||||||
// https://github.com/rust-lang/rust-clippy/pull/9686
|
|
||||||
// is merged into a release
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
fn base_simulator(initial_pledge: u128) -> Simulator {
|
fn base_simulator(initial_pledge: u128) -> Simulator {
|
||||||
let profit_margin = Percent::from_percentage_value(10).unwrap();
|
let profit_margin = Percent::from_percentage_value(10).unwrap();
|
||||||
let interval_operating_cost = Coin::new(40_000_000, "unym");
|
let interval_operating_cost = Coin::new(40_000_000, "unym");
|
||||||
@@ -279,32 +238,20 @@ mod tests {
|
|||||||
profit_margin_percent: profit_margin,
|
profit_margin_percent: profit_margin,
|
||||||
interval_operating_cost,
|
interval_operating_cost,
|
||||||
};
|
};
|
||||||
simulator.bond(initial_pledge, cost_params).unwrap();
|
simulator.bond(initial_pledge, cost_params);
|
||||||
simulator
|
simulator
|
||||||
}
|
}
|
||||||
|
|
||||||
// essentially our delegations + estimated rewards HAVE TO equal to what we actually determined
|
// essentially our delegations + estimated rewards HAVE TO equal to what we actually determined
|
||||||
//
|
|
||||||
// explicitly marking this as part of #[allow(clippy::unwrap_used)] until
|
|
||||||
// https://github.com/rust-lang/rust-clippy/pull/9686
|
|
||||||
// is merged into a release
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
fn check_rewarding_invariant(simulator: &Simulator) {
|
fn check_rewarding_invariant(simulator: &Simulator) {
|
||||||
for node in simulator.nodes.values() {
|
for node in simulator.nodes.values() {
|
||||||
let delegation_sum: Decimal = node
|
let delegation_sum: Decimal =
|
||||||
.delegations
|
node.delegations.values().map(|d| d.dec_amount()).sum();
|
||||||
.values()
|
|
||||||
.map(|d| d.dec_amount().unwrap())
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
let reward_sum: Decimal = node
|
let reward_sum: Decimal = node
|
||||||
.delegations
|
.delegations
|
||||||
.values()
|
.values()
|
||||||
.map(|d| {
|
.map(|d| node.rewarding_details.determine_delegation_reward(d))
|
||||||
node.rewarding_details
|
|
||||||
.determine_delegation_reward(d)
|
|
||||||
.unwrap()
|
|
||||||
})
|
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
// let reward_sum = simulator.determine_total_delegation_reward();
|
// let reward_sum = simulator.determine_total_delegation_reward();
|
||||||
@@ -322,7 +269,7 @@ mod tests {
|
|||||||
|
|
||||||
let epoch_params =
|
let epoch_params =
|
||||||
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
||||||
let rewards = simulator.simulate_epoch_single_node(epoch_params).unwrap();
|
let rewards = simulator.simulate_epoch_single_node(epoch_params);
|
||||||
|
|
||||||
assert_eq!(rewards.delegates, Decimal::zero());
|
assert_eq!(rewards.delegates, Decimal::zero());
|
||||||
compare_decimals(
|
compare_decimals(
|
||||||
@@ -335,13 +282,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn single_delegation_at_genesis() {
|
fn single_delegation_at_genesis() {
|
||||||
let mut simulator = base_simulator(10000_000000);
|
let mut simulator = base_simulator(10000_000000);
|
||||||
simulator
|
simulator.delegate("alice", Coin::new(18000_000000, "unym"), 0);
|
||||||
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let node_params =
|
let node_params =
|
||||||
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
||||||
let rewards = simulator.simulate_epoch_single_node(node_params).unwrap();
|
let rewards = simulator.simulate_epoch_single_node(node_params);
|
||||||
|
|
||||||
compare_decimals(
|
compare_decimals(
|
||||||
rewards.delegates,
|
rewards.delegates,
|
||||||
@@ -352,7 +297,7 @@ mod tests {
|
|||||||
|
|
||||||
compare_decimals(
|
compare_decimals(
|
||||||
rewards.delegates,
|
rewards.delegates,
|
||||||
simulator.determine_total_delegation_reward().unwrap(),
|
simulator.determine_total_delegation_reward(),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let node = &simulator.nodes[&0];
|
let node = &simulator.nodes[&0];
|
||||||
@@ -372,22 +317,20 @@ mod tests {
|
|||||||
let node_params =
|
let node_params =
|
||||||
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
NodeRewardParams::new(Percent::from_percentage_value(100).unwrap(), true);
|
||||||
|
|
||||||
let rewards1 = simulator.simulate_epoch_single_node(node_params).unwrap();
|
let rewards1 = simulator.simulate_epoch_single_node(node_params);
|
||||||
let expected_operator1 = "1128452.5416104363".parse().unwrap();
|
let expected_operator1 = "1128452.5416104363".parse().unwrap();
|
||||||
assert_eq!(rewards1.delegates, Decimal::zero());
|
assert_eq!(rewards1.delegates, Decimal::zero());
|
||||||
compare_decimals(rewards1.operator, expected_operator1, None);
|
compare_decimals(rewards1.operator, expected_operator1, None);
|
||||||
|
|
||||||
simulator
|
simulator.delegate("alice", Coin::new(18000_000000, "unym"), 0);
|
||||||
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let rewards2 = simulator.simulate_epoch_single_node(node_params).unwrap();
|
let rewards2 = simulator.simulate_epoch_single_node(node_params);
|
||||||
let expected_operator2 = "1363843.413584609".parse().unwrap();
|
let expected_operator2 = "1363843.413584609".parse().unwrap();
|
||||||
let expected_delegator_reward1 = "1795952.25874404".parse().unwrap();
|
let expected_delegator_reward1 = "1795952.25874404".parse().unwrap();
|
||||||
compare_decimals(rewards2.delegates, expected_delegator_reward1, None);
|
compare_decimals(rewards2.delegates, expected_delegator_reward1, None);
|
||||||
compare_decimals(rewards2.operator, expected_operator2, None);
|
compare_decimals(rewards2.operator, expected_operator2, None);
|
||||||
|
|
||||||
let rewards3 = simulator.simulate_epoch_single_node(node_params).unwrap();
|
let rewards3 = simulator.simulate_epoch_single_node(node_params);
|
||||||
let expected_operator3 = "1364017.7824440491".parse().unwrap();
|
let expected_operator3 = "1364017.7824440491".parse().unwrap();
|
||||||
let expected_delegator_reward2 = "1796135.9269468693".parse().unwrap();
|
let expected_delegator_reward2 = "1796135.9269468693".parse().unwrap();
|
||||||
compare_decimals(rewards3.delegates, expected_delegator_reward2, None);
|
compare_decimals(rewards3.delegates, expected_delegator_reward2, None);
|
||||||
@@ -421,15 +364,11 @@ mod tests {
|
|||||||
|
|
||||||
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
|
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
|
||||||
// delegating at different times still work)
|
// delegating at different times still work)
|
||||||
simulator
|
simulator.delegate("alice", Coin::new(18000_000000, "unym"), 0);
|
||||||
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
|
simulator.delegate("bob", Coin::new(4000_000000, "unym"), 0);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("bob", Coin::new(4000_000000, "unym"), 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// "normal", sanity check rewarding
|
// "normal", sanity check rewarding
|
||||||
let rewards1 = simulator.simulate_epoch_single_node(node_params).unwrap();
|
let rewards1 = simulator.simulate_epoch_single_node(node_params);
|
||||||
let expected_operator1 = "1411087.1007647323".parse().unwrap();
|
let expected_operator1 = "1411087.1007647323".parse().unwrap();
|
||||||
let expected_delegator_reward1 = "2199961.032388664".parse().unwrap();
|
let expected_delegator_reward1 = "2199961.032388664".parse().unwrap();
|
||||||
compare_decimals(rewards1.delegates, expected_delegator_reward1, None);
|
compare_decimals(rewards1.delegates, expected_delegator_reward1, None);
|
||||||
@@ -439,15 +378,14 @@ mod tests {
|
|||||||
let node = simulator.nodes.get_mut(&0).unwrap();
|
let node = simulator.nodes.get_mut(&0).unwrap();
|
||||||
let reward = node
|
let reward = node
|
||||||
.rewarding_details
|
.rewarding_details
|
||||||
.withdraw_operator_reward(&original_pledge)
|
.withdraw_operator_reward(&original_pledge);
|
||||||
.unwrap();
|
|
||||||
assert_eq!(reward.amount, truncate_reward_amount(expected_operator1));
|
assert_eq!(reward.amount, truncate_reward_amount(expected_operator1));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
node.rewarding_details.operator,
|
node.rewarding_details.operator,
|
||||||
Decimal::from_atomics(original_pledge.amount, 0).unwrap()
|
Decimal::from_atomics(original_pledge.amount, 0).unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
let rewards2 = simulator.simulate_epoch_single_node(node_params).unwrap();
|
let rewards2 = simulator.simulate_epoch_single_node(node_params);
|
||||||
let expected_operator2 = "1411113.0004067947".parse().unwrap();
|
let expected_operator2 = "1411113.0004067947".parse().unwrap();
|
||||||
let expected_delegator_reward2 = "2200183.3879084454".parse().unwrap();
|
let expected_delegator_reward2 = "2200183.3879084454".parse().unwrap();
|
||||||
compare_decimals(rewards2.delegates, expected_delegator_reward2, None);
|
compare_decimals(rewards2.delegates, expected_delegator_reward2, None);
|
||||||
@@ -464,15 +402,11 @@ mod tests {
|
|||||||
|
|
||||||
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
|
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
|
||||||
// delegating at different times still work)
|
// delegating at different times still work)
|
||||||
simulator
|
simulator.delegate("alice", Coin::new(18000_000000, "unym"), 0);
|
||||||
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
|
simulator.delegate("bob", Coin::new(4000_000000, "unym"), 0);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("bob", Coin::new(4000_000000, "unym"), 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// "normal", sanity check rewarding
|
// "normal", sanity check rewarding
|
||||||
let rewards1 = simulator.simulate_epoch_single_node(node_params).unwrap();
|
let rewards1 = simulator.simulate_epoch_single_node(node_params);
|
||||||
let expected_operator1 = "1411087.1007647323".parse().unwrap();
|
let expected_operator1 = "1411087.1007647323".parse().unwrap();
|
||||||
let expected_delegator_reward1 = "2199961.032388664".parse().unwrap();
|
let expected_delegator_reward1 = "2199961.032388664".parse().unwrap();
|
||||||
compare_decimals(rewards1.delegates, expected_delegator_reward1, None);
|
compare_decimals(rewards1.delegates, expected_delegator_reward1, None);
|
||||||
@@ -490,7 +424,7 @@ mod tests {
|
|||||||
assert_eq!(reward.amount, truncate_reward_amount(expected_del1_reward));
|
assert_eq!(reward.amount, truncate_reward_amount(expected_del1_reward));
|
||||||
|
|
||||||
// new reward after withdrawal
|
// new reward after withdrawal
|
||||||
let rewards2 = simulator.simulate_epoch_single_node(node_params).unwrap();
|
let rewards2 = simulator.simulate_epoch_single_node(node_params);
|
||||||
let expected_operator2 = "1411250.1907492676".parse().unwrap();
|
let expected_operator2 = "1411250.1907492676".parse().unwrap();
|
||||||
let expected_delegator_reward2 = "2200004.051009689".parse().unwrap();
|
let expected_delegator_reward2 = "2200004.051009689".parse().unwrap();
|
||||||
compare_decimals(rewards2.delegates, expected_delegator_reward2, None);
|
compare_decimals(rewards2.delegates, expected_delegator_reward2, None);
|
||||||
@@ -533,30 +467,22 @@ mod tests {
|
|||||||
let mut performance = Percent::from_percentage_value(100).unwrap();
|
let mut performance = Percent::from_percentage_value(100).unwrap();
|
||||||
for epoch in 0..720 {
|
for epoch in 0..720 {
|
||||||
if epoch == 0 {
|
if epoch == 0 {
|
||||||
simulator
|
simulator.delegate("a", Coin::new(18000_000000, "unym"), 0)
|
||||||
.delegate("a", Coin::new(18000_000000, "unym"), 0)
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
if epoch == 42 {
|
if epoch == 42 {
|
||||||
simulator
|
simulator.delegate("b", Coin::new(2000_000000, "unym"), 0)
|
||||||
.delegate("b", Coin::new(2000_000000, "unym"), 0)
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
if epoch == 89 {
|
if epoch == 89 {
|
||||||
is_active = false;
|
is_active = false;
|
||||||
}
|
}
|
||||||
if epoch == 123 {
|
if epoch == 123 {
|
||||||
simulator
|
simulator.delegate("c", Coin::new(6666_000000, "unym"), 0)
|
||||||
.delegate("c", Coin::new(6666_000000, "unym"), 0)
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
if epoch == 167 {
|
if epoch == 167 {
|
||||||
performance = Percent::from_percentage_value(90).unwrap();
|
performance = Percent::from_percentage_value(90).unwrap();
|
||||||
}
|
}
|
||||||
if epoch == 245 {
|
if epoch == 245 {
|
||||||
simulator
|
simulator.delegate("d", Coin::new(2050_000000, "unym"), 0)
|
||||||
.delegate("d", Coin::new(2050_000000, "unym"), 0)
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
if epoch == 264 {
|
if epoch == 264 {
|
||||||
let (delegation, _reward) = simulator.undelegate("b", 0).unwrap();
|
let (delegation, _reward) = simulator.undelegate("b", 0).unwrap();
|
||||||
@@ -577,15 +503,13 @@ mod tests {
|
|||||||
// TODO: figure out if there's a good way to verify whether `reward` is what we expect it to be
|
// TODO: figure out if there's a good way to verify whether `reward` is what we expect it to be
|
||||||
}
|
}
|
||||||
if epoch == 545 {
|
if epoch == 545 {
|
||||||
simulator
|
simulator.delegate("e", Coin::new(5000_000000, "unym"), 0)
|
||||||
.delegate("e", Coin::new(5000_000000, "unym"), 0)
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this has to always hold
|
// this has to always hold
|
||||||
check_rewarding_invariant(&simulator);
|
check_rewarding_invariant(&simulator);
|
||||||
let node_params = NodeRewardParams::new(performance, is_active);
|
let node_params = NodeRewardParams::new(performance, is_active);
|
||||||
simulator.simulate_epoch_single_node(node_params).unwrap();
|
simulator.simulate_epoch_single_node(node_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// after everyone undelegates, there should be nothing left in the delegates pool
|
// after everyone undelegates, there should be nothing left in the delegates pool
|
||||||
@@ -639,135 +563,95 @@ mod tests {
|
|||||||
|
|
||||||
let mut simulator = Simulator::new(rewarding_params, interval);
|
let mut simulator = Simulator::new(rewarding_params, interval);
|
||||||
|
|
||||||
let n0 = simulator
|
let n0 = simulator.bond(
|
||||||
.bond(
|
Coin::new(11_000_000_000000, "unym"),
|
||||||
Coin::new(11_000_000_000000, "unym"),
|
MixNodeCostParams {
|
||||||
MixNodeCostParams {
|
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
},
|
||||||
},
|
);
|
||||||
)
|
simulator.delegate("delegator", Coin::new(1_000_000_000000, "unym"), n0);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("delegator", Coin::new(1_000_000_000000, "unym"), n0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n1 = simulator
|
let n1 = simulator.bond(
|
||||||
.bond(
|
Coin::new(1_000_000_000000, "unym"),
|
||||||
Coin::new(1_000_000_000000, "unym"),
|
MixNodeCostParams {
|
||||||
MixNodeCostParams {
|
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
},
|
||||||
},
|
);
|
||||||
)
|
simulator.delegate("delegator", Coin::new(11_000_000_000000, "unym"), n1);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("delegator", Coin::new(11_000_000_000000, "unym"), n1)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n2 = simulator
|
let n2 = simulator.bond(
|
||||||
.bond(
|
Coin::new(1_000_000_000000, "unym"),
|
||||||
Coin::new(1_000_000_000000, "unym"),
|
MixNodeCostParams {
|
||||||
MixNodeCostParams {
|
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
},
|
||||||
},
|
);
|
||||||
)
|
simulator.delegate("delegator", Coin::new(9_000_000_000000, "unym"), n2);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("delegator", Coin::new(9_000_000_000000, "unym"), n2)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n3 = simulator
|
let n3 = simulator.bond(
|
||||||
.bond(
|
Coin::new(1_000_000_000000, "unym"),
|
||||||
Coin::new(1_000_000_000000, "unym"),
|
MixNodeCostParams {
|
||||||
MixNodeCostParams {
|
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
|
||||||
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
|
interval_operating_cost: Coin::new(500_000_000, "unym"),
|
||||||
interval_operating_cost: Coin::new(500_000_000, "unym"),
|
},
|
||||||
},
|
);
|
||||||
)
|
simulator.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n3);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n3)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n4 = simulator
|
let n4 = simulator.bond(
|
||||||
.bond(
|
Coin::new(1000_000000, "unym"),
|
||||||
Coin::new(1000_000000, "unym"),
|
MixNodeCostParams {
|
||||||
MixNodeCostParams {
|
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
},
|
||||||
},
|
);
|
||||||
)
|
simulator.delegate("delegator", Coin::new(7_999_000_000000, "unym"), n4);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("delegator", Coin::new(7_999_000_000000, "unym"), n4)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n5 = simulator
|
let n5 = simulator.bond(
|
||||||
.bond(
|
Coin::new(1_000_000_000000, "unym"),
|
||||||
Coin::new(1_000_000_000000, "unym"),
|
MixNodeCostParams {
|
||||||
MixNodeCostParams {
|
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
},
|
||||||
},
|
);
|
||||||
)
|
simulator.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n5);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n5)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n6 = simulator
|
let n6 = simulator.bond(
|
||||||
.bond(
|
Coin::new(11_000_000_000000, "unym"),
|
||||||
Coin::new(11_000_000_000000, "unym"),
|
MixNodeCostParams {
|
||||||
MixNodeCostParams {
|
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
},
|
||||||
},
|
);
|
||||||
)
|
simulator.delegate("delegator", Coin::new(1_000_000_000000, "unym"), n6);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("delegator", Coin::new(1_000_000_000000, "unym"), n6)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n7 = simulator
|
let n7 = simulator.bond(
|
||||||
.bond(
|
Coin::new(1_000_000_000000, "unym"),
|
||||||
Coin::new(1_000_000_000000, "unym"),
|
MixNodeCostParams {
|
||||||
MixNodeCostParams {
|
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
},
|
||||||
},
|
);
|
||||||
)
|
simulator.delegate("delegator", Coin::new(9_000_000_000000, "unym"), n7);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("delegator", Coin::new(9_000_000_000000, "unym"), n7)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n8 = simulator
|
let n8 = simulator.bond(
|
||||||
.bond(
|
Coin::new(1_000_000_000000, "unym"),
|
||||||
Coin::new(1_000_000_000000, "unym"),
|
MixNodeCostParams {
|
||||||
MixNodeCostParams {
|
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
|
||||||
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
|
interval_operating_cost: Coin::new(500_000_000, "unym"),
|
||||||
interval_operating_cost: Coin::new(500_000_000, "unym"),
|
},
|
||||||
},
|
);
|
||||||
)
|
simulator.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n8);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n8)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n9 = simulator
|
let n9 = simulator.bond(
|
||||||
.bond(
|
Coin::new(1_000_000_000000, "unym"),
|
||||||
Coin::new(1_000_000_000000, "unym"),
|
MixNodeCostParams {
|
||||||
MixNodeCostParams {
|
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
||||||
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
|
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
||||||
interval_operating_cost: Coin::new(40_000_000, "unym"),
|
},
|
||||||
},
|
);
|
||||||
)
|
simulator.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n9);
|
||||||
.unwrap();
|
|
||||||
simulator
|
|
||||||
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n9)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let uptime_1 = Percent::from_percentage_value(100).unwrap();
|
let uptime_1 = Percent::from_percentage_value(100).unwrap();
|
||||||
let uptime_09 = Percent::from_percentage_value(90).unwrap();
|
let uptime_09 = Percent::from_percentage_value(90).unwrap();
|
||||||
@@ -789,7 +673,7 @@ mod tests {
|
|||||||
.collect::<BTreeMap<_, _>>();
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
|
||||||
for _ in 0..23 {
|
for _ in 0..23 {
|
||||||
simulator.simulate_full_interval(&node_params).unwrap();
|
simulator.simulate_full_interval(&node_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we allow the delta to be within 0.1unym,
|
// we allow the delta to be within 0.1unym,
|
||||||
|
|||||||
+12
-20
@@ -20,25 +20,21 @@ impl SimulatedNode {
|
|||||||
cost_params: MixNodeCostParams,
|
cost_params: MixNodeCostParams,
|
||||||
initial_pledge: &Coin,
|
initial_pledge: &Coin,
|
||||||
current_epoch: EpochId,
|
current_epoch: EpochId,
|
||||||
) -> Result<Self, MixnetContractError> {
|
) -> Self {
|
||||||
Ok(SimulatedNode {
|
SimulatedNode {
|
||||||
mix_id,
|
mix_id,
|
||||||
rewarding_details: MixNodeRewarding::initialise_new(
|
rewarding_details: MixNodeRewarding::initialise_new(
|
||||||
cost_params,
|
cost_params,
|
||||||
initial_pledge,
|
initial_pledge,
|
||||||
current_epoch,
|
current_epoch,
|
||||||
)?,
|
),
|
||||||
delegations: HashMap::new(),
|
delegations: HashMap::new(),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delegate<S: Into<String>>(
|
pub fn delegate<S: Into<String>>(&mut self, delegator: S, delegation: Coin) {
|
||||||
&mut self,
|
|
||||||
delegator: S,
|
|
||||||
delegation: Coin,
|
|
||||||
) -> Result<(), MixnetContractError> {
|
|
||||||
self.rewarding_details
|
self.rewarding_details
|
||||||
.add_base_delegation(delegation.amount)?;
|
.add_base_delegation(delegation.amount);
|
||||||
|
|
||||||
let delegator = delegator.into();
|
let delegator = delegator.into();
|
||||||
let delegation = Delegation::new(
|
let delegation = Delegation::new(
|
||||||
@@ -51,7 +47,6 @@ impl SimulatedNode {
|
|||||||
);
|
);
|
||||||
|
|
||||||
self.delegations.insert(delegator, delegation);
|
self.delegations.insert(delegator, delegation);
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undelegate<S: Into<String>>(
|
pub fn undelegate<S: Into<String>>(
|
||||||
@@ -59,19 +54,16 @@ impl SimulatedNode {
|
|||||||
delegator: S,
|
delegator: S,
|
||||||
) -> Result<(Coin, Coin), MixnetContractError> {
|
) -> Result<(Coin, Coin), MixnetContractError> {
|
||||||
let delegator = delegator.into();
|
let delegator = delegator.into();
|
||||||
let delegation = self.delegations.remove(&delegator).ok_or(
|
let delegation = self
|
||||||
MixnetContractError::NoMixnodeDelegationFound {
|
.delegations
|
||||||
mix_id: MixId::MAX,
|
.remove(&delegator)
|
||||||
address: delegator,
|
.expect("delegation not found");
|
||||||
proxy: None,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let reward = self
|
let reward = self
|
||||||
.rewarding_details
|
.rewarding_details
|
||||||
.determine_delegation_reward(&delegation)?;
|
.determine_delegation_reward(&delegation);
|
||||||
self.rewarding_details
|
self.rewarding_details
|
||||||
.remove_delegation_decimal(delegation.dec_amount()? + reward)?;
|
.remove_delegation_decimal(delegation.dec_amount() + reward)?;
|
||||||
|
|
||||||
let reward_denom = &delegation.amount.denom;
|
let reward_denom = &delegation.amount.denom;
|
||||||
let truncated_reward = truncate_reward(reward, reward_denom);
|
let truncated_reward = truncate_reward(reward, reward_denom);
|
||||||
|
|||||||
@@ -35,10 +35,6 @@ impl LayerDistribution {
|
|||||||
(Layer::Two, self.layer2),
|
(Layer::Two, self.layer2),
|
||||||
(Layer::Three, self.layer3),
|
(Layer::Three, self.layer3),
|
||||||
];
|
];
|
||||||
|
|
||||||
// we explicitly put 3 elements into the iterator, so the iterator is DEFINITELY
|
|
||||||
// not empty and thus the unwrap cannot fail
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
layers.iter().min_by_key(|x| x.1).unwrap().0
|
layers.iter().min_by_key(|x| x.1).unwrap().0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ mixnet-contract-common = { path = "../mixnet-contract" }
|
|||||||
contracts-common = { path = "../contracts-common" }
|
contracts-common = { path = "../contracts-common" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
schemars = "0.8"
|
schemars = "0.8"
|
||||||
log = "0.4"
|
|
||||||
ts-rs = {version = "6.1.2", optional = true}
|
ts-rs = {version = "6.1.2", optional = true}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
#![warn(clippy::expect_used)]
|
|
||||||
#![warn(clippy::unwrap_used)]
|
|
||||||
|
|
||||||
use contracts_common::Percent;
|
use contracts_common::Percent;
|
||||||
use cosmwasm_std::{Addr, Coin, Timestamp, Uint128};
|
use cosmwasm_std::{Addr, Coin, Timestamp, Uint128};
|
||||||
use log::warn;
|
|
||||||
use mixnet_contract_common::MixId;
|
use mixnet_contract_common::MixId;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg};
|
pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg};
|
||||||
|
|
||||||
@@ -62,23 +58,11 @@ impl FromStr for PledgeCap {
|
|||||||
fn from_str(cap: &str) -> Result<Self, Self::Err> {
|
fn from_str(cap: &str) -> Result<Self, Self::Err> {
|
||||||
let cap = cap.replace('_', "").replace(',', ".");
|
let cap = cap.replace('_', "").replace(',', ".");
|
||||||
match Percent::from_str(&cap) {
|
match Percent::from_str(&cap) {
|
||||||
Ok(p) => {
|
Ok(p) => Ok(PledgeCap::Percent(p)),
|
||||||
if p.is_zero() {
|
Err(_) => match cap.parse::<u128>() {
|
||||||
warn!("Pledge cap set to 0%, are you sure this is right?")
|
Ok(i) => Ok(PledgeCap::Absolute(Uint128::from(i))),
|
||||||
}
|
Err(_e) => Err(format!("Could not parse {} as Percent or Uint128", cap)),
|
||||||
Ok(PledgeCap::Percent(p))
|
},
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
match cap.parse::<u128>() {
|
|
||||||
Ok(i) => {
|
|
||||||
if i < 100_000_000_000 {
|
|
||||||
warn!("PledgeCap set to less then 100_000 NYM, are you sure this is right?");
|
|
||||||
}
|
|
||||||
Ok(PledgeCap::Absolute(Uint128::from(i)))
|
|
||||||
}
|
|
||||||
Err(_e) => Err(format!("Could not parse {} as Percent or Uint128", cap)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pub const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6);
|
|||||||
pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
|
pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
|
||||||
|
|
||||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
|
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
|
||||||
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
|
"n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr";
|
||||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
||||||
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
|
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
|
||||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ impl SecretKey {
|
|||||||
// x || ys.len() || ys
|
// x || ys.len() || ys
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
let ys_len = self.ys.len();
|
let ys_len = self.ys.len();
|
||||||
let mut bytes = Vec::with_capacity(8 + (ys_len + 1) * 32);
|
let mut bytes = Vec::with_capacity(8 + (ys_len + 1) as usize * 32);
|
||||||
|
|
||||||
bytes.extend_from_slice(&self.x.to_bytes());
|
bytes.extend_from_slice(&self.x.to_bytes());
|
||||||
bytes.extend_from_slice(&ys_len.to_le_bytes());
|
bytes.extend_from_slice(&ys_len.to_le_bytes());
|
||||||
@@ -162,7 +162,7 @@ impl TryFrom<&[u8]> for VerificationKey {
|
|||||||
let mut beta_g1_end: u64 = 0;
|
let mut beta_g1_end: u64 = 0;
|
||||||
for i in 0..betas_len {
|
for i in 0..betas_len {
|
||||||
let start = (104 + i * 48) as usize;
|
let start = (104 + i * 48) as usize;
|
||||||
let end = start + 48;
|
let end = (start + 48) as usize;
|
||||||
let beta_i_bytes = bytes[start..end].try_into().unwrap();
|
let beta_i_bytes = bytes[start..end].try_into().unwrap();
|
||||||
let beta_i = try_deserialize_g1_projective(
|
let beta_i = try_deserialize_g1_projective(
|
||||||
&beta_i_bytes,
|
&beta_i_bytes,
|
||||||
@@ -178,7 +178,7 @@ impl TryFrom<&[u8]> for VerificationKey {
|
|||||||
let mut beta_g2 = Vec::with_capacity(betas_len as usize);
|
let mut beta_g2 = Vec::with_capacity(betas_len as usize);
|
||||||
for i in 0..betas_len {
|
for i in 0..betas_len {
|
||||||
let start = (beta_g1_end + i * 96) as usize;
|
let start = (beta_g1_end + i * 96) as usize;
|
||||||
let end = start + 96;
|
let end = (start + 96) as usize;
|
||||||
let beta_i_bytes = bytes[start..end].try_into().unwrap();
|
let beta_i_bytes = bytes[start..end].try_into().unwrap();
|
||||||
let beta_i = try_deserialize_g2_projective(
|
let beta_i = try_deserialize_g2_projective(
|
||||||
&beta_i_bytes,
|
&beta_i_bytes,
|
||||||
|
|||||||
@@ -94,10 +94,10 @@ fn prepare_unlinked_fragmented_set(
|
|||||||
|
|
||||||
for i in 1..(pre_casted_frags + 1) {
|
for i in 1..(pre_casted_frags + 1) {
|
||||||
// we can't use u8 directly here as upper (NON-INCLUSIVE, so it would always fit) bound could be u8::max_value() + 1
|
// we can't use u8 directly here as upper (NON-INCLUSIVE, so it would always fit) bound could be u8::max_value() + 1
|
||||||
let lb = (i - 1) * unlinked_fragment_payload_max_len(max_plaintext_size);
|
let lb = (i as usize - 1) * unlinked_fragment_payload_max_len(max_plaintext_size);
|
||||||
let ub = usize::min(
|
let ub = usize::min(
|
||||||
message.len(),
|
message.len(),
|
||||||
i * unlinked_fragment_payload_max_len(max_plaintext_size),
|
i as usize * unlinked_fragment_payload_max_len(max_plaintext_size),
|
||||||
);
|
);
|
||||||
fragments.push(
|
fragments.push(
|
||||||
Fragment::try_new(
|
Fragment::try_new(
|
||||||
|
|||||||
+28
-5
@@ -1,8 +1,34 @@
|
|||||||
## Unreleased
|
## [nym-contracts-v1.1.0](https://github.com/nymtech/nym/tree/nym-contracts-v1.1.0) (2022-11-09)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- mixnet-contract: rework of rewarding ([#1472]), which includes, but is not limited to:
|
||||||
|
- internal reward accounting was modified to be similar to the ideas presented in Cosmos' F1 paper, which results in throughput gains and no storage or gas cost bloat over time,
|
||||||
|
- introduced internal queues for pending epoch and interval events that only get resolved once relevant epoch/interval rolls over
|
||||||
|
- the contract no longer stores any historical information regarding past epochs/parameters/stake state for the purposes of rewarding
|
||||||
|
- a lot of queries got renamed to keep naming more consistent,
|
||||||
|
- introduced new utility-based queries such as a query for reward estimation for the current epoch,
|
||||||
|
- mixnodes are now identified by a monotonously increasing `mix_id`
|
||||||
|
- bonding now results in getting fresh `mix_id` and thus if given node decides to unbond and rebond, it will lose all its delegations,
|
||||||
|
- mixnode operators are now allowed to set their operating costs as opposed to having fixed value of 40nym/interval
|
||||||
|
- rewarding parameters are now correctly updated at an **interval** end
|
||||||
|
- rewarding parameters now include a staking supply scale factor attribute (beta in the tokenomics paper)
|
||||||
|
- node performance can now be more granular with internal `Decimal` representation as opposed to an `u8`
|
||||||
|
- node profit margin can now be more granular with internal `Decimal` representation as opposed to an `u8`
|
||||||
|
- mixnode operators are now allowed to change their configuration options, such as port information, without having to unbond
|
||||||
|
- mixnode unbonding is no longer instantaneous, instead it happens once an epoch rolls over
|
||||||
|
- it is now possible to query for operator and node history to see how often (and with what parameters) they rebonded
|
||||||
|
- other minor bugfixes and changes
|
||||||
|
- ...
|
||||||
|
- new exciting bugs to find and squash
|
||||||
|
|
||||||
|
- vesting-contract: optional locked token pledge cap per account ([#1687]), defaults to 10%
|
||||||
|
- vesting-contract: updated internal delegation storage due to mixnet contract revamp ([#1472])
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- vesting-contract: added query for obtaining contract build information ([#1726])
|
- vesting-contract: added query for obtaining contract build information ([#1726])
|
||||||
|
|
||||||
|
[#1472]: https://github.com/nymtech/nym/pull/1472
|
||||||
|
[#1687]: https://github.com/nymtech/nym/pull/1687
|
||||||
[#1726]: https://github.com/nymtech/nym/pull/1726
|
[#1726]: https://github.com/nymtech/nym/pull/1726
|
||||||
|
|
||||||
|
|
||||||
@@ -11,12 +37,10 @@
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- vesting-contract: added queries for delegation timestamps and paged query for all vesting delegations in the contract ([#1569])
|
- vesting-contract: added queries for delegation timestamps and paged query for all vesting delegations in the contract ([#1569])
|
||||||
- all binaries: added shell completion and [Fig](fig.io) spec generation ([#1638])
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- mixnet-contract: compounding delegator rewards now happens instantaneously as opposed to having to wait for the current epoch to finish ([#1571])
|
- mixnet-contract: compounding delegator rewards now happens instantaneously as opposed to having to wait for the current epoch to finish ([#1571])
|
||||||
- network-requester: updated CLI to use `clap` macros ([#1638])
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@@ -26,9 +50,8 @@
|
|||||||
|
|
||||||
[#1544]: https://github.com/nymtech/nym/pull/1544
|
[#1544]: https://github.com/nymtech/nym/pull/1544
|
||||||
[#1569]: https://github.com/nymtech/nym/pull/1569
|
[#1569]: https://github.com/nymtech/nym/pull/1569
|
||||||
[#1569]: https://github.com/nymtech/nym/pull/1571
|
[#1571]: https://github.com/nymtech/nym/pull/1571
|
||||||
[#1613]: https://github.com/nymtech/nym/pull/1613
|
[#1613]: https://github.com/nymtech/nym/pull/1613
|
||||||
[#1638]: https://github.com/nymtech/nym/pull/1638
|
|
||||||
|
|
||||||
## [nym-contracts-v1.0.1](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.1) (2022-06-22)
|
## [nym-contracts-v1.0.1](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.1) (2022-06-22)
|
||||||
|
|
||||||
|
|||||||
Generated
+1
-2
@@ -930,7 +930,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mixnet-contract"
|
name = "mixnet-contract"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bs58",
|
"bs58",
|
||||||
"cosmwasm-schema",
|
"cosmwasm-schema",
|
||||||
@@ -1625,7 +1625,6 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"contracts-common",
|
"contracts-common",
|
||||||
"cosmwasm-std",
|
"cosmwasm-std",
|
||||||
"log",
|
|
||||||
"mixnet-contract-common",
|
"mixnet-contract-common",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
allow-unwrap-in-tests = true
|
|
||||||
allow-expect-in-tests = true
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mixnet-contract"
|
name = "mixnet-contract"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ pub fn instantiate(
|
|||||||
Interval::init_interval(msg.epochs_in_interval, msg.epoch_duration, &env);
|
Interval::init_interval(msg.epochs_in_interval, msg.epoch_duration, &env);
|
||||||
let reward_params = msg
|
let reward_params = msg
|
||||||
.initial_rewarding_params
|
.initial_rewarding_params
|
||||||
.into_rewarding_params(msg.epochs_in_interval)?;
|
.into_rewarding_params(msg.epochs_in_interval);
|
||||||
|
|
||||||
interval_storage::initialise_storage(deps.storage, starting_interval)?;
|
interval_storage::initialise_storage(deps.storage, starting_interval)?;
|
||||||
mixnet_params_storage::initialise_storage(deps.storage, state)?;
|
mixnet_params_storage::initialise_storage(deps.storage, state)?;
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ pub(crate) fn delegate(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// add the amount we're intending to delegate (whether it's fresh or we're adding to the existing one)
|
// add the amount we're intending to delegate (whether it's fresh or we're adding to the existing one)
|
||||||
mix_rewarding.add_base_delegation(stored_delegation_amount.amount)?;
|
mix_rewarding.add_base_delegation(stored_delegation_amount.amount);
|
||||||
|
|
||||||
let cosmos_event = new_delegation_event(
|
let cosmos_event = new_delegation_event(
|
||||||
created_at,
|
created_at,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
#![warn(clippy::expect_used)]
|
|
||||||
#![warn(clippy::unwrap_used)]
|
|
||||||
|
|
||||||
mod constants;
|
mod constants;
|
||||||
pub mod contract;
|
pub mod contract;
|
||||||
mod delegations;
|
mod delegations;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ pub(crate) fn save_new_mixnode(
|
|||||||
let mix_id = next_mixnode_id_counter(storage)?;
|
let mix_id = next_mixnode_id_counter(storage)?;
|
||||||
let current_epoch = interval_storage::current_interval(storage)?.current_epoch_absolute_id();
|
let current_epoch = interval_storage::current_interval(storage)?.current_epoch_absolute_id();
|
||||||
|
|
||||||
let mixnode_rewarding = MixNodeRewarding::initialise_new(cost_params, &pledge, current_epoch)?;
|
let mixnode_rewarding = MixNodeRewarding::initialise_new(cost_params, &pledge, current_epoch);
|
||||||
let mixnode_bond = MixNodeBond::new(
|
let mixnode_bond = MixNodeBond::new(
|
||||||
mix_id,
|
mix_id,
|
||||||
owner,
|
owner,
|
||||||
|
|||||||
@@ -4,9 +4,8 @@
|
|||||||
use super::storage;
|
use super::storage;
|
||||||
use crate::delegations::storage as delegations_storage;
|
use crate::delegations::storage as delegations_storage;
|
||||||
use crate::interval::storage as interval_storage;
|
use crate::interval::storage as interval_storage;
|
||||||
use cosmwasm_std::{Coin, Storage};
|
use cosmwasm_std::{Coin, Decimal, Storage};
|
||||||
use mixnet_contract_common::error::MixnetContractError;
|
use mixnet_contract_common::error::MixnetContractError;
|
||||||
use mixnet_contract_common::helpers::IntoBaseDecimal;
|
|
||||||
use mixnet_contract_common::mixnode::{MixNodeDetails, MixNodeRewarding};
|
use mixnet_contract_common::mixnode::{MixNodeDetails, MixNodeRewarding};
|
||||||
use mixnet_contract_common::Delegation;
|
use mixnet_contract_common::Delegation;
|
||||||
|
|
||||||
@@ -23,10 +22,11 @@ pub(crate) fn apply_reward_pool_changes(
|
|||||||
+ pending_pool_change.added;
|
+ pending_pool_change.added;
|
||||||
let staking_supply = rewarding_params.interval.staking_supply
|
let staking_supply = rewarding_params.interval.staking_supply
|
||||||
+ rewarding_params.interval.staking_supply_scale_factor * pending_pool_change.removed;
|
+ rewarding_params.interval.staking_supply_scale_factor * pending_pool_change.removed;
|
||||||
let epoch_reward_budget = reward_pool / interval.epochs_in_interval().into_base_decimal()?
|
let epoch_reward_budget = reward_pool
|
||||||
|
/ Decimal::from_atomics(interval.epochs_in_interval(), 0).unwrap()
|
||||||
* rewarding_params.interval.interval_pool_emission;
|
* rewarding_params.interval.interval_pool_emission;
|
||||||
let stake_saturation_point =
|
let stake_saturation_point =
|
||||||
staking_supply / rewarding_params.rewarded_set_size.into_base_decimal()?;
|
staking_supply / Decimal::from_atomics(rewarding_params.rewarded_set_size, 0).unwrap();
|
||||||
|
|
||||||
rewarding_params.interval.reward_pool = reward_pool;
|
rewarding_params.interval.reward_pool = reward_pool;
|
||||||
rewarding_params.interval.staking_supply = staking_supply;
|
rewarding_params.interval.staking_supply = staking_supply;
|
||||||
@@ -46,7 +46,7 @@ pub(crate) fn withdraw_operator_reward(
|
|||||||
let mix_id = mix_details.mix_id();
|
let mix_id = mix_details.mix_id();
|
||||||
let mut mix_rewarding = mix_details.rewarding_details;
|
let mut mix_rewarding = mix_details.rewarding_details;
|
||||||
let original_pledge = mix_details.bond_information.original_pledge;
|
let original_pledge = mix_details.bond_information.original_pledge;
|
||||||
let reward = mix_rewarding.withdraw_operator_reward(&original_pledge)?;
|
let reward = mix_rewarding.withdraw_operator_reward(&original_pledge);
|
||||||
|
|
||||||
// save updated rewarding info
|
// save updated rewarding info
|
||||||
storage::MIXNODE_REWARDING.save(store, mix_id, &mix_rewarding)?;
|
storage::MIXNODE_REWARDING.save(store, mix_id, &mix_rewarding)?;
|
||||||
@@ -87,7 +87,7 @@ mod tests {
|
|||||||
let mut test = TestSetup::new();
|
let mut test = TestSetup::new();
|
||||||
|
|
||||||
let epochs_in_interval = test.current_interval().epochs_in_interval();
|
let epochs_in_interval = test.current_interval().epochs_in_interval();
|
||||||
let epochs_in_interval_dec = epochs_in_interval.into_base_decimal().unwrap();
|
let epochs_in_interval_dec = Decimal::from_atomics(epochs_in_interval, 0).unwrap();
|
||||||
let start_rewarding_params = test.rewarding_params();
|
let start_rewarding_params = test.rewarding_params();
|
||||||
|
|
||||||
// nothing changes if pending changes are empty
|
// nothing changes if pending changes are empty
|
||||||
@@ -95,7 +95,7 @@ mod tests {
|
|||||||
assert_eq!(start_rewarding_params, test.rewarding_params());
|
assert_eq!(start_rewarding_params, test.rewarding_params());
|
||||||
|
|
||||||
// normal case of having distributed some rewards
|
// normal case of having distributed some rewards
|
||||||
let distributed_rewards = 100_000_000u32.into_base_decimal().unwrap();
|
let distributed_rewards = Decimal::from_atomics(100_000_000u32, 0).unwrap();
|
||||||
storage::PENDING_REWARD_POOL_CHANGE
|
storage::PENDING_REWARD_POOL_CHANGE
|
||||||
.save(
|
.save(
|
||||||
test.deps_mut().storage,
|
test.deps_mut().storage,
|
||||||
@@ -132,10 +132,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
updated_rewarding_params.interval.stake_saturation_point,
|
updated_rewarding_params.interval.stake_saturation_point,
|
||||||
updated_rewarding_params.interval.staking_supply
|
updated_rewarding_params.interval.staking_supply
|
||||||
/ updated_rewarding_params
|
/ Decimal::from_atomics(updated_rewarding_params.rewarded_set_size, 0).unwrap()
|
||||||
.rewarded_set_size
|
|
||||||
.into_base_decimal()
|
|
||||||
.unwrap()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// resets changes back to 0
|
// resets changes back to 0
|
||||||
@@ -147,7 +144,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// future case of having to also increase the reward pool
|
// future case of having to also increase the reward pool
|
||||||
let added_credentials = 50_000_000u32.into_base_decimal().unwrap();
|
let added_credentials = Decimal::from_atomics(50_000_000u32, 0).unwrap();
|
||||||
storage::PENDING_REWARD_POOL_CHANGE
|
storage::PENDING_REWARD_POOL_CHANGE
|
||||||
.save(
|
.save(
|
||||||
test.deps_mut().storage,
|
test.deps_mut().storage,
|
||||||
@@ -184,10 +181,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
updated_rewarding_params2.interval.stake_saturation_point,
|
updated_rewarding_params2.interval.stake_saturation_point,
|
||||||
updated_rewarding_params2.interval.staking_supply
|
updated_rewarding_params2.interval.staking_supply
|
||||||
/ updated_rewarding_params2
|
/ Decimal::from_atomics(updated_rewarding_params2.rewarded_set_size, 0).unwrap()
|
||||||
.rewarded_set_size
|
|
||||||
.into_base_decimal()
|
|
||||||
.unwrap()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// resets changes back to 0
|
// resets changes back to 0
|
||||||
@@ -204,7 +198,7 @@ mod tests {
|
|||||||
let mut test = TestSetup::new();
|
let mut test = TestSetup::new();
|
||||||
|
|
||||||
let pledge = Uint128::new(250_000_000);
|
let pledge = Uint128::new(250_000_000);
|
||||||
let pledge_dec = 250_000_000u32.into_base_decimal().unwrap();
|
let pledge_dec = Decimal::from_atomics(250_000_000u32, 0).unwrap();
|
||||||
let mix_id = test.add_dummy_mixnode("mix-owner", Some(pledge));
|
let mix_id = test.add_dummy_mixnode("mix-owner", Some(pledge));
|
||||||
|
|
||||||
// no rewards
|
// no rewards
|
||||||
@@ -241,7 +235,7 @@ mod tests {
|
|||||||
let mut test = TestSetup::new();
|
let mut test = TestSetup::new();
|
||||||
|
|
||||||
let delegation_amount = Uint128::new(2500_000_000);
|
let delegation_amount = Uint128::new(2500_000_000);
|
||||||
let delegation_dec = 2500_000_000u32.into_base_decimal().unwrap();
|
let delegation_dec = Decimal::from_atomics(2500_000_000u32, 0).unwrap();
|
||||||
let mix_id = test.add_dummy_mixnode("mix-owner", None);
|
let mix_id = test.add_dummy_mixnode("mix-owner", None);
|
||||||
let delegator = "delegator";
|
let delegator = "delegator";
|
||||||
test.add_immediate_delegation(delegator, delegation_amount, mix_id);
|
test.add_immediate_delegation(delegator, delegation_amount, mix_id);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use crate::interval::storage as interval_storage;
|
|||||||
use crate::mixnodes;
|
use crate::mixnodes;
|
||||||
use crate::mixnodes::storage as mixnodes_storage;
|
use crate::mixnodes::storage as mixnodes_storage;
|
||||||
use cosmwasm_std::{coin, Coin, Decimal, Deps, StdResult};
|
use cosmwasm_std::{coin, Coin, Decimal, Deps, StdResult};
|
||||||
use mixnet_contract_common::helpers::into_base_decimal;
|
|
||||||
use mixnet_contract_common::mixnode::MixNodeDetails;
|
use mixnet_contract_common::mixnode::MixNodeDetails;
|
||||||
use mixnet_contract_common::reward_params::{NodeRewardParams, Performance, RewardingParams};
|
use mixnet_contract_common::reward_params::{NodeRewardParams, Performance, RewardingParams};
|
||||||
use mixnet_contract_common::rewarding::helpers::truncate_reward;
|
use mixnet_contract_common::rewarding::helpers::truncate_reward;
|
||||||
@@ -20,18 +19,16 @@ pub(crate) fn query_rewarding_params(deps: Deps<'_>) -> StdResult<RewardingParam
|
|||||||
storage::REWARDING_PARAMS.load(deps.storage)
|
storage::REWARDING_PARAMS.load(deps.storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pending_operator_reward(
|
fn pending_operator_reward(mix_details: Option<MixNodeDetails>) -> PendingRewardResponse {
|
||||||
mix_details: Option<MixNodeDetails>,
|
match mix_details {
|
||||||
) -> StdResult<PendingRewardResponse> {
|
|
||||||
Ok(match mix_details {
|
|
||||||
Some(mix_details) => PendingRewardResponse {
|
Some(mix_details) => PendingRewardResponse {
|
||||||
amount_staked: Some(mix_details.original_pledge().clone()),
|
amount_staked: Some(mix_details.original_pledge().clone()),
|
||||||
amount_earned: Some(mix_details.pending_operator_reward()),
|
amount_earned: Some(mix_details.pending_operator_reward()),
|
||||||
amount_earned_detailed: Some(mix_details.pending_detailed_operator_reward()?),
|
amount_earned_detailed: Some(mix_details.pending_detailed_operator_reward()),
|
||||||
mixnode_still_fully_bonded: !mix_details.is_unbonding(),
|
mixnode_still_fully_bonded: !mix_details.is_unbonding(),
|
||||||
},
|
},
|
||||||
None => PendingRewardResponse::default(),
|
None => PendingRewardResponse::default(),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_pending_operator_reward(
|
pub fn query_pending_operator_reward(
|
||||||
@@ -42,7 +39,7 @@ pub fn query_pending_operator_reward(
|
|||||||
// in order to determine operator's reward we need to know its original pledge and thus
|
// in order to determine operator's reward we need to know its original pledge and thus
|
||||||
// we have to load the entire thing
|
// we have to load the entire thing
|
||||||
let mix_details = mixnodes::helpers::get_mixnode_details_by_owner(deps.storage, owner_address)?;
|
let mix_details = mixnodes::helpers::get_mixnode_details_by_owner(deps.storage, owner_address)?;
|
||||||
pending_operator_reward(mix_details)
|
Ok(pending_operator_reward(mix_details))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_pending_mixnode_operator_reward(
|
pub fn query_pending_mixnode_operator_reward(
|
||||||
@@ -52,7 +49,7 @@ pub fn query_pending_mixnode_operator_reward(
|
|||||||
// in order to determine operator's reward we need to know its original pledge and thus
|
// in order to determine operator's reward we need to know its original pledge and thus
|
||||||
// we have to load the entire thing
|
// we have to load the entire thing
|
||||||
let mix_details = mixnodes::helpers::get_mixnode_details_by_id(deps.storage, mix_id)?;
|
let mix_details = mixnodes::helpers::get_mixnode_details_by_id(deps.storage, mix_id)?;
|
||||||
pending_operator_reward(mix_details)
|
Ok(pending_operator_reward(mix_details))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_pending_delegator_reward(
|
pub fn query_pending_delegator_reward(
|
||||||
@@ -77,8 +74,8 @@ pub fn query_pending_delegator_reward(
|
|||||||
None => return Ok(PendingRewardResponse::default()),
|
None => return Ok(PendingRewardResponse::default()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let detailed_reward = mix_rewarding.determine_delegation_reward(&delegation)?;
|
let detailed_reward = mix_rewarding.determine_delegation_reward(&delegation);
|
||||||
let delegator_reward = mix_rewarding.pending_delegator_reward(&delegation)?;
|
let delegator_reward = mix_rewarding.pending_delegator_reward(&delegation);
|
||||||
|
|
||||||
// check if the mixnode isnt in the process of unbonding (or has already unbonded)
|
// check if the mixnode isnt in the process of unbonding (or has already unbonded)
|
||||||
let is_bonded = matches!(mixnodes_storage::mixnode_bonds().may_load(deps.storage, mix_id)?, Some(mix_bond) if !mix_bond.is_unbonding);
|
let is_bonded = matches!(mixnodes_storage::mixnode_bonds().may_load(deps.storage, mix_id)?, Some(mix_bond) if !mix_bond.is_unbonding);
|
||||||
@@ -180,8 +177,8 @@ pub(crate) fn query_estimated_current_epoch_delegator_reward(
|
|||||||
None => return Ok(EstimatedCurrentEpochRewardResponse::empty_response()),
|
None => return Ok(EstimatedCurrentEpochRewardResponse::empty_response()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let staked_dec = into_base_decimal(delegation.amount.amount)?;
|
let staked_dec = Decimal::from_atomics(delegation.amount.amount, 0).unwrap();
|
||||||
let current_value = staked_dec + mix_rewarding.determine_delegation_reward(&delegation)?;
|
let current_value = staked_dec + mix_rewarding.determine_delegation_reward(&delegation);
|
||||||
let amount_staked = delegation.amount;
|
let amount_staked = delegation.amount;
|
||||||
|
|
||||||
// check if the mixnode isnt in the process of unbonding (or has already unbonded)
|
// check if the mixnode isnt in the process of unbonding (or has already unbonded)
|
||||||
@@ -795,10 +792,7 @@ mod tests {
|
|||||||
let delegation = test.delegation(mix_id, owner, &None);
|
let delegation = test.delegation(mix_id, owner, &None);
|
||||||
|
|
||||||
let staked_dec = Decimal::from_atomics(delegation.amount.amount, 0).unwrap();
|
let staked_dec = Decimal::from_atomics(delegation.amount.amount, 0).unwrap();
|
||||||
let current_value = staked_dec
|
let current_value = staked_dec + mix_rewarding.determine_delegation_reward(&delegation);
|
||||||
+ mix_rewarding
|
|
||||||
.determine_delegation_reward(&delegation)
|
|
||||||
.unwrap();
|
|
||||||
let amount_staked = delegation.amount;
|
let amount_staked = delegation.amount;
|
||||||
|
|
||||||
EstimatedCurrentEpochRewardResponse {
|
EstimatedCurrentEpochRewardResponse {
|
||||||
|
|||||||
@@ -744,7 +744,7 @@ pub mod tests {
|
|||||||
performance,
|
performance,
|
||||||
in_active_set: true,
|
in_active_set: true,
|
||||||
};
|
};
|
||||||
let sim_res = sim.simulate_epoch_single_node(node_params).unwrap();
|
let sim_res = sim.simulate_epoch_single_node(node_params);
|
||||||
assert_eq!(sim_res, dist);
|
assert_eq!(sim_res, dist);
|
||||||
}
|
}
|
||||||
test.skip_to_next_epoch_end();
|
test.skip_to_next_epoch_end();
|
||||||
@@ -768,7 +768,7 @@ pub mod tests {
|
|||||||
performance,
|
performance,
|
||||||
in_active_set: true,
|
in_active_set: true,
|
||||||
};
|
};
|
||||||
let sim_res = sim.simulate_epoch_single_node(node_params).unwrap();
|
let sim_res = sim.simulate_epoch_single_node(node_params);
|
||||||
assert_eq!(sim_res, dist);
|
assert_eq!(sim_res, dist);
|
||||||
}
|
}
|
||||||
test.skip_to_next_epoch_end();
|
test.skip_to_next_epoch_end();
|
||||||
@@ -809,8 +809,8 @@ pub mod tests {
|
|||||||
in_active_set: true,
|
in_active_set: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let dist1 = sim1.simulate_epoch_single_node(node_params).unwrap();
|
let dist1 = sim1.simulate_epoch_single_node(node_params);
|
||||||
let dist2 = sim2.simulate_epoch_single_node(node_params).unwrap();
|
let dist2 = sim2.simulate_epoch_single_node(node_params);
|
||||||
|
|
||||||
let env = test.env();
|
let env = test.env();
|
||||||
|
|
||||||
@@ -858,17 +858,15 @@ pub mod tests {
|
|||||||
let unit_delegation_base = actual_prior1.unit_delegation;
|
let unit_delegation_base = actual_prior1.unit_delegation;
|
||||||
|
|
||||||
// recompute the state of fully compounded delegation from before this rewarding was distributed
|
// recompute the state of fully compounded delegation from before this rewarding was distributed
|
||||||
let pre_rewarding_del11 = del11.dec_amount().unwrap()
|
let pre_rewarding_del11 = del11.dec_amount()
|
||||||
+ (prior_unit_reward - del11.cumulative_reward_ratio)
|
+ (prior_unit_reward - del11.cumulative_reward_ratio) * del11.dec_amount()
|
||||||
* del11.dec_amount().unwrap()
|
|
||||||
/ (del11.cumulative_reward_ratio + unit_delegation_base);
|
/ (del11.cumulative_reward_ratio + unit_delegation_base);
|
||||||
|
|
||||||
let computed_del11_reward =
|
let computed_del11_reward =
|
||||||
pre_rewarding_del11 / prior_delegates1 * delegates_reward1;
|
pre_rewarding_del11 / prior_delegates1 * delegates_reward1;
|
||||||
|
|
||||||
let pre_rewarding_del12 = del12.dec_amount().unwrap()
|
let pre_rewarding_del12 = del12.dec_amount()
|
||||||
+ (prior_unit_reward - del12.cumulative_reward_ratio)
|
+ (prior_unit_reward - del12.cumulative_reward_ratio) * del12.dec_amount()
|
||||||
* del12.dec_amount().unwrap()
|
|
||||||
/ (del12.cumulative_reward_ratio + unit_delegation_base);
|
/ (del12.cumulative_reward_ratio + unit_delegation_base);
|
||||||
|
|
||||||
let computed_del12_reward =
|
let computed_del12_reward =
|
||||||
@@ -922,9 +920,8 @@ pub mod tests {
|
|||||||
let unit_delegation_base = actual_prior2.unit_delegation;
|
let unit_delegation_base = actual_prior2.unit_delegation;
|
||||||
|
|
||||||
// recompute the state of fully compounded delegation from before this rewarding was distributed
|
// recompute the state of fully compounded delegation from before this rewarding was distributed
|
||||||
let pre_rewarding_del21 = del21.dec_amount().unwrap()
|
let pre_rewarding_del21 = del21.dec_amount()
|
||||||
+ (prior_unit_reward - del21.cumulative_reward_ratio)
|
+ (prior_unit_reward - del21.cumulative_reward_ratio) * del21.dec_amount()
|
||||||
* del21.dec_amount().unwrap()
|
|
||||||
/ (del21.cumulative_reward_ratio + unit_delegation_base);
|
/ (del21.cumulative_reward_ratio + unit_delegation_base);
|
||||||
|
|
||||||
let computed_del21_reward =
|
let computed_del21_reward =
|
||||||
@@ -952,8 +949,8 @@ pub mod tests {
|
|||||||
in_active_set: true,
|
in_active_set: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let dist1 = sim1.simulate_epoch_single_node(node_params).unwrap();
|
let dist1 = sim1.simulate_epoch_single_node(node_params);
|
||||||
let dist2 = sim2.simulate_epoch_single_node(node_params).unwrap();
|
let dist2 = sim2.simulate_epoch_single_node(node_params);
|
||||||
|
|
||||||
let env = test.env();
|
let env = test.env();
|
||||||
|
|
||||||
@@ -1001,25 +998,22 @@ pub mod tests {
|
|||||||
let unit_delegation_base = actual_prior1.unit_delegation;
|
let unit_delegation_base = actual_prior1.unit_delegation;
|
||||||
|
|
||||||
// recompute the state of fully compounded delegation from before this rewarding was distributed
|
// recompute the state of fully compounded delegation from before this rewarding was distributed
|
||||||
let pre_rewarding_del11 = del11.dec_amount().unwrap()
|
let pre_rewarding_del11 = del11.dec_amount()
|
||||||
+ (prior_unit_reward - del11.cumulative_reward_ratio)
|
+ (prior_unit_reward - del11.cumulative_reward_ratio) * del11.dec_amount()
|
||||||
* del11.dec_amount().unwrap()
|
|
||||||
/ (del11.cumulative_reward_ratio + unit_delegation_base);
|
/ (del11.cumulative_reward_ratio + unit_delegation_base);
|
||||||
|
|
||||||
let computed_del11_reward =
|
let computed_del11_reward =
|
||||||
pre_rewarding_del11 / prior_delegates1 * delegates_reward1;
|
pre_rewarding_del11 / prior_delegates1 * delegates_reward1;
|
||||||
|
|
||||||
let pre_rewarding_del12 = del12.dec_amount().unwrap()
|
let pre_rewarding_del12 = del12.dec_amount()
|
||||||
+ (prior_unit_reward - del12.cumulative_reward_ratio)
|
+ (prior_unit_reward - del12.cumulative_reward_ratio) * del12.dec_amount()
|
||||||
* del12.dec_amount().unwrap()
|
|
||||||
/ (del12.cumulative_reward_ratio + unit_delegation_base);
|
/ (del12.cumulative_reward_ratio + unit_delegation_base);
|
||||||
|
|
||||||
let computed_del12_reward =
|
let computed_del12_reward =
|
||||||
pre_rewarding_del12 / prior_delegates1 * delegates_reward1;
|
pre_rewarding_del12 / prior_delegates1 * delegates_reward1;
|
||||||
|
|
||||||
let pre_rewarding_del13 = del13.dec_amount().unwrap()
|
let pre_rewarding_del13 = del13.dec_amount()
|
||||||
+ (prior_unit_reward - del13.cumulative_reward_ratio)
|
+ (prior_unit_reward - del13.cumulative_reward_ratio) * del13.dec_amount()
|
||||||
* del13.dec_amount().unwrap()
|
|
||||||
/ (del13.cumulative_reward_ratio + unit_delegation_base);
|
/ (del13.cumulative_reward_ratio + unit_delegation_base);
|
||||||
|
|
||||||
let computed_del13_reward =
|
let computed_del13_reward =
|
||||||
@@ -1073,17 +1067,15 @@ pub mod tests {
|
|||||||
let unit_delegation_base = actual_prior2.unit_delegation;
|
let unit_delegation_base = actual_prior2.unit_delegation;
|
||||||
|
|
||||||
// recompute the state of fully compounded delegation from before this rewarding was distributed
|
// recompute the state of fully compounded delegation from before this rewarding was distributed
|
||||||
let pre_rewarding_del21 = del21.dec_amount().unwrap()
|
let pre_rewarding_del21 = del21.dec_amount()
|
||||||
+ (prior_unit_reward - del21.cumulative_reward_ratio)
|
+ (prior_unit_reward - del21.cumulative_reward_ratio) * del21.dec_amount()
|
||||||
* del21.dec_amount().unwrap()
|
|
||||||
/ (del21.cumulative_reward_ratio + unit_delegation_base);
|
/ (del21.cumulative_reward_ratio + unit_delegation_base);
|
||||||
|
|
||||||
let computed_del21_reward =
|
let computed_del21_reward =
|
||||||
pre_rewarding_del21 / prior_delegates2 * delegates_reward2;
|
pre_rewarding_del21 / prior_delegates2 * delegates_reward2;
|
||||||
|
|
||||||
let pre_rewarding_del23 = del23.dec_amount().unwrap()
|
let pre_rewarding_del23 = del23.dec_amount()
|
||||||
+ (prior_unit_reward - del23.cumulative_reward_ratio)
|
+ (prior_unit_reward - del23.cumulative_reward_ratio) * del23.dec_amount()
|
||||||
* del23.dec_amount().unwrap()
|
|
||||||
/ (del23.cumulative_reward_ratio + unit_delegation_base);
|
/ (del23.cumulative_reward_ratio + unit_delegation_base);
|
||||||
|
|
||||||
let computed_del23_reward =
|
let computed_del23_reward =
|
||||||
|
|||||||
@@ -69,10 +69,6 @@ pub(crate) fn validate_pledge(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// throughout this function we've been using the value at `pledge[0]` without problems
|
|
||||||
// (plus we have even validated that the vec is not empty), so the unwrap here is absolutely fine,
|
|
||||||
// since it cannot possibly fail without UB
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
Ok(pledge.pop().unwrap())
|
Ok(pledge.pop().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,10 +106,6 @@ pub(crate) fn validate_delegation_stake(
|
|||||||
return Err(MixnetContractError::EmptyDelegation);
|
return Err(MixnetContractError::EmptyDelegation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// throughout this function we've been using the value at `delegation[0]` without problems
|
|
||||||
// (plus we have even validated that the vec is not empty), so the unwrap here is absolutely fine,
|
|
||||||
// since it cannot possibly fail without UB
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
Ok(delegation.pop().unwrap())
|
Ok(delegation.pop().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::errors::ContractError;
|
use crate::errors::ContractError;
|
||||||
use crate::queued_migrations::migrate_to_v2_mixnet_contract;
|
use crate::queued_migrations::migrate_to_v2_mixnet_contract;
|
||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
account_from_address, BlockTimestampSecs, ADMIN, DELEGATIONS, MIXNET_CONTRACT_ADDRESS,
|
account_from_address, save_account, BlockTimestampSecs, ADMIN, DELEGATIONS,
|
||||||
MIX_DENOM,
|
MIXNET_CONTRACT_ADDRESS, MIX_DENOM,
|
||||||
};
|
};
|
||||||
use crate::traits::{
|
use crate::traits::{
|
||||||
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
|
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
|
||||||
@@ -158,7 +158,7 @@ pub fn try_update_locked_pledge_cap(
|
|||||||
let mut account = account_from_address(&address, deps.storage, deps.api)?;
|
let mut account = account_from_address(&address, deps.storage, deps.api)?;
|
||||||
|
|
||||||
account.pledge_cap = Some(cap);
|
account.pledge_cap = Some(cap);
|
||||||
// update_locked_pledge_cap(amount, deps.storage)?;
|
save_account(&account, deps.storage)?;
|
||||||
Ok(Response::default())
|
Ok(Response::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,7 +589,7 @@ pub fn try_get_current_vesting_period(
|
|||||||
env: Env,
|
env: Env,
|
||||||
) -> Result<Period, ContractError> {
|
) -> Result<Period, ContractError> {
|
||||||
let account = account_from_address(address, deps.storage, deps.api)?;
|
let account = account_from_address(address, deps.storage, deps.api)?;
|
||||||
account.get_current_vesting_period(env.block.time)
|
Ok(account.get_current_vesting_period(env.block.time))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads mixnode bond from vesting contract storage.
|
/// Loads mixnode bond from vesting contract storage.
|
||||||
@@ -691,7 +691,7 @@ pub fn try_get_original_vesting(
|
|||||||
deps: Deps<'_>,
|
deps: Deps<'_>,
|
||||||
) -> Result<OriginalVestingResponse, ContractError> {
|
) -> Result<OriginalVestingResponse, ContractError> {
|
||||||
let account = account_from_address(vesting_account_address, deps.storage, deps.api)?;
|
let account = account_from_address(vesting_account_address, deps.storage, deps.api)?;
|
||||||
account.get_original_vesting()
|
Ok(account.get_original_vesting())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [crate::traits::VestingAccount::get_delegated_free]
|
/// See [crate::traits::VestingAccount::get_delegated_free]
|
||||||
|
|||||||
@@ -50,6 +50,4 @@ pub enum ContractError {
|
|||||||
MinVestingFunds { sent: u128, need: u128 },
|
MinVestingFunds { sent: u128, need: u128 },
|
||||||
#[error("VESTING ({}): Maximum amount of locked coins has already been pledged: {current}, cap is {cap}", line!())]
|
#[error("VESTING ({}): Maximum amount of locked coins has already been pledged: {current}, cap is {cap}", line!())]
|
||||||
LockedPledgeCapReached { current: Uint128, cap: Uint128 },
|
LockedPledgeCapReached { current: Uint128, cap: Uint128 },
|
||||||
#[error("VESTING: ({}: Account owned by {owner} has unpopulated vesting periods!", line!())]
|
|
||||||
UnpopulatedVestingPeriods { owner: Addr },
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
#![allow(rustdoc::private_intra_doc_links)]
|
#![allow(rustdoc::private_intra_doc_links)]
|
||||||
//! Nym vesting contract, providing vesting accounts with ability to stake unvested tokens
|
//! Nym vesting contract, providing vesting accounts with ability to stake unvested tokens
|
||||||
|
|
||||||
#![warn(clippy::expect_used)]
|
|
||||||
#![warn(clippy::unwrap_used)]
|
|
||||||
|
|
||||||
pub mod contract;
|
pub mod contract;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod queued_migrations;
|
mod queued_migrations;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ pub trait VestingAccount {
|
|||||||
|
|
||||||
/// Returns amount of coins set at account creation
|
/// Returns amount of coins set at account creation
|
||||||
/// See [/vesting-contract/struct.Account.html/method.get_original_vesting] for impl
|
/// See [/vesting-contract/struct.Account.html/method.get_original_vesting] for impl
|
||||||
fn get_original_vesting(&self) -> Result<OriginalVestingResponse, ContractError>;
|
fn get_original_vesting(&self) -> OriginalVestingResponse;
|
||||||
|
|
||||||
/// See [/vesting-contract/struct.Account.html/method.get_delegated_free] for impl
|
/// See [/vesting-contract/struct.Account.html/method.get_delegated_free] for impl
|
||||||
fn get_delegated_free(
|
fn get_delegated_free(
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ impl Account {
|
|||||||
pub fn absolute_pledge_cap(&self) -> Result<Uint128, ContractError> {
|
pub fn absolute_pledge_cap(&self) -> Result<Uint128, ContractError> {
|
||||||
match self.pledge_cap() {
|
match self.pledge_cap() {
|
||||||
PledgeCap::Absolute(cap) => Ok(cap),
|
PledgeCap::Absolute(cap) => Ok(cap),
|
||||||
PledgeCap::Percent(p) => Ok(p * self.get_original_vesting()?.amount.amount),
|
PledgeCap::Percent(p) => Ok(p * self.get_original_vesting().amount.amount),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,13 +81,8 @@ impl Account {
|
|||||||
self.periods.len()
|
self.periods.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn period_duration(&self) -> Result<u64, ContractError> {
|
pub fn period_duration(&self) -> u64 {
|
||||||
self.periods
|
self.periods.get(0).unwrap().period_seconds
|
||||||
.get(0)
|
|
||||||
.ok_or(ContractError::UnpopulatedVestingPeriods {
|
|
||||||
owner: self.owner_address.clone(),
|
|
||||||
})
|
|
||||||
.map(|p| p.period_seconds)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn storage_key(&self) -> u32 {
|
pub fn storage_key(&self) -> u32 {
|
||||||
@@ -124,28 +119,11 @@ impl Account {
|
|||||||
|
|
||||||
/// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
|
/// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
|
||||||
/// In case vesting is over it will always return NUM_VESTING_PERIODS.
|
/// In case vesting is over it will always return NUM_VESTING_PERIODS.
|
||||||
pub fn get_current_vesting_period(
|
pub fn get_current_vesting_period(&self, block_time: Timestamp) -> Period {
|
||||||
&self,
|
if block_time.seconds() < self.periods.first().unwrap().start_time {
|
||||||
block_time: Timestamp,
|
Period::Before
|
||||||
) -> Result<Period, ContractError> {
|
} else if self.periods.last().unwrap().end_time() < block_time {
|
||||||
let first_period =
|
Period::After
|
||||||
self.periods
|
|
||||||
.first()
|
|
||||||
.ok_or(ContractError::UnpopulatedVestingPeriods {
|
|
||||||
owner: self.owner_address.clone(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let last_period = self
|
|
||||||
.periods
|
|
||||||
.last()
|
|
||||||
.ok_or(ContractError::UnpopulatedVestingPeriods {
|
|
||||||
owner: self.owner_address.clone(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if block_time.seconds() < first_period.start_time {
|
|
||||||
Ok(Period::Before)
|
|
||||||
} else if last_period.end_time() < block_time {
|
|
||||||
Ok(Period::After)
|
|
||||||
} else {
|
} else {
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
for period in &self.periods {
|
for period in &self.periods {
|
||||||
@@ -154,7 +132,7 @@ impl Account {
|
|||||||
}
|
}
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
Ok(Period::In(index))
|
Period::In(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ impl VestingAccount for Account {
|
|||||||
storage: &dyn Storage,
|
storage: &dyn Storage,
|
||||||
) -> Result<Coin, ContractError> {
|
) -> Result<Coin, ContractError> {
|
||||||
let block_time = block_time.unwrap_or(env.block.time);
|
let block_time = block_time.unwrap_or(env.block.time);
|
||||||
let period = self.get_current_vesting_period(block_time)?;
|
let period = self.get_current_vesting_period(block_time);
|
||||||
let denom = MIX_DENOM.load(storage)?;
|
let denom = MIX_DENOM.load(storage)?;
|
||||||
|
|
||||||
let amount = match period {
|
let amount = match period {
|
||||||
@@ -94,7 +94,7 @@ impl VestingAccount for Account {
|
|||||||
storage: &dyn Storage,
|
storage: &dyn Storage,
|
||||||
) -> Result<Coin, ContractError> {
|
) -> Result<Coin, ContractError> {
|
||||||
Ok(Coin {
|
Ok(Coin {
|
||||||
amount: self.get_original_vesting()?.amount().amount
|
amount: self.get_original_vesting().amount().amount
|
||||||
- self.get_vested_coins(block_time, env, storage)?.amount,
|
- self.get_vested_coins(block_time, env, storage)?.amount,
|
||||||
denom: MIX_DENOM.load(storage)?,
|
denom: MIX_DENOM.load(storage)?,
|
||||||
})
|
})
|
||||||
@@ -108,12 +108,12 @@ impl VestingAccount for Account {
|
|||||||
self.periods[(self.num_vesting_periods() - 1)].end_time()
|
self.periods[(self.num_vesting_periods() - 1)].end_time()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_original_vesting(&self) -> Result<OriginalVestingResponse, ContractError> {
|
fn get_original_vesting(&self) -> OriginalVestingResponse {
|
||||||
Ok(OriginalVestingResponse::new(
|
OriginalVestingResponse::new(
|
||||||
self.coin.clone(),
|
self.coin.clone(),
|
||||||
self.num_vesting_periods(),
|
self.num_vesting_periods(),
|
||||||
self.period_duration()?,
|
self.period_duration(),
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_delegated_free(
|
fn get_delegated_free(
|
||||||
@@ -123,12 +123,13 @@ impl VestingAccount for Account {
|
|||||||
storage: &dyn Storage,
|
storage: &dyn Storage,
|
||||||
) -> Result<Coin, ContractError> {
|
) -> Result<Coin, ContractError> {
|
||||||
let block_time = block_time.unwrap_or(env.block.time);
|
let block_time = block_time.unwrap_or(env.block.time);
|
||||||
let period = self.get_current_vesting_period(block_time)?;
|
|
||||||
let withdrawn = self.load_withdrawn(storage)?;
|
let withdrawn = self.load_withdrawn(storage)?;
|
||||||
let max_available = self
|
let max_available = self
|
||||||
.get_vested_coins(Some(block_time), env, storage)?
|
.get_vested_coins(Some(block_time), env, storage)?
|
||||||
.amount
|
.amount
|
||||||
.saturating_sub(withdrawn);
|
.saturating_sub(withdrawn);
|
||||||
|
|
||||||
|
let period = self.get_current_vesting_period(block_time);
|
||||||
let start_time = match period {
|
let start_time = match period {
|
||||||
Period::Before => 0,
|
Period::Before => 0,
|
||||||
Period::After => u64::MAX,
|
Period::After => u64::MAX,
|
||||||
@@ -173,7 +174,7 @@ impl VestingAccount for Account {
|
|||||||
storage: &dyn Storage,
|
storage: &dyn Storage,
|
||||||
) -> Result<Coin, ContractError> {
|
) -> Result<Coin, ContractError> {
|
||||||
let block_time = block_time.unwrap_or(env.block.time);
|
let block_time = block_time.unwrap_or(env.block.time);
|
||||||
let period = self.get_current_vesting_period(block_time)?;
|
let period = self.get_current_vesting_period(block_time);
|
||||||
let max_vested = self.get_vested_coins(Some(block_time), env, storage)?;
|
let max_vested = self.get_vested_coins(Some(block_time), env, storage)?;
|
||||||
let start_time = match period {
|
let start_time = match period {
|
||||||
Period::Before => 0,
|
Period::Before => 0,
|
||||||
|
|||||||
@@ -183,14 +183,12 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(account.periods().len(), num_vesting_periods as usize);
|
assert_eq!(account.periods().len(), num_vesting_periods as usize);
|
||||||
|
|
||||||
let current_period = account
|
let current_period = account.get_current_vesting_period(Timestamp::from_seconds(0));
|
||||||
.get_current_vesting_period(Timestamp::from_seconds(0))
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(Period::Before, current_period);
|
assert_eq!(Period::Before, current_period);
|
||||||
|
|
||||||
let block_time =
|
let block_time =
|
||||||
Timestamp::from_seconds(account.start_time().seconds() + vesting_period + 1);
|
Timestamp::from_seconds(account.start_time().seconds() + vesting_period + 1);
|
||||||
let current_period = account.get_current_vesting_period(block_time).unwrap();
|
let current_period = account.get_current_vesting_period(block_time);
|
||||||
assert_eq!(current_period, Period::In(1));
|
assert_eq!(current_period, Period::In(1));
|
||||||
let vested_coins = account
|
let vested_coins = account
|
||||||
.get_vested_coins(Some(block_time), &env, &deps.storage)
|
.get_vested_coins(Some(block_time), &env, &deps.storage)
|
||||||
@@ -201,37 +199,21 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
vested_coins.amount,
|
vested_coins.amount,
|
||||||
Uint128::new(
|
Uint128::new(
|
||||||
account
|
account.get_original_vesting().amount().amount.u128() / num_vesting_periods as u128
|
||||||
.get_original_vesting()
|
|
||||||
.unwrap()
|
|
||||||
.amount()
|
|
||||||
.amount
|
|
||||||
.u128()
|
|
||||||
/ num_vesting_periods as u128
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vesting_coins.amount,
|
vesting_coins.amount,
|
||||||
Uint128::new(
|
Uint128::new(
|
||||||
account
|
account.get_original_vesting().amount().amount.u128()
|
||||||
.get_original_vesting()
|
- account.get_original_vesting().amount().amount.u128()
|
||||||
.unwrap()
|
|
||||||
.amount()
|
|
||||||
.amount
|
|
||||||
.u128()
|
|
||||||
- account
|
|
||||||
.get_original_vesting()
|
|
||||||
.unwrap()
|
|
||||||
.amount()
|
|
||||||
.amount
|
|
||||||
.u128()
|
|
||||||
/ num_vesting_periods as u128
|
/ num_vesting_periods as u128
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let block_time =
|
let block_time =
|
||||||
Timestamp::from_seconds(account.start_time().seconds() + 5 * vesting_period + 1);
|
Timestamp::from_seconds(account.start_time().seconds() + 5 * vesting_period + 1);
|
||||||
let current_period = account.get_current_vesting_period(block_time).unwrap();
|
let current_period = account.get_current_vesting_period(block_time);
|
||||||
assert_eq!(current_period, Period::In(5));
|
assert_eq!(current_period, Period::In(5));
|
||||||
let vested_coins = account
|
let vested_coins = account
|
||||||
.get_vested_coins(Some(block_time), &env, &deps.storage)
|
.get_vested_coins(Some(block_time), &env, &deps.storage)
|
||||||
@@ -242,30 +224,15 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
vested_coins.amount,
|
vested_coins.amount,
|
||||||
Uint128::new(
|
Uint128::new(
|
||||||
5 * account
|
5 * account.get_original_vesting().amount().amount.u128()
|
||||||
.get_original_vesting()
|
|
||||||
.unwrap()
|
|
||||||
.amount()
|
|
||||||
.amount
|
|
||||||
.u128()
|
|
||||||
/ num_vesting_periods as u128
|
/ num_vesting_periods as u128
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vesting_coins.amount,
|
vesting_coins.amount,
|
||||||
Uint128::new(
|
Uint128::new(
|
||||||
account
|
account.get_original_vesting().amount().amount.u128()
|
||||||
.get_original_vesting()
|
- 5 * account.get_original_vesting().amount().amount.u128()
|
||||||
.unwrap()
|
|
||||||
.amount()
|
|
||||||
.amount
|
|
||||||
.u128()
|
|
||||||
- 5 * account
|
|
||||||
.get_original_vesting()
|
|
||||||
.unwrap()
|
|
||||||
.amount()
|
|
||||||
.amount
|
|
||||||
.u128()
|
|
||||||
/ num_vesting_periods as u128
|
/ num_vesting_periods as u128
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -273,7 +240,7 @@ mod tests {
|
|||||||
let block_time = Timestamp::from_seconds(
|
let block_time = Timestamp::from_seconds(
|
||||||
account.start_time().seconds() + vesting_over_period * vesting_period + 1,
|
account.start_time().seconds() + vesting_over_period * vesting_period + 1,
|
||||||
);
|
);
|
||||||
let current_period = account.get_current_vesting_period(block_time).unwrap();
|
let current_period = account.get_current_vesting_period(block_time);
|
||||||
assert_eq!(current_period, Period::After);
|
assert_eq!(current_period, Period::After);
|
||||||
let vested_coins = account
|
let vested_coins = account
|
||||||
.get_vested_coins(Some(block_time), &env, &deps.storage)
|
.get_vested_coins(Some(block_time), &env, &deps.storage)
|
||||||
@@ -283,14 +250,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vested_coins.amount,
|
vested_coins.amount,
|
||||||
Uint128::new(
|
Uint128::new(account.get_original_vesting().amount().amount.u128())
|
||||||
account
|
|
||||||
.get_original_vesting()
|
|
||||||
.unwrap()
|
|
||||||
.amount()
|
|
||||||
.amount
|
|
||||||
.u128()
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
assert_eq!(vesting_coins.amount, Uint128::zero());
|
assert_eq!(vesting_coins.amount, Uint128::zero());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,269 +0,0 @@
|
|||||||
# This template contains all of the possible sections and their default values
|
|
||||||
|
|
||||||
# Note that all fields that take a lint level have these possible values:
|
|
||||||
# * deny - An error will be produced and the check will fail
|
|
||||||
# * warn - A warning will be produced, but the check will not fail
|
|
||||||
# * allow - No warning or error will be produced, though in some cases a note
|
|
||||||
# will be
|
|
||||||
|
|
||||||
# The values provided in this template are the default values that will be used
|
|
||||||
# when any section or field is not specified in your own configuration
|
|
||||||
|
|
||||||
# Root options
|
|
||||||
|
|
||||||
# If 1 or more target triples (and optionally, target_features) are specified,
|
|
||||||
# only the specified targets will be checked when running `cargo deny check`.
|
|
||||||
# This means, if a particular package is only ever used as a target specific
|
|
||||||
# dependency, such as, for example, the `nix` crate only being used via the
|
|
||||||
# `target_family = "unix"` configuration, that only having windows targets in
|
|
||||||
# this list would mean the nix crate, as well as any of its exclusive
|
|
||||||
# dependencies not shared by any other crates, would be ignored, as the target
|
|
||||||
# list here is effectively saying which targets you are building for.
|
|
||||||
targets = [
|
|
||||||
# The triple can be any string, but only the target triples built in to
|
|
||||||
# rustc (as of 1.40) can be checked against actual config expressions
|
|
||||||
#{ triple = "x86_64-unknown-linux-musl" },
|
|
||||||
# You can also specify which target_features you promise are enabled for a
|
|
||||||
# particular target. target_features are currently not validated against
|
|
||||||
# the actual valid features supported by the target architecture.
|
|
||||||
#{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
|
|
||||||
]
|
|
||||||
# When creating the dependency graph used as the source of truth when checks are
|
|
||||||
# executed, this field can be used to prune crates from the graph, removing them
|
|
||||||
# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate
|
|
||||||
# is pruned from the graph, all of its dependencies will also be pruned unless
|
|
||||||
# they are connected to another crate in the graph that hasn't been pruned,
|
|
||||||
# so it should be used with care. The identifiers are [Package ID Specifications]
|
|
||||||
# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html)
|
|
||||||
#exclude = []
|
|
||||||
# If true, metadata will be collected with `--all-features`. Note that this can't
|
|
||||||
# be toggled off if true, if you want to conditionally enable `--all-features` it
|
|
||||||
# is recommended to pass `--all-features` on the cmd line instead
|
|
||||||
all-features = false
|
|
||||||
# If true, metadata will be collected with `--no-default-features`. The same
|
|
||||||
# caveat with `all-features` applies
|
|
||||||
no-default-features = false
|
|
||||||
# If set, these feature will be enabled when collecting metadata. If `--features`
|
|
||||||
# is specified on the cmd line they will take precedence over this option.
|
|
||||||
#features = []
|
|
||||||
# When outputting inclusion graphs in diagnostics that include features, this
|
|
||||||
# option can be used to specify the depth at which feature edges will be added.
|
|
||||||
# This option is included since the graphs can be quite large and the addition
|
|
||||||
# of features from the crate(s) to all of the graph roots can be far too verbose.
|
|
||||||
# This option can be overridden via `--feature-depth` on the cmd line
|
|
||||||
feature-depth = 1
|
|
||||||
|
|
||||||
# This section is considered when running `cargo deny check advisories`
|
|
||||||
# More documentation for the advisories section can be found here:
|
|
||||||
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
|
|
||||||
[advisories]
|
|
||||||
# The path where the advisory database is cloned/fetched into
|
|
||||||
db-path = "~/.cargo/advisory-db"
|
|
||||||
# The url(s) of the advisory databases to use
|
|
||||||
db-urls = ["https://github.com/rustsec/advisory-db"]
|
|
||||||
# The lint level for security vulnerabilities
|
|
||||||
vulnerability = "deny"
|
|
||||||
# The lint level for unmaintained crates
|
|
||||||
unmaintained = "allow"
|
|
||||||
# The lint level for crates that have been yanked from their source registry
|
|
||||||
yanked = "warn"
|
|
||||||
# The lint level for crates with security notices. Note that as of
|
|
||||||
# 2019-12-17 there are no security notice advisories in
|
|
||||||
# https://github.com/rustsec/advisory-db
|
|
||||||
notice = "warn"
|
|
||||||
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
|
||||||
# output a note when they are encountered.
|
|
||||||
ignore = [
|
|
||||||
"RUSTSEC-2020-0159",
|
|
||||||
"RUSTSEC-2020-0071",
|
|
||||||
]
|
|
||||||
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
|
|
||||||
# lower than the range specified will be ignored. Note that ignored advisories
|
|
||||||
# will still output a note when they are encountered.
|
|
||||||
# * None - CVSS Score 0.0
|
|
||||||
# * Low - CVSS Score 0.1 - 3.9
|
|
||||||
# * Medium - CVSS Score 4.0 - 6.9
|
|
||||||
# * High - CVSS Score 7.0 - 8.9
|
|
||||||
# * Critical - CVSS Score 9.0 - 10.0
|
|
||||||
#severity-threshold =
|
|
||||||
|
|
||||||
# If this is true, then cargo deny will use the git executable to fetch advisory database.
|
|
||||||
# If this is false, then it uses a built-in git library.
|
|
||||||
# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
|
|
||||||
# See Git Authentication for more information about setting up git authentication.
|
|
||||||
#git-fetch-with-cli = true
|
|
||||||
|
|
||||||
# This section is considered when running `cargo deny check licenses`
|
|
||||||
# More documentation for the licenses section can be found here:
|
|
||||||
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
|
|
||||||
[licenses]
|
|
||||||
# The lint level for crates which do not have a detectable license
|
|
||||||
unlicensed = "deny"
|
|
||||||
# List of explicitly allowed licenses
|
|
||||||
# See https://spdx.org/licenses/ for list of possible licenses
|
|
||||||
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
|
|
||||||
allow = [
|
|
||||||
#"MIT",
|
|
||||||
#"Apache-2.0",
|
|
||||||
]
|
|
||||||
# List of explicitly disallowed licenses
|
|
||||||
# See https://spdx.org/licenses/ for list of possible licenses
|
|
||||||
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
|
|
||||||
deny = [
|
|
||||||
#"Nokia",
|
|
||||||
]
|
|
||||||
# Lint level for licenses considered copyleft
|
|
||||||
copyleft = "warn"
|
|
||||||
# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses
|
|
||||||
# * both - The license will be approved if it is both OSI-approved *AND* FSF
|
|
||||||
# * either - The license will be approved if it is either OSI-approved *OR* FSF
|
|
||||||
# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF
|
|
||||||
# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved
|
|
||||||
# * neither - This predicate is ignored and the default lint level is used
|
|
||||||
allow-osi-fsf-free = "neither"
|
|
||||||
# Lint level used when no other predicates are matched
|
|
||||||
# 1. License isn't in the allow or deny lists
|
|
||||||
# 2. License isn't copyleft
|
|
||||||
# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither"
|
|
||||||
default = "deny"
|
|
||||||
# The confidence threshold for detecting a license from license text.
|
|
||||||
# The higher the value, the more closely the license text must be to the
|
|
||||||
# canonical license text of a valid SPDX license file.
|
|
||||||
# [possible values: any between 0.0 and 1.0].
|
|
||||||
confidence-threshold = 0.8
|
|
||||||
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
|
|
||||||
# aren't accepted for every possible crate as with the normal allow list
|
|
||||||
exceptions = [
|
|
||||||
# Each entry is the crate and version constraint, and its specific allow
|
|
||||||
# list
|
|
||||||
#{ allow = ["Zlib"], name = "adler32", version = "*" },
|
|
||||||
]
|
|
||||||
|
|
||||||
# Some crates don't have (easily) machine readable licensing information,
|
|
||||||
# adding a clarification entry for it allows you to manually specify the
|
|
||||||
# licensing information
|
|
||||||
#[[licenses.clarify]]
|
|
||||||
# The name of the crate the clarification applies to
|
|
||||||
#name = "ring"
|
|
||||||
# The optional version constraint for the crate
|
|
||||||
#version = "*"
|
|
||||||
# The SPDX expression for the license requirements of the crate
|
|
||||||
#expression = "MIT AND ISC AND OpenSSL"
|
|
||||||
# One or more files in the crate's source used as the "source of truth" for
|
|
||||||
# the license expression. If the contents match, the clarification will be used
|
|
||||||
# when running the license check, otherwise the clarification will be ignored
|
|
||||||
# and the crate will be checked normally, which may produce warnings or errors
|
|
||||||
# depending on the rest of your configuration
|
|
||||||
#license-files = [
|
|
||||||
# Each entry is a crate relative path, and the (opaque) hash of its contents
|
|
||||||
#{ path = "LICENSE", hash = 0xbd0eed23 }
|
|
||||||
#]
|
|
||||||
|
|
||||||
[licenses.private]
|
|
||||||
# If true, ignores workspace crates that aren't published, or are only
|
|
||||||
# published to private registries.
|
|
||||||
# To see how to mark a crate as unpublished (to the official registry),
|
|
||||||
# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
|
|
||||||
ignore = false
|
|
||||||
# One or more private registries that you might publish crates to, if a crate
|
|
||||||
# is only published to private registries, and ignore is true, the crate will
|
|
||||||
# not have its license(s) checked
|
|
||||||
registries = [
|
|
||||||
#"https://sekretz.com/registry
|
|
||||||
]
|
|
||||||
|
|
||||||
# This section is considered when running `cargo deny check bans`.
|
|
||||||
# More documentation about the 'bans' section can be found here:
|
|
||||||
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
|
|
||||||
[bans]
|
|
||||||
# Lint level for when multiple versions of the same crate are detected
|
|
||||||
multiple-versions = "warn"
|
|
||||||
# Lint level for when a crate version requirement is `*`
|
|
||||||
wildcards = "allow"
|
|
||||||
# The graph highlighting used when creating dotgraphs for crates
|
|
||||||
# with multiple versions
|
|
||||||
# * lowest-version - The path to the lowest versioned duplicate is highlighted
|
|
||||||
# * simplest-path - The path to the version with the fewest edges is highlighted
|
|
||||||
# * all - Both lowest-version and simplest-path are used
|
|
||||||
highlight = "all"
|
|
||||||
# The default lint level for `default` features for crates that are members of
|
|
||||||
# the workspace that is being checked. This can be overriden by allowing/denying
|
|
||||||
# `default` on a crate-by-crate basis if desired.
|
|
||||||
workspace-default-features = "allow"
|
|
||||||
# The default lint level for `default` features for external crates that are not
|
|
||||||
# members of the workspace. This can be overriden by allowing/denying `default`
|
|
||||||
# on a crate-by-crate basis if desired.
|
|
||||||
external-default-features = "allow"
|
|
||||||
# List of crates that are allowed. Use with care!
|
|
||||||
allow = [
|
|
||||||
#{ name = "ansi_term", version = "=0.11.0" },
|
|
||||||
]
|
|
||||||
# List of crates to deny
|
|
||||||
deny = [
|
|
||||||
# Each entry the name of a crate and a version range. If version is
|
|
||||||
# not specified, all versions will be matched.
|
|
||||||
#{ name = "ansi_term", version = "=0.11.0" },
|
|
||||||
#
|
|
||||||
# Wrapper crates can optionally be specified to allow the crate when it
|
|
||||||
# is a direct dependency of the otherwise banned crate
|
|
||||||
#{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
|
|
||||||
]
|
|
||||||
|
|
||||||
# List of features to allow/deny
|
|
||||||
# Each entry the name of a crate and a version range. If version is
|
|
||||||
# not specified, all versions will be matched.
|
|
||||||
#[[bans.features]]
|
|
||||||
#name = "reqwest"
|
|
||||||
# Features to not allow
|
|
||||||
#deny = ["json"]
|
|
||||||
# Features to allow
|
|
||||||
#allow = [
|
|
||||||
# "rustls",
|
|
||||||
# "__rustls",
|
|
||||||
# "__tls",
|
|
||||||
# "hyper-rustls",
|
|
||||||
# "rustls",
|
|
||||||
# "rustls-pemfile",
|
|
||||||
# "rustls-tls-webpki-roots",
|
|
||||||
# "tokio-rustls",
|
|
||||||
# "webpki-roots",
|
|
||||||
#]
|
|
||||||
# If true, the allowed features must exactly match the enabled feature set. If
|
|
||||||
# this is set there is no point setting `deny`
|
|
||||||
#exact = true
|
|
||||||
|
|
||||||
# Certain crates/versions that will be skipped when doing duplicate detection.
|
|
||||||
skip = [
|
|
||||||
#{ name = "ansi_term", version = "=0.11.0" },
|
|
||||||
]
|
|
||||||
# Similarly to `skip` allows you to skip certain crates during duplicate
|
|
||||||
# detection. Unlike skip, it also includes the entire tree of transitive
|
|
||||||
# dependencies starting at the specified crate, up to a certain depth, which is
|
|
||||||
# by default infinite.
|
|
||||||
skip-tree = [
|
|
||||||
#{ name = "ansi_term", version = "=0.11.0", depth = 20 },
|
|
||||||
]
|
|
||||||
|
|
||||||
# This section is considered when running `cargo deny check sources`.
|
|
||||||
# More documentation about the 'sources' section can be found here:
|
|
||||||
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
|
|
||||||
[sources]
|
|
||||||
# Lint level for what to happen when a crate from a crate registry that is not
|
|
||||||
# in the allow list is encountered
|
|
||||||
unknown-registry = "warn"
|
|
||||||
# Lint level for what to happen when a crate from a git repository that is not
|
|
||||||
# in the allow list is encountered
|
|
||||||
unknown-git = "warn"
|
|
||||||
# List of URLs for allowed crate registries. Defaults to the crates.io index
|
|
||||||
# if not specified. If it is specified but empty, no registries are allowed.
|
|
||||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
|
||||||
# List of URLs for allowed Git repositories
|
|
||||||
allow-git = []
|
|
||||||
|
|
||||||
[sources.allow-org]
|
|
||||||
# 1 or more github.com organizations to allow git sources for
|
|
||||||
github = [""]
|
|
||||||
# 1 or more gitlab.com organizations to allow git sources for
|
|
||||||
gitlab = [""]
|
|
||||||
# 1 or more bitbucket.org organizations to allow git sources for
|
|
||||||
bitbucket = [""]
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "explorer-api"
|
name = "explorer-api"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ pub(crate) async fn retrieve_mixnode_econ_stats(
|
|||||||
|
|
||||||
Some(EconomicDynamicsStats {
|
Some(EconomicDynamicsStats {
|
||||||
stake_saturation: best_effort_small_dec_to_f64(stake_saturation.saturation) as f32,
|
stake_saturation: best_effort_small_dec_to_f64(stake_saturation.saturation) as f32,
|
||||||
|
uncapped_saturation: best_effort_small_dec_to_f64(stake_saturation.uncapped_saturation)
|
||||||
|
as f32,
|
||||||
active_set_inclusion_probability: inclusion_probability.in_active,
|
active_set_inclusion_probability: inclusion_probability.in_active,
|
||||||
reserve_set_inclusion_probability: inclusion_probability.in_reserve,
|
reserve_set_inclusion_probability: inclusion_probability.in_reserve,
|
||||||
// drop precision for compatibility sake
|
// drop precision for compatibility sake
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ pub(crate) struct PrettyDetailedMixNodeBond {
|
|||||||
pub layer: Layer,
|
pub layer: Layer,
|
||||||
pub mix_node: MixNode,
|
pub mix_node: MixNode,
|
||||||
pub stake_saturation: f32,
|
pub stake_saturation: f32,
|
||||||
|
pub uncapped_saturation: f32,
|
||||||
pub avg_uptime: u8,
|
pub avg_uptime: u8,
|
||||||
pub estimated_operator_apy: f64,
|
pub estimated_operator_apy: f64,
|
||||||
pub estimated_delegators_apy: f64,
|
pub estimated_delegators_apy: f64,
|
||||||
@@ -153,6 +154,7 @@ pub(crate) struct NodeStats {
|
|||||||
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
pub(crate) struct EconomicDynamicsStats {
|
pub(crate) struct EconomicDynamicsStats {
|
||||||
pub(crate) stake_saturation: f32,
|
pub(crate) stake_saturation: f32,
|
||||||
|
pub(crate) uncapped_saturation: f32,
|
||||||
|
|
||||||
pub(crate) active_set_inclusion_probability: SelectionChance,
|
pub(crate) active_set_inclusion_probability: SelectionChance,
|
||||||
pub(crate) reserve_set_inclusion_probability: SelectionChance,
|
pub(crate) reserve_set_inclusion_probability: SelectionChance,
|
||||||
|
|||||||
@@ -151,6 +151,8 @@ impl ThreadsafeMixNodesCache {
|
|||||||
mix_node: node.mixnode_details.bond_information.mix_node.clone(),
|
mix_node: node.mixnode_details.bond_information.mix_node.clone(),
|
||||||
avg_uptime: node.performance.round_to_integer(),
|
avg_uptime: node.performance.round_to_integer(),
|
||||||
stake_saturation: best_effort_small_dec_to_f64(node.stake_saturation) as f32,
|
stake_saturation: best_effort_small_dec_to_f64(node.stake_saturation) as f32,
|
||||||
|
uncapped_saturation: best_effort_small_dec_to_f64(node.uncapped_stake_saturation)
|
||||||
|
as f32,
|
||||||
estimated_operator_apy: best_effort_small_dec_to_f64(node.estimated_operator_apy),
|
estimated_operator_apy: best_effort_small_dec_to_f64(node.estimated_operator_apy),
|
||||||
estimated_delegators_apy: best_effort_small_dec_to_f64(node.estimated_delegators_apy),
|
estimated_delegators_apy: best_effort_small_dec_to_f64(node.estimated_delegators_apy),
|
||||||
operating_cost: rewarding_info.cost_params.interval_operating_cost.clone(),
|
operating_cost: rewarding_info.cost_params.interval_operating_cost.clone(),
|
||||||
|
|||||||
@@ -6,14 +6,16 @@ export const EconomicsInfoColumns: ColumnsType[] = [
|
|||||||
title: 'Estimated Total Reward',
|
title: 'Estimated Total Reward',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
tooltipInfo: 'Estimated reward per epoch for this profit margin if your node is selected in the active set.',
|
tooltipInfo:
|
||||||
|
'Estimated node reward (total for the operator and delegators) in the current epoch. There are roughly 24 epochs in a day.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'estimatedOperatorReward',
|
field: 'estimatedOperatorReward',
|
||||||
title: 'Estimated Operator Reward',
|
title: 'Estimated Operator Reward',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
tooltipInfo: 'Estimated reward per epoch for this profit margin if your node is selected in the active set.',
|
tooltipInfo:
|
||||||
|
"Estimated operator's reward (including PM and Operating Cost) in the current epoch. There are roughly 24 epochs in a day.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'selectionChance',
|
field: 'selectionChance',
|
||||||
@@ -29,7 +31,7 @@ export const EconomicsInfoColumns: ColumnsType[] = [
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
tooltipInfo:
|
tooltipInfo:
|
||||||
'Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 1 million NYM, computed as S/K where S is total amount of tokens available to stakeholders and K is the number of nodes in the reward set.',
|
'Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 750k NYM, computed as S/K where S is total amount of tokens available to stakeholders and K is the number of nodes in the reward set.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'profitMargin',
|
field: 'profitMargin',
|
||||||
@@ -37,7 +39,7 @@ export const EconomicsInfoColumns: ColumnsType[] = [
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
tooltipInfo:
|
tooltipInfo:
|
||||||
'Percentage of the delegates rewards that the operator takes as fee before rewards are distributed to the delegates.',
|
'Percentage of the delegators rewards that the operator takes as fee before rewards are distributed to the delegators.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operatingCost',
|
field: 'operatingCost',
|
||||||
|
|||||||
@@ -4,23 +4,8 @@ import { ApiState, MixNodeEconomicDynamicsStatsResponse } from '../../../typeDef
|
|||||||
import { EconomicsInfoRowWithIndex } from './types';
|
import { EconomicsInfoRowWithIndex } from './types';
|
||||||
import { toPercentIntegerString } from '../../../utils';
|
import { toPercentIntegerString } from '../../../utils';
|
||||||
|
|
||||||
const selectionChance = (economicDynamicsStats: ApiState<MixNodeEconomicDynamicsStatsResponse> | undefined) => {
|
const selectionChance = (economicDynamicsStats: ApiState<MixNodeEconomicDynamicsStatsResponse> | undefined) =>
|
||||||
const inclusionProbability = economicDynamicsStats?.data?.active_set_inclusion_probability;
|
economicDynamicsStats?.data?.active_set_inclusion_probability || '-';
|
||||||
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
|
|
||||||
switch (inclusionProbability) {
|
|
||||||
case 'VeryLow':
|
|
||||||
return 'Very Low';
|
|
||||||
case 'VeryHigh':
|
|
||||||
return 'Very High';
|
|
||||||
case 'High':
|
|
||||||
case 'Good':
|
|
||||||
case 'Low':
|
|
||||||
case 'Moderate':
|
|
||||||
return inclusionProbability;
|
|
||||||
default:
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EconomicsInfoRows = (): EconomicsInfoRowWithIndex => {
|
export const EconomicsInfoRows = (): EconomicsInfoRowWithIndex => {
|
||||||
const { economicDynamicsStats, mixNode } = useMixnodeContext();
|
const { economicDynamicsStats, mixNode } = useMixnodeContext();
|
||||||
@@ -29,7 +14,7 @@ export const EconomicsInfoRows = (): EconomicsInfoRowWithIndex => {
|
|||||||
currencyToString((economicDynamicsStats?.data?.estimated_total_node_reward || '').toString()) || '-';
|
currencyToString((economicDynamicsStats?.data?.estimated_total_node_reward || '').toString()) || '-';
|
||||||
const estimatedOperatorRewards =
|
const estimatedOperatorRewards =
|
||||||
currencyToString((economicDynamicsStats?.data?.estimated_operator_reward || '').toString()) || '-';
|
currencyToString((economicDynamicsStats?.data?.estimated_operator_reward || '').toString()) || '-';
|
||||||
const stakeSaturation = economicDynamicsStats?.data?.stake_saturation || '-';
|
const stakeSaturation = economicDynamicsStats?.data?.uncapped_saturation || '-';
|
||||||
const profitMargin = mixNode?.data?.profit_margin_percent
|
const profitMargin = mixNode?.data?.profit_margin_percent
|
||||||
? toPercentIntegerString(mixNode?.data?.profit_margin_percent)
|
? toPercentIntegerString(mixNode?.data?.profit_margin_percent)
|
||||||
: '-';
|
: '-';
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
|||||||
const totalBond = pledge + delegations;
|
const totalBond = pledge + delegations;
|
||||||
const selfPercentage = ((pledge * 100) / totalBond).toFixed(2);
|
const selfPercentage = ((pledge * 100) / totalBond).toFixed(2);
|
||||||
const profitPercentage = toPercentIntegerString(item.profit_margin_percent) || 0;
|
const profitPercentage = toPercentIntegerString(item.profit_margin_percent) || 0;
|
||||||
const stakeSaturation = typeof item.stake_saturation === 'number' ? item.stake_saturation * 100 : 0;
|
const uncappedSaturation = typeof item.uncapped_saturation === 'number' ? item.uncapped_saturation * 100 : 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mix_id: item.mix_id,
|
mix_id: item.mix_id,
|
||||||
@@ -47,7 +47,7 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
|||||||
layer: item?.layer || '',
|
layer: item?.layer || '',
|
||||||
profit_percentage: `${profitPercentage}%`,
|
profit_percentage: `${profitPercentage}%`,
|
||||||
avg_uptime: `${item.avg_uptime}%` || '-',
|
avg_uptime: `${item.avg_uptime}%` || '-',
|
||||||
stake_saturation: stakeSaturation,
|
stake_saturation: uncappedSaturation,
|
||||||
operating_cost: `${unymToNym(item.operating_cost.amount, 6)} NYM`,
|
operating_cost: `${unymToNym(item.operating_cost?.amount, 6)} NYM`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Link as RRDLink } from 'react-router-dom';
|
import { Link as RRDLink } from 'react-router-dom';
|
||||||
import { Box, Button, Card, Grid, Typography, Link as MuiLink } from '@mui/material';
|
import { Box, Button, Card, Grid, Link as MuiLink } from '@mui/material';
|
||||||
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
|
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
|
||||||
import { SelectChangeEvent } from '@mui/material/Select';
|
import { SelectChangeEvent } from '@mui/material/Select';
|
||||||
import { useMainContext } from '../../context/main';
|
import { useMainContext } from '../../context/main';
|
||||||
@@ -12,6 +12,8 @@ import { Title } from '../../components/Title';
|
|||||||
import { cellStyles, UniversalDataGrid } from '../../components/Universal-DataGrid';
|
import { cellStyles, UniversalDataGrid } from '../../components/Universal-DataGrid';
|
||||||
import { currencyToString } from '../../utils/currency';
|
import { currencyToString } from '../../utils/currency';
|
||||||
import { Tooltip } from '../../components/Tooltip';
|
import { Tooltip } from '../../components/Tooltip';
|
||||||
|
import { BIG_DIPPER } from '../../api/constants';
|
||||||
|
import { splice } from '../../utils';
|
||||||
|
|
||||||
export const PageGateways: React.FC = () => {
|
export const PageGateways: React.FC = () => {
|
||||||
const { gateways } = useMainContext();
|
const { gateways } = useMainContext();
|
||||||
@@ -69,9 +71,14 @@ export const PageGateways: React.FC = () => {
|
|||||||
headerClassName: 'MuiDataGrid-header-override',
|
headerClassName: 'MuiDataGrid-header-override',
|
||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
renderCell: (params: GridRenderCellParams) => (
|
renderCell: (params: GridRenderCellParams) => (
|
||||||
<Typography sx={cellStyles} data-testid="pledge-amount">
|
<MuiLink
|
||||||
|
sx={{ ...cellStyles }}
|
||||||
|
component={RRDLink}
|
||||||
|
to={`/network-components/gateway/${params.row.identityKey}`}
|
||||||
|
data-testid="pledge-amount"
|
||||||
|
>
|
||||||
{currencyToString(params.value)}
|
{currencyToString(params.value)}
|
||||||
</Typography>
|
</MuiLink>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -81,9 +88,14 @@ export const PageGateways: React.FC = () => {
|
|||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
headerClassName: 'MuiDataGrid-header-override',
|
headerClassName: 'MuiDataGrid-header-override',
|
||||||
renderCell: (params: GridRenderCellParams) => (
|
renderCell: (params: GridRenderCellParams) => (
|
||||||
<Typography sx={cellStyles} data-testid="host">
|
<MuiLink
|
||||||
|
sx={{ ...cellStyles }}
|
||||||
|
component={RRDLink}
|
||||||
|
to={`/network-components/gateway/${params.row.identityKey}`}
|
||||||
|
data-testid="host"
|
||||||
|
>
|
||||||
{params.value}
|
{params.value}
|
||||||
</Typography>
|
</MuiLink>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -120,9 +132,14 @@ export const PageGateways: React.FC = () => {
|
|||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
headerClassName: 'MuiDataGrid-header-override',
|
headerClassName: 'MuiDataGrid-header-override',
|
||||||
renderCell: (params: GridRenderCellParams) => (
|
renderCell: (params: GridRenderCellParams) => (
|
||||||
<Typography sx={cellStyles} data-testid="owner">
|
<MuiLink
|
||||||
{params.value}
|
sx={{ ...cellStyles }}
|
||||||
</Typography>
|
href={`${BIG_DIPPER}/account/${params.value}`}
|
||||||
|
target="_blank"
|
||||||
|
data-testid="owner"
|
||||||
|
>
|
||||||
|
{splice(7, 29, params.value)}
|
||||||
|
</MuiLink>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const columns: ColumnsType[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'self_percentage',
|
field: 'self_percentage',
|
||||||
title: 'Self %',
|
title: 'Bond %',
|
||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
width: 99,
|
width: 99,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export const PageMixnodes: React.FC = () => {
|
|||||||
renderHeader: () => (
|
renderHeader: () => (
|
||||||
<CustomColumnHeading
|
<CustomColumnHeading
|
||||||
headingTitle="Stake Saturation"
|
headingTitle="Stake Saturation"
|
||||||
tooltipInfo="Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 1 million NYM, computed as S/K where S is total amount of tokens available to stakeholders and K is the number of nodes in the reward set."
|
tooltipInfo="Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 750k NYM, computed as S/K where S is total amount of tokens available to stakeholders and K is the number of nodes in the reward set."
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
headerClassName: 'MuiDataGrid-header-override',
|
headerClassName: 'MuiDataGrid-header-override',
|
||||||
@@ -192,7 +192,7 @@ export const PageMixnodes: React.FC = () => {
|
|||||||
renderHeader: () => (
|
renderHeader: () => (
|
||||||
<CustomColumnHeading
|
<CustomColumnHeading
|
||||||
headingTitle="Profit Margin"
|
headingTitle="Profit Margin"
|
||||||
tooltipInfo="Percentage of the delegates rewards that the operator takes as fee before rewards are distributed to the delegates."
|
tooltipInfo="Percentage of the delegators rewards that the operator takes as fee before rewards are distributed to the delegators."
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
headerClassName: 'MuiDataGrid-header-override',
|
headerClassName: 'MuiDataGrid-header-override',
|
||||||
@@ -210,10 +210,10 @@ export const PageMixnodes: React.FC = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operating_cost',
|
field: 'operating_cost',
|
||||||
headerName: 'Operating cost',
|
headerName: 'Operating Cost',
|
||||||
renderHeader: () => (
|
renderHeader: () => (
|
||||||
<CustomColumnHeading
|
<CustomColumnHeading
|
||||||
headingTitle="Operating cost"
|
headingTitle="Operating Cost"
|
||||||
tooltipInfo="Monthly operational cost of running this node. This cost is set by the operator and it influences how the rewards are split between the operator and delegators."
|
tooltipInfo="Monthly operational cost of running this node. This cost is set by the operator and it influences how the rewards are split between the operator and delegators."
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export interface MixNodeResponseItem {
|
|||||||
mix_node: MixNode;
|
mix_node: MixNode;
|
||||||
avg_uptime: number;
|
avg_uptime: number;
|
||||||
stake_saturation: number;
|
stake_saturation: number;
|
||||||
|
uncapped_saturation: number;
|
||||||
operating_cost: Amount;
|
operating_cost: Amount;
|
||||||
profit_margin_percent: string;
|
profit_margin_percent: string;
|
||||||
}
|
}
|
||||||
@@ -214,8 +215,9 @@ export type UptimeStoryResponse = {
|
|||||||
|
|
||||||
export type MixNodeEconomicDynamicsStatsResponse = {
|
export type MixNodeEconomicDynamicsStatsResponse = {
|
||||||
stake_saturation: number;
|
stake_saturation: number;
|
||||||
|
uncapped_saturation: number;
|
||||||
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
|
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
|
||||||
active_set_inclusion_probability: 'High' | 'Good' | 'Low' | 'VeryLow' | 'Moderate' | 'VeryHigh';
|
active_set_inclusion_probability: 'High' | 'Good' | 'Low';
|
||||||
reserve_set_inclusion_probability: 'High' | 'Good' | 'Low';
|
reserve_set_inclusion_probability: 'High' | 'Good' | 'Low';
|
||||||
estimated_total_node_reward: number;
|
estimated_total_node_reward: number;
|
||||||
estimated_operator_reward: number;
|
estimated_operator_reward: number;
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "nym-gateway"
|
name = "nym-gateway"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "nym-mixnode"
|
name = "nym-mixnode"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
## [nym-connect-v1.1.0](https://github.com/nymtech/nym/tree/nym-connect-v1.1.0) (2022-11-09)
|
||||||
|
|
||||||
|
- nym-connect: rework of rewarding changes the directory data structures that describe the mixnet topology ([#1472])
|
||||||
|
- clients: add testing-only support for two more extended packet sizes (8kb and 16kb).
|
||||||
|
- native-client/socks5-client: `disable_loop_cover_traffic_stream` Debug config option to disable the separate loop cover traffic stream ([#1666])
|
||||||
|
- native-client/socks5-client: `disable_main_poisson_packet_distribution` Debug config option to make the client ignore poisson distribution in the main packet stream and ONLY send real message (and as fast as they come) ([#1664])
|
||||||
|
- native-client/socks5-client: `use_extended_packet_size` Debug config option to make the client use 'ExtendedPacketSize' for its traffic (32kB as opposed to 2kB in 1.0.2) ([#1671])
|
||||||
|
- network-requester: added additional Blockstream Green wallet endpoint to `example.allowed.list` ([#1611])
|
||||||
|
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#1558])
|
||||||
|
|
||||||
|
[#1472]: https://github.com/nymtech/nym/pull/1472
|
||||||
|
[#1558]: https://github.com/nymtech/nym/pull/1558
|
||||||
|
[#1611]: https://github.com/nymtech/nym/pull/1611
|
||||||
|
[#1664]: https://github.com/nymtech/nym/pull/1664
|
||||||
|
[#1666]: https://github.com/nymtech/nym/pull/1666
|
||||||
|
[#1671]: https://github.com/nymtech/nym/pull/1671
|
||||||
|
|
||||||
## [nym-connect-v1.0.2](https://github.com/nymtech/nym/tree/nym-connect-v1.0.2) (2022-08-18)
|
## [nym-connect-v1.0.2](https://github.com/nymtech/nym/tree/nym-connect-v1.0.2) (2022-08-18)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
Generated
+2
-4
@@ -3214,7 +3214,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-connect"
|
name = "nym-connect"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bip39",
|
"bip39",
|
||||||
"client-core",
|
"client-core",
|
||||||
@@ -3247,7 +3247,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"client-core",
|
"client-core",
|
||||||
@@ -3273,7 +3273,6 @@ dependencies = [
|
|||||||
"snafu",
|
"snafu",
|
||||||
"socks5-requests",
|
"socks5-requests",
|
||||||
"task",
|
"task",
|
||||||
"thiserror",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"topology",
|
"topology",
|
||||||
"url",
|
"url",
|
||||||
@@ -6189,7 +6188,6 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"contracts-common",
|
"contracts-common",
|
||||||
"cosmwasm-std",
|
"cosmwasm-std",
|
||||||
"log",
|
|
||||||
"mixnet-contract-common",
|
"mixnet-contract-common",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-connect"
|
name = "nym-connect"
|
||||||
version = "1.0.2"
|
version = "1.1.0"
|
||||||
description = "nym-connect"
|
description = "nym-connect"
|
||||||
authors = ["Nym Technologies SA"]
|
authors = ["Nym Technologies SA"]
|
||||||
license = ""
|
license = ""
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ pub type StatusReceiver = futures::channel::oneshot::Receiver<Socks5StatusMessag
|
|||||||
pub enum Socks5StatusMessage {
|
pub enum Socks5StatusMessage {
|
||||||
/// The SOCKS5 task successfully stopped
|
/// The SOCKS5 task successfully stopped
|
||||||
Stopped,
|
Stopped,
|
||||||
/// The SOCKS5 task failed to start
|
|
||||||
FailedToStart,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main SOCKS5 client task. It loads the configuration from file determined by the `id`.
|
/// The main SOCKS5 client task. It loads the configuration from file determined by the `id`.
|
||||||
@@ -45,18 +43,10 @@ pub fn start_nym_socks5_client(
|
|||||||
// The status channel is used to both get the state of the task, and if it's closed, to check
|
// The status channel is used to both get the state of the task, and if it's closed, to check
|
||||||
// for panic.
|
// for panic.
|
||||||
std::thread::spawn(|| {
|
std::thread::spawn(|| {
|
||||||
let result = tokio::runtime::Runtime::new()
|
tokio::runtime::Runtime::new()
|
||||||
.expect("Failed to create runtime for SOCKS5 client")
|
.expect("Failed to create runtime for SOCKS5 client")
|
||||||
.block_on(async move { socks5_client.run_and_listen(socks5_ctrl_rx).await });
|
.block_on(async move { socks5_client.run_and_listen(socks5_ctrl_rx).await });
|
||||||
|
|
||||||
if let Err(err) = result {
|
|
||||||
log::error!("SOCKS5 proxy failed to start: {err}");
|
|
||||||
socks5_status_tx
|
|
||||||
.send(Socks5StatusMessage::FailedToStart)
|
|
||||||
.expect("Failed to send status message back to main task");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info!("SOCKS5 task finished");
|
log::info!("SOCKS5 task finished");
|
||||||
socks5_status_tx
|
socks5_status_tx
|
||||||
.send(Socks5StatusMessage::Stopped)
|
.send(Socks5StatusMessage::Stopped)
|
||||||
@@ -79,9 +69,6 @@ pub fn start_disconnect_listener(
|
|||||||
Ok(Socks5StatusMessage::Stopped) => {
|
Ok(Socks5StatusMessage::Stopped) => {
|
||||||
log::info!("SOCKS5 task reported it has finished");
|
log::info!("SOCKS5 task reported it has finished");
|
||||||
}
|
}
|
||||||
Ok(Socks5StatusMessage::FailedToStart) => {
|
|
||||||
log::info!("SOCKS5 task reported it failed to start");
|
|
||||||
}
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
log::info!("SOCKS5 task appears to have stopped abruptly");
|
log::info!("SOCKS5 task appears to have stopped abruptly");
|
||||||
// TODO: we should probably generate some events here, or otherwise signal to the
|
// TODO: we should probably generate some events here, or otherwise signal to the
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "nym-connect",
|
"productName": "nym-connect",
|
||||||
"version": "1.0.2"
|
"version": "1.1.0"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"distDir": "../dist",
|
"distDir": "../dist",
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
+15
-3
@@ -1,8 +1,21 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Unreleased
|
## [nym-wallet-v1.1.0](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.0) (2022-11-09)
|
||||||
|
|
||||||
- Add window for showing logs for when the terminal is not available.
|
- wallet: Add window for showing logs for when the terminal is not available.
|
||||||
|
- wallet: Rework of rewarding ([#1472])
|
||||||
|
- wallet: Highlight delegations on an unbonded node to notify the delegator to undelegate stake
|
||||||
|
- wallet: Group delegations on unbonded nodes at the top of the list
|
||||||
|
- wallet: Simplified delegation screen when user has no delegations
|
||||||
|
- wallet: Update "create password" howto guide
|
||||||
|
- wallet: Allow setting of operatoring cost when node is bonded
|
||||||
|
- wallet: Allow update of operatoring cost on bonded node
|
||||||
|
- wallet: New bond settings area
|
||||||
|
- wallet: Display pending unbonds
|
||||||
|
- wallet: Display routing score and average score for gateways
|
||||||
|
- wallet: Improve dialogs for account creation
|
||||||
|
|
||||||
|
[#1472]: https://github.com/nymtech/nym/pull/1472
|
||||||
|
|
||||||
## [nym-wallet-v1.0.9](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.0.8) (2022-09-08)
|
## [nym-wallet-v1.0.9](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.0.8) (2022-09-08)
|
||||||
|
|
||||||
@@ -14,7 +27,6 @@
|
|||||||
- wallet: compound and redeem functionalities for operator rewards
|
- wallet: compound and redeem functionalities for operator rewards
|
||||||
- wallet: a few minor touch ups and bug fixes
|
- wallet: a few minor touch ups and bug fixes
|
||||||
|
|
||||||
|
|
||||||
## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11)
|
## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11)
|
||||||
|
|
||||||
- wallet: dark mode
|
- wallet: dark mode
|
||||||
|
|||||||
Generated
+1
-2
@@ -2877,7 +2877,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym_wallet"
|
name = "nym_wallet"
|
||||||
version = "1.0.9"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"argon2 0.3.4",
|
"argon2 0.3.4",
|
||||||
@@ -5423,7 +5423,6 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"contracts-common",
|
"contracts-common",
|
||||||
"cosmwasm-std",
|
"cosmwasm-std",
|
||||||
"log",
|
|
||||||
"mixnet-contract-common",
|
"mixnet-contract-common",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -160,9 +160,9 @@ mod qa {
|
|||||||
pub(crate) const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
|
pub(crate) const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
|
||||||
|
|
||||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
|
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
|
||||||
"n1qa4hswlcjmttulj0q9qa46jf64f93pecl6tydcsjldfe0hy5ju0sdmwzya";
|
|
||||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
|
||||||
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
|
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
|
||||||
|
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
||||||
|
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
|
||||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||||
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||||
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
|
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
|
||||||
@@ -177,8 +177,8 @@ mod qa {
|
|||||||
//pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "http://0.0.0.0";
|
//pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "http://0.0.0.0";
|
||||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||||
vec![ValidatorDetails::new(
|
vec![ValidatorDetails::new(
|
||||||
"https://v2-env-validator.qa.nymte.ch/",
|
"https://qwerty-validator.qa.nymte.ch/",
|
||||||
Some("https://v2-env-val-api.qa.nymte.ch/api/"),
|
Some("https://qwerty-validator-api.qa.nymte.ch/api"),
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym_wallet"
|
name = "nym_wallet"
|
||||||
version = "1.0.9"
|
version = "1.1.0"
|
||||||
description = "Nym Native Wallet"
|
description = "Nym Native Wallet"
|
||||||
authors = ["Nym Technologies SA"]
|
authors = ["Nym Technologies SA"]
|
||||||
license = ""
|
license = ""
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "nym-wallet",
|
"productName": "nym-wallet",
|
||||||
"version": "1.0.9"
|
"version": "1.1.0"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"distDir": "../dist",
|
"distDir": "../dist",
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const headers: Header[] = [
|
|||||||
header: 'Operator rewards',
|
header: 'Operator rewards',
|
||||||
id: 'operator-rewards',
|
id: 'operator-rewards',
|
||||||
tooltipText:
|
tooltipText:
|
||||||
'This is your (operator) new rewards including the PM and cost. You can compound your rewards manually every epoch or unbond your node to redeem them.',
|
'This is your (operator) rewards including the PM and cost. Rewards are automatically compounded every epoch. You can redeem your rewards at any time.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'No. delegators',
|
header: 'No. delegators',
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ const AmountFormData = ({
|
|||||||
<CurrencyFormField
|
<CurrencyFormField
|
||||||
required
|
required
|
||||||
fullWidth
|
fullWidth
|
||||||
label="Operator cost"
|
label="Operator Cost"
|
||||||
autoFocus
|
autoFocus
|
||||||
onChanged={(newValue) => setValue('operatorCost', newValue, { shouldValidate: true })}
|
onChanged={(newValue) => setValue('operatorCost', newValue, { shouldValidate: true })}
|
||||||
validationError={errors.operatorCost?.amount?.message}
|
validationError={errors.operatorCost?.amount?.message}
|
||||||
|
|||||||
@@ -130,16 +130,10 @@ const TokenTransfer = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Typography variant="subtitle2" sx={{ color: (t) => t.palette.nym.text.muted, mt: 2 }}>
|
<Typography variant="subtitle2" sx={{ color: (t) => t.palette.nym.text.muted, mt: 2 }}>
|
||||||
Transferable tokens
|
Unlocked transferable tokens
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography
|
<Typography data-testid="refresh-success" sx={{ color: 'text.primary' }} variant="h5" textTransform="uppercase">
|
||||||
data-testid="refresh-success"
|
|
||||||
sx={{ color: 'text.primary' }}
|
|
||||||
variant="h5"
|
|
||||||
fontWeight="700"
|
|
||||||
textTransform="uppercase"
|
|
||||||
>
|
|
||||||
{userBalance.tokenAllocation?.spendable || 'n/a'} {clientDetails?.display_mix_denom.toUpperCase()}
|
{userBalance.tokenAllocation?.spendable || 'n/a'} {clientDetails?.display_mix_denom.toUpperCase()}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -167,7 +161,6 @@ export const VestingCard = ({ onTransfer }: { onTransfer: () => Promise<void> })
|
|||||||
<NymCard
|
<NymCard
|
||||||
title="Vesting Schedule"
|
title="Vesting Schedule"
|
||||||
data-testid="check-unvested-tokens"
|
data-testid="check-unvested-tokens"
|
||||||
Icon={<InfoOutlined />}
|
|
||||||
Action={
|
Action={
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
|
|||||||
@@ -33,10 +33,17 @@ export const NodeUnbondPage = ({ bondedNode, onConfirm, onError }: Props) => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item container direction={'column'} spacing={2} width={0.5} padding={3}>
|
<Grid item container direction={'column'} spacing={2} width={0.5} padding={3}>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
|
<Box sx={{ mb: 1 }}>
|
||||||
|
<Error
|
||||||
|
message={`Remember you should only unbond if you want to remove your ${
|
||||||
|
isMixnode(bondedNode) ? 'node' : 'gateway'
|
||||||
|
} from the network for good.`}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
<Error
|
<Error
|
||||||
message={`Unbonding is irreversible and it won’t be possible to restore the current state of your ${
|
message={`Unbonding is irreversible and it won’t be possible to restore the current state of your ${
|
||||||
isMixnode(bondedNode) ? 'node' : 'gateway'
|
isMixnode(bondedNode) ? 'node' : 'gateway'
|
||||||
} again`}
|
} again.`}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user